├── .build.yml ├── .gitignore ├── Arch ├── PKGBUILD └── ncursesFM.install ├── CMakeLists.txt ├── COPYING ├── README.md ├── Script ├── ncursesFM └── ncursesFM.conf ├── TODO.md ├── cmake └── FindLibmagic.cmake ├── inc ├── archiver.h ├── bookmarks.h ├── config.h ├── declarations.h ├── devices.h ├── fm.h ├── inhibit.h ├── install_package.h ├── log.h ├── notify.h ├── print.h ├── quit.h ├── search.h ├── string_constants.h ├── sysinfo.h ├── ui.h ├── utils.h └── worker_thread.h ├── msg ├── CMakeLists.txt ├── de │ ├── CMakeLists.txt │ └── de.po ├── es_AR │ ├── CMakeLists.txt │ └── es_AR.po ├── fr │ ├── CMakeLists.txt │ └── fr.po ├── it │ ├── CMakeLists.txt │ └── it.po └── ncursesFM.pot ├── ncursesFM.png └── src ├── archiver.c ├── bookmarks.c ├── config.c ├── devices.c ├── fm.c ├── inhibit.c ├── install_package.c ├── log.c ├── main.c ├── notify.c ├── print.c ├── quit.c ├── search.c ├── string_constants.c ├── sysinfo.c ├── ui.c ├── utils.c └── worker_thread.c /.build.yml: -------------------------------------------------------------------------------- 1 | image: archlinux 2 | packages: 3 | - systemd 4 | - libconfig 5 | - libcups 6 | - ncurses 7 | - libarchive 8 | - glibc 9 | - libnotify 10 | - cmake 11 | - bash-completion 12 | sources: 13 | - https://github.com/FedeDP/ncursesFM 14 | tasks: 15 | - prepare: | 16 | cd ncursesFM 17 | mkdir build 18 | cd build 19 | cmake -DENABLE_CUPS=true -DENABLE_NOTIFY=true .. 20 | - build: | 21 | cd ncursesFM/build 22 | make 23 | triggers: 24 | - action: email 25 | condition: failure 26 | to: "" 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | /ncursesFM 3 | ncursesFM.kdev4 4 | src/version.c 5 | build/ 6 | -------------------------------------------------------------------------------- /Arch/PKGBUILD: -------------------------------------------------------------------------------- 1 | # Maintainer: Federico Di Pierro 2 | 3 | pkgname=ncursesfm-git 4 | _gitname=ncursesFM 5 | pkgver=v3.1.r37.gd823532 6 | pkgrel=2 7 | pkgdesc="A FileManager written in c and ncurses library." 8 | arch=('i686' 'x86_64') 9 | url="https://github.com/FedeDP/${_gitname}" 10 | license=('GPL') 11 | depends=('ncurses' 'libconfig' 'libarchive' 'glibc' 'libcups' 'systemd' 'libnotify') 12 | optdepends=('xdg-utils: xdg-open support' 13 | 'udisks2: mountable drives and ISO mount support' 14 | 'packagekit: package installation support' 15 | 'upower: AC (dis)connection events support') 16 | # libcups and libnotify are optional build dep. 17 | # If compiled without them, the program will run just fine; 18 | # but that would disable desktop notifications and printing support. 19 | makedepends=('git' 'bash-completion' 'cmake') 20 | source=("git://github.com/FedeDP/${_gitname}.git") 21 | backup=('etc/default/ncursesFM.conf') 22 | install=ncursesFM.install 23 | sha256sums=("SKIP") 24 | 25 | pkgver() { 26 | cd "$_gitname" 27 | git describe --long --tags | sed 's/\([^-]*-g\)/r\1/;s/-/./g' 28 | } 29 | 30 | prepare() { 31 | cd "${srcdir}/${_gitname}" 32 | mkdir -p build 33 | } 34 | 35 | build() { 36 | cd "${srcdir}/${_gitname}/build" 37 | cmake \ 38 | -G "Unix Makefiles" \ 39 | -DCMAKE_INSTALL_PREFIX=/usr \ 40 | -DCMAKE_INSTALL_LIBDIR=lib \ 41 | -DENABLE_CUPS=true \ 42 | -DENABLE_NOTIFY=true \ 43 | .. 44 | make 45 | } 46 | 47 | package() { 48 | cd "${srcdir}/${_gitname}/build" 49 | make DESTDIR="$pkgdir" install 50 | } 51 | -------------------------------------------------------------------------------- /Arch/ncursesFM.install: -------------------------------------------------------------------------------- 1 | post_install() { 2 | echo '--> Take a look at config file to set your preferred variables:' 3 | echo '--> /etc/default/ncursesFM.conf' 4 | } 5 | 6 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | project(ncursesFM VERSION 3.1 LANGUAGES C) 4 | 5 | set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") 6 | 7 | include(GNUInstallDirs) 8 | find_package(PkgConfig REQUIRED) 9 | find_package(Threads REQUIRED) 10 | find_package(Libmagic REQUIRED) 11 | 12 | # Options 13 | set(NCURSESFM_CONFDIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}/default" 14 | CACHE FILEPATH "Path for config file") 15 | 16 | # Create program target 17 | file(GLOB_RECURSE SOURCES src/*.c) 18 | add_executable(${PROJECT_NAME} ${SOURCES}) 19 | target_include_directories(${PROJECT_NAME} PRIVATE 20 | "${CMAKE_CURRENT_SOURCE_DIR}/src") 21 | 22 | target_compile_definitions(${PROJECT_NAME} PRIVATE 23 | -D_GNU_SOURCE 24 | -DVERSION="${PROJECT_VERSION}" 25 | -DCONFDIR="${NCURSESFM_CONFDIR}" 26 | -DBINDIR="${CMAKE_INSTALL_BINDIR}" 27 | -DLOCALEDIR="${CMAKE_INSTALL_LOCALEDIR}" 28 | ) 29 | set_property(TARGET ${PROJECT_NAME} PROPERTY C_STANDARD 99) 30 | 31 | # Required dependencies 32 | pkg_check_modules(REQ_LIBS REQUIRED libconfig libarchive ncursesw libudev) 33 | pkg_search_module(LOGIN_LIBS REQUIRED libelogind libsystemd>=221) 34 | 35 | if (ENABLE_CUPS) 36 | find_package(Cups REQUIRED) 37 | message(STATUS "Libups support enabled") 38 | target_compile_definitions(${PROJECT_NAME} PRIVATE LIBCUPS_PRESENT) 39 | endif() 40 | 41 | if (ENABLE_NOTIFY) 42 | pkg_check_modules(OTHER_LIBS REQUIRED libnotify) 43 | message(STATUS "Libnotify support enabled") 44 | target_compile_definitions(${PROJECT_NAME} PRIVATE LIBNOTIFY_PRESENT) 45 | endif() 46 | 47 | target_link_libraries(${PROJECT_NAME} 48 | m 49 | ${REQ_LIBS_LIBRARIES} 50 | ${OTHER_LIBS_LIBRARIES} 51 | ${LOGIN_LIBS_LIBRARIES} 52 | ${CMAKE_THREAD_LIBS_INIT} 53 | ${LIBMAGIC_LIBRARIES} 54 | ${CUPS_LIBRARIES} 55 | ) 56 | target_include_directories(${PROJECT_NAME} PRIVATE 57 | ${REQ_LIBS_INCLUDE_DIRS} 58 | ${OTHER_LIBS_INCLUDE_DIRS} 59 | ${LOGIN_LIBS_INCLUDE_DIRS} 60 | ${LIBMAGIC_INCLUDES} 61 | ${CUPS_INCLUDES} 62 | ) 63 | list(APPEND COMBINED_LDFLAGS ${REQ_LIBS_LDFLAGS}) 64 | list(APPEND COMBINED_LDFLAGS ${LOGIN_LIBS_LDFLAGS}) 65 | 66 | # Convert ld flag list from list to space separated string. 67 | string(REPLACE ";" " " COMBINED_LDFLAGS "${COMBINED_LDFLAGS}") 68 | 69 | # Set the LDFLAGS target property 70 | set_target_properties( 71 | ${PROJECT_NAME} PROPERTIES 72 | LINK_FLAGS "${COMBINED_LDFLAGS}" 73 | ) 74 | 75 | add_subdirectory(msg) 76 | 77 | # Installation of targets (must be before file configuration to work) 78 | install(TARGETS ${PROJECT_NAME} 79 | RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") 80 | 81 | # Configure files with install paths 82 | set(EXTRA_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Script") 83 | 84 | # Installation of files 85 | pkg_get_variable(COMPLETIONS_DIR bash-completion completionsdir) 86 | 87 | install(FILES ${EXTRA_DIR}/${PROJECT_NAME}.conf 88 | DESTINATION ${NCURSESFM_CONFDIR}) 89 | 90 | if (COMPLETIONS_DIR) 91 | install(FILES ${EXTRA_DIR}/${PROJECT_NAME} 92 | DESTINATION ${COMPLETIONS_DIR}) 93 | endif() 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![builds.sr.ht status](https://builds.sr.ht/~fededp/ncursesfm/.build.yml.svg)](https://builds.sr.ht/~fededp/ncursesfm/.build.yml?) 2 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/1c183099576b4a80a5bdcaced76571b6)](https://www.codacy.com/app/FedeDP/ncursesFM?utm_source=github.com&utm_medium=referral&utm_content=FedeDP/ncursesFM&utm_campaign=Badge_Grade) 3 | 4 | # NcursesFM 5 | 6 | *Ncurses File Manager for linux* 7 | 8 | Written in C, with ncurses library, it aims to be as user friendly and lightweight as possible, while being good looking and simple. 9 | Being simple doesn't imply being useless; indeed it is a full featured fm. 10 | For a **full list of features**, **deps** and **how to build**, please refer to [wiki pages](https://github.com/FedeDP/ncursesFM/wiki/). 11 | 12 | ![](https://github.com/FedeDP/ncursesFM/raw/master/ncursesFM.png) 13 | 14 | ## Main features: 15 | 16 | **Remember that every shortcut in ncursesFM is case insensitive!** 17 | 18 | *Press 'L' while in program to view a detailed helper message* 19 | 20 | * Every feature you would expect by a basic FM. 21 | * Terminal window resize support. 22 | * i18n support: for now en, it, de, es_AR, fr (list of translators: https://github.com/FedeDP/ncursesFM/wiki/I18n#translators). 23 | * 2 tabs support. Their content is kept in sync. Jump between tabs with arrow keys (left/right). 24 | * config support through libconfig. 25 | * Basic mouse support. 26 | * Simple sysinfo monitor that will refresh every 30s: clock, battery and some system info. 27 | * Fast browse mode: enable it with ','. It lets you jump between files by just typing their names. 28 | * 4 sorting modes. 29 | * Stats support (permissions and sizes). 30 | * Inotify monitor to check for fs events in current opened directories. 31 | * Bookmarks support. 32 | * Search support: it will search your string in current directory tree. It can search your string inside archives too. 33 | * Basic print support through libcups. 34 | * Extract/compress files/folders through libarchive. 35 | * Powermanagement inhibition while processing a job (eg: while pasting a file) to avoid data loss. 36 | * Internal udisks2 monitor, to poll for new devices. It can automount new connected devices too. Device monitor will list only mountable devices, eg: dvd reader will not be listed until a cd/dvd is inserted. 37 | * Drives/usb sticks/ISO files (un)mount through udisks2. 38 | * Distro package files installation through packagekit. 39 | * libnotify support. 40 | -------------------------------------------------------------------------------- /Script/ncursesFM: -------------------------------------------------------------------------------- 1 | # bash completion for ncursesFM -*- shell-script -*- 2 | 3 | _ncursesFM() 4 | { 5 | local cur prev words cword 6 | _init_completion || return 7 | 8 | case $prev in 9 | "--editor"|"--starting_dir") 10 | _filedir 11 | return 0 12 | ;; 13 | "--helper_win"|"--inhibit"|"--automount"|"--persistent_log") 14 | COMPREPLY=( $( compgen -W "0 1" -- ${cur}) ) 15 | return 0 16 | ;; 17 | "--loglevel") 18 | COMPREPLY=( $( compgen -W "0 1 2 3" -- ${cur}) ) 19 | return 0 20 | ;; 21 | "--safe") 22 | COMPREPLY=( $( compgen -W "0 1 2" -- ${cur}) ) 23 | return 0 24 | ;; 25 | esac 26 | opts="--editor --helper_win --inhibit --automount --loglevel --persistent_log --starting_dir --help --low_level --safe" 27 | if [[ "$cur" == -* ]] || [[ -z "$cur" ]]; then 28 | COMPREPLY=( $( compgen -W "${opts}" -- ${cur}) ) 29 | fi 30 | 31 | } && 32 | complete -F _ncursesFM ncursesFM 33 | 34 | # ex: ts=4 sw=4 et filetype=sh 35 | -------------------------------------------------------------------------------- /Script/ncursesFM.conf: -------------------------------------------------------------------------------- 1 | ## Fallback editor in case there's no xdg-open, or ncursesFM is executed from a tty. 2 | #editor = "/usr/bin/nano"; 3 | 4 | ## show_hidden: 0 to hide hidden files. Anything else to show them. 5 | # show_hidden = 0; 6 | 7 | ## starting_directory: directory where the program will start by default. 8 | ## If blank or commented, program will start in the directory where it is launched. 9 | # starting_directory = "/home/_YourUsername"; 10 | 11 | ## Not 0 to use starting_directory for second tab too. 0 to use current directory. 12 | # use_default_starting_dir_second_tab = 0; 13 | 14 | ## Not 0 to inhibits power management functions through sd-bus, 15 | ## only while a list of job is running. Defaults to 0. Only for systemd systems. 16 | # inhibit = 0; 17 | 18 | ## Not 0 to open a helper window explaining shortcuts after program started. 19 | # starting_helper = 1; 20 | 21 | ## Not 0 to enable external disk/usb sticks automount support. 22 | ## Requires libudev support and systemd(sd-bus) support. 23 | # automount = 0; 24 | 25 | ## 0 to only log errors, 1 to log warnings too, 26 | ## 2 to log info messages too. 3 disables log. 27 | # loglevel = 0; 28 | 29 | ## Not 0 to have a persistent log across program restarts. 30 | # persistent_log = 0; 31 | 32 | ## Set low battery threshold. When battery reaches a level < threshold, 33 | ## battery level will be printed in red. 34 | # bat_low_level = 15; 35 | 36 | ## Set ui cursor chars 37 | # cursor_chars = "->"; 38 | 39 | ## Sysinfo line layout 40 | ## use: 41 | ## C -> clock 42 | ## P -> process/ram monitor 43 | ## B -> battery monitor 44 | # sysinfo_layout = "CPB"; 45 | 46 | ## Safety level: 47 | ## 0 -> full unsafe (does not ask anything even for installing packages, 48 | ## removing files, print) 49 | ## 1 -> ask before removing / printing / installing packages 50 | ## 2 -> ask before doing everything (that needs user input) 51 | # safe = 2; 52 | 53 | ## Silent: 54 | ## 0 -> to show libnotify notifications 55 | ## !0 -> to avoid showing libnotify notifications 56 | # silent = 0; 57 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | ## TODO 2 | 3 | ### Rewriting 4 | 5 | #### Cmake 6 | 7 | - [x] Port to cmake 8 | - [x] Gettext support 9 | - [x] Install support 10 | - [x] systemd is a mandatory dep 11 | - [x] Libconfig mandatory 12 | - [ ] port to libpopt instead of getopt (?) 13 | - [x] Switch to sr.ht CI 14 | 15 | - [ ] Release 16 | 17 | #### Libmodule 18 | 19 | - [ ] Port to libmodule 20 | - [ ] Release 21 | 22 | #### Ui Changes/improvements 23 | 24 | - [ ] Port to ncurses panel/menu.h 25 | - [ ] let user switch modality from within each other (eg: from device_mode to bookmarks) 26 | - [ ] rename bookmarks to "Places" and add mounted fs 27 | - [ ] new mode: job's mode -> to check and review all queued jobs 28 | - [ ] add a config to start with 2 tabs and to select for each tab the modality -> by default 2 tabs -> first tab files and second tab Places 29 | - [ ] add a manpage and drop helper message (simplify changing modality too) (?) 30 | 31 | ### Trash support 32 | - [ ] add trash support using Trashd daemon (optional, if trashd is not present, trash support won't be loaded), with new key (d?) 33 | -------------------------------------------------------------------------------- /cmake/FindLibmagic.cmake: -------------------------------------------------------------------------------- 1 | 2 | #------------------------------------------------------------------------------- 3 | # Copyright (c) 2013-2013, Lars Baehren 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without modification, 7 | # are permitted provided that the following conditions are met: 8 | # 9 | # * Redistributions of source code must retain the above copyright notice, this 10 | # list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright notice, 12 | # this list of conditions and the following disclaimer in the documentation 13 | # and/or other materials provided with the distribution. 14 | # 15 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | #------------------------------------------------------------------------------- 26 | 27 | # - Check for the presence of LIBMAGIC 28 | # 29 | # The following variables are set when LIBMAGIC is found: 30 | # LIBMAGIC_FOUND = Set to true, if all components of LIBMAGIC have been 31 | # found. 32 | # LIBMAGIC_INCLUDES = Include path for the header files of LIBMAGIC 33 | # LIBMAGIC_LIBRARIES = Link these to use LIBMAGIC 34 | # LIBMAGIC_LFLAGS = Linker flags (optional) 35 | 36 | if (NOT LIBMAGIC_FOUND) 37 | 38 | if (NOT LIBMAGIC_ROOT_DIR) 39 | set (LIBMAGIC_ROOT_DIR ${CMAKE_INSTALL_PREFIX}) 40 | endif (NOT LIBMAGIC_ROOT_DIR) 41 | 42 | ##____________________________________________________________________________ 43 | ## Check for the header files 44 | 45 | find_path (LIBMAGIC_FILE_H 46 | NAMES file/file.h 47 | HINTS ${LIBMAGIC_ROOT_DIR} ${CMAKE_INSTALL_PREFIX} 48 | PATH_SUFFIXES include 49 | ) 50 | if (LIBMAGIC_FILE_H) 51 | list (APPEND LIBMAGIC_INCLUDES ${LIBMAGIC_FILE_H}) 52 | endif (LIBMAGIC_FILE_H) 53 | 54 | find_path (LIBMAGIC_MAGIC_H 55 | NAMES magic.h 56 | HINTS ${LIBMAGIC_ROOT_DIR} ${CMAKE_INSTALL_PREFIX} 57 | PATH_SUFFIXES include include/linux 58 | ) 59 | if (LIBMAGIC_MAGIC_H) 60 | list (APPEND LIBMAGIC_INCLUDES ${LIBMAGIC_MAGIC_H}) 61 | endif (LIBMAGIC_MAGIC_H) 62 | 63 | list (REMOVE_DUPLICATES LIBMAGIC_INCLUDES) 64 | 65 | ##____________________________________________________________________________ 66 | ## Check for the library 67 | 68 | find_library (LIBMAGIC_LIBRARIES magic 69 | HINTS ${LIBMAGIC_ROOT_DIR} ${CMAKE_INSTALL_PREFIX} 70 | PATH_SUFFIXES lib 71 | ) 72 | 73 | ##____________________________________________________________________________ 74 | ## Actions taken when all components have been found 75 | 76 | find_package_handle_standard_args (LIBMAGIC DEFAULT_MSG LIBMAGIC_LIBRARIES LIBMAGIC_INCLUDES) 77 | 78 | if (LIBMAGIC_FOUND) 79 | if (NOT LIBMAGIC_FIND_QUIETLY) 80 | message (STATUS "Found components for LIBMAGIC") 81 | message (STATUS "LIBMAGIC_ROOT_DIR = ${LIBMAGIC_ROOT_DIR}") 82 | message (STATUS "LIBMAGIC_INCLUDES = ${LIBMAGIC_INCLUDES}") 83 | message (STATUS "LIBMAGIC_LIBRARIES = ${LIBMAGIC_LIBRARIES}") 84 | endif (NOT LIBMAGIC_FIND_QUIETLY) 85 | else (LIBMAGIC_FOUND) 86 | if (LIBMAGIC_FIND_REQUIRED) 87 | message (FATAL_ERROR "Could not find LIBMAGIC!") 88 | endif (LIBMAGIC_FIND_REQUIRED) 89 | endif (LIBMAGIC_FOUND) 90 | 91 | ##____________________________________________________________________________ 92 | ## Mark advanced variables 93 | 94 | mark_as_advanced ( 95 | LIBMAGIC_ROOT_DIR 96 | LIBMAGIC_INCLUDES 97 | LIBMAGIC_LIBRARIES 98 | ) 99 | 100 | endif (NOT LIBMAGIC_FOUND) -------------------------------------------------------------------------------- /inc/archiver.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "ui.h" 4 | 5 | int create_archive(void); 6 | int extract_file(void); 7 | -------------------------------------------------------------------------------- /inc/bookmarks.h: -------------------------------------------------------------------------------- 1 | #include "fm.h" 2 | 3 | void get_bookmarks(void); 4 | void add_file_to_bookmarks(const char *str); 5 | void remove_bookmark_from_file(void); 6 | void show_bookmarks(void); 7 | void manage_enter_bookmarks(struct stat current_file_stat); 8 | void remove_all_user_bookmarks(void); 9 | void free_bookmarks(void); 10 | -------------------------------------------------------------------------------- /inc/config.h: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | 6 | void parse_cmd(int argc, char * const argv[]); 7 | void load_config_files(void); 8 | void config_checks(void); 9 | -------------------------------------------------------------------------------- /inc/declarations.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | 19 | // used for internationalization 20 | #define _(str) gettext(str) 21 | 22 | #define MAX_TABS 2 23 | #define MAX_NUMBER_OF_FOUND 100 24 | #define BUFF_SIZE 8192 25 | 26 | /* 27 | * Lines where to print messages to user inside info_win (ui_functions) 28 | */ 29 | #define ASK_LINE 0 30 | #define INFO_LINE 1 31 | #define ERR_LINE 2 32 | #define SYSTEM_INFO_LINE 3 33 | 34 | /* 35 | * Long operations that require a worker thread 36 | */ 37 | #define MOVE_TH 0 38 | #define PASTE_TH 1 39 | #define RM_TH 2 40 | #define ARCHIVER_TH 3 41 | #define EXTRACTOR_TH 4 42 | 43 | /* 44 | * Short (fast) operations that do not require spawning a separate thread 45 | */ 46 | #define NEW_FILE_TH 0 47 | #define CREATE_DIR_TH 1 48 | #define RENAME_TH 2 49 | 50 | /* 51 | * Quit status 52 | */ 53 | #define NORM_QUIT 1 54 | #define MEM_ERR_QUIT 2 55 | #define GENERIC_ERR_QUIT 3 56 | 57 | /* 58 | * Device monitor status 59 | */ 60 | #define DEVMON_OFF -2 61 | #define DEVMON_STARTING -1 62 | #define DEVMON_READY 0 63 | 64 | /* 65 | * Search status 66 | */ 67 | #define NO_SEARCH 0 68 | #define SEARCHING 1 69 | #define SEARCHED 2 70 | 71 | /* 72 | * Safe values 73 | */ 74 | #define FULL_SAFE 2 75 | #define SAFE 1 76 | #define UNSAFE 0 77 | 78 | /* 79 | * struct pollfd indexes 80 | */ 81 | #define GETCH_IX 0 82 | #define TIMER_IX 1 83 | #define INOTIFY_IX1 2 84 | #define INOTIFY_IX2 3 85 | #define INFO_IX 4 86 | #define SIGNAL_IX 5 87 | #if ARCHIVE_VERSION_NUMBER >= 3002000 88 | #define ARCHIVE_IX 6 89 | #define DEVMON_IX 7 90 | #else 91 | #define DEVMON_IX 6 92 | #endif 93 | 94 | /* 95 | * Useful macro to know number of elements in arrays 96 | */ 97 | #define NUM(a) (sizeof(a) / sizeof(*a)) 98 | 99 | /* 100 | * Config settings struct 101 | */ 102 | struct conf { 103 | char editor[PATH_MAX + 1]; 104 | int show_hidden; 105 | char starting_dir[PATH_MAX + 1]; 106 | int second_tab_starting_dir; 107 | int inhibit; 108 | int automount; 109 | int starting_helper; 110 | int loglevel; 111 | int persistent_log; 112 | int bat_low_level; 113 | int safe; 114 | #ifdef LIBNOTIFY_PRESENT 115 | int silent; 116 | #endif 117 | wchar_t cursor_chars[3]; 118 | char sysinfo_layout[4]; 119 | }; 120 | 121 | /* 122 | * for each tab: an fd to catch inotify events, 123 | * and a wd, that uniquely represents an inotify watch. 124 | */ 125 | struct inotify { 126 | int fd; 127 | int wd; 128 | }; 129 | 130 | /* 131 | * Struct that holds UI informations per-tab 132 | */ 133 | struct scrstr { 134 | WINDOW *fm; 135 | int width; 136 | int delta; 137 | int stat_active; 138 | char tot_size[30]; 139 | }; 140 | 141 | enum working_mode {normal, fast_browse_, bookmarks_, search_, device_, selected_}; 142 | 143 | /* 144 | * Struct used to store tab's information 145 | */ 146 | struct tab { 147 | int curr_pos; 148 | char my_cwd[PATH_MAX + 1]; 149 | char (*nl)[PATH_MAX + 1]; 150 | int number_of_files; 151 | char title[PATH_MAX + 1]; 152 | struct inotify inot; 153 | char old_file[NAME_MAX + 1]; 154 | struct scrstr mywin; 155 | enum working_mode mode; 156 | int show_hidden; 157 | int sorting_index; 158 | }; 159 | 160 | /* 161 | * Struct used to store searches information 162 | */ 163 | struct search_vars { 164 | char searched_string[20]; 165 | char found_searched[MAX_NUMBER_OF_FOUND][PATH_MAX + 1]; 166 | int searching; 167 | int search_archive; 168 | int search_lazy; 169 | int found_cont; 170 | }; 171 | 172 | /* 173 | * Struct that defines a list of thread job to be executed one after the other. 174 | */ 175 | typedef struct thread_list { 176 | // list of file selected for this job 177 | char (*selected_files)[PATH_MAX + 1]; 178 | // number of files selected for this job 179 | int num_selected; 180 | // function associated to this job 181 | int (*f)(void); 182 | // when needed: fullpath (eg where to extract each file) 183 | char full_path[PATH_MAX + 1]; 184 | // next job pointer 185 | struct thread_list *next; 186 | // num of this job (index of this job in thread_h list) 187 | // needed when printing "job [1/4]" on INFO_LINE 188 | int num; 189 | // type of this job (needed to associate it with its function) 190 | int type; 191 | } thread_job_list; 192 | 193 | /* 194 | * main_p: Needed to interrupt main cycles getch 195 | * from external signals; 196 | * nfds: number of elements in main_p struct; 197 | * info_fd: pipe used to pass info_msg waiting 198 | * to be printed to main_poll. 199 | */ 200 | struct pollfd *main_p; 201 | int nfds, info_fd[2]; 202 | #if ARCHIVE_VERSION_NUMBER >= 3002000 203 | int archive_cb_fd[2]; 204 | char passphrase[100]; 205 | #endif 206 | thread_job_list *thread_h; 207 | char (*selected)[PATH_MAX + 1]; 208 | struct conf config; 209 | struct tab ps[MAX_TABS]; 210 | struct search_vars sv; 211 | 212 | /* 213 | * active win, quit status, number of worker thread jobs, 214 | * tabs counter and device_init status. 215 | * Has_X-> needed for xdg-open (if we're on a X env) and for notifications 216 | * num_selected -> number of selected files 217 | */ 218 | int active, quit, num_of_jobs, cont, device_init, has_desktop, num_selected; 219 | 220 | pthread_t install_th; 221 | pthread_t worker_th, search_th; 222 | 223 | /* 224 | * pointer to abstract which list of strings currently 225 | * is active for current tab 226 | */ 227 | char (*str_ptr[MAX_TABS])[PATH_MAX + 1]; 228 | -------------------------------------------------------------------------------- /inc/devices.h: -------------------------------------------------------------------------------- 1 | #include "fm.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void isomount(const char *str); 9 | int start_monitor(void); 10 | void devices_bus_process(void); 11 | void show_devices_tab(void); 12 | void manage_mount_device(void); 13 | void manage_enter_device(void); 14 | void free_device_monitor(void); 15 | void show_devices_stat(int i, int win, char *str); 16 | -------------------------------------------------------------------------------- /inc/fm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "install_package.h" 4 | #include "search.h" 5 | #include "archiver.h" 6 | #include "worker_thread.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #ifdef LIBCUPS_PRESENT 14 | #include "print.h" 15 | #endif 16 | 17 | int change_dir(const char *str, int win); 18 | void change_tab(void); 19 | void switch_hidden(void); 20 | void manage_file(const char *str); 21 | void fast_file_operations(const int index); 22 | int remove_file(void); 23 | void manage_space_press(const char *str); 24 | void manage_all_space_press(void); 25 | void remove_selected(void); 26 | void remove_all_selected(void); 27 | void show_selected(void); 28 | void free_selected(void); 29 | int paste_file(void); 30 | int move_file(void); 31 | void fast_browse(wint_t c); 32 | 33 | -------------------------------------------------------------------------------- /inc/inhibit.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "ui.h" 4 | 5 | int inhibit_suspend(const char *str); 6 | void close_bus(sd_bus_error *error, sd_bus_message *mess, sd_bus *bus); 7 | void stop_inhibition(int fd); 8 | -------------------------------------------------------------------------------- /inc/install_package.h: -------------------------------------------------------------------------------- 1 | #include "inhibit.h" 2 | 3 | void *install_package(void *str); -------------------------------------------------------------------------------- /inc/log.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "declarations.h" 7 | 8 | /* 9 | * Log levels 10 | */ 11 | #define LOG_ERR 0 12 | #define LOG_WARN 1 13 | #define LOG_INFO 2 14 | #define NO_LOG 3 15 | 16 | #define INFO(msg) log_message(__FILE__, __LINE__, __func__, msg, 'I', LOG_INFO) 17 | #define WARN(msg) log_message(__FILE__, __LINE__, __func__, msg, 'W', LOG_WARN) 18 | #define ERROR(msg) log_message(__FILE__, __LINE__, __func__, msg, 'E', LOG_ERR) 19 | #define ERROR_INT(num) \ 20 | do { \ 21 | char num_str[50]; \ 22 | sprintf(num_str, "%d", num); \ 23 | log_message(__FILE__, __LINE__, __func__, str, 'E', LOG_ERR); \ 24 | } while (0) 25 | 26 | void open_log(void); 27 | void log_message(const char *filename, int lineno, const char *funcname, const char *log_msg, char type, int log_level); 28 | void close_log(void); 29 | -------------------------------------------------------------------------------- /inc/notify.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "declarations.h" 5 | 6 | void init_notify(void); 7 | void send_notification(const char *mesg); 8 | void destroy_notify(void); 9 | -------------------------------------------------------------------------------- /inc/print.h: -------------------------------------------------------------------------------- 1 | #include "ui.h" 2 | #include 3 | 4 | void print_support(const char *str); 5 | -------------------------------------------------------------------------------- /inc/quit.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ui.h" 4 | #include "bookmarks.h" 5 | 6 | int program_quit(void); 7 | -------------------------------------------------------------------------------- /inc/search.h: -------------------------------------------------------------------------------- 1 | #include "fm.h" 2 | 3 | #define SEARCH_LINE 2 4 | 5 | void search(void); 6 | void list_found(void); 7 | int search_enter_press(const char *str); 8 | void leave_search_mode(const char *str); 9 | -------------------------------------------------------------------------------- /inc/string_constants.h: -------------------------------------------------------------------------------- 1 | #define LONG_FILE_OPERATIONS 5 2 | #define SHORT_FILE_OPERATIONS 3 3 | 4 | #define MODES 6 5 | 6 | extern const char yes[]; 7 | extern const char no[]; 8 | 9 | extern const int HELPER_HEIGHT[MODES]; 10 | 11 | extern const char editor_missing[]; 12 | 13 | extern const char generic_error[]; 14 | 15 | extern const char *info_win_str[3]; 16 | 17 | extern const char *arch_ext[6]; 18 | 19 | extern const char *sorting_str[4]; 20 | 21 | extern const char bookmarks_add_quest[]; 22 | extern const char bookmarks_rm_quest[]; 23 | extern const char bookmarks_xdg_err[]; 24 | extern const char bookmarks_file_err[]; 25 | extern const char bookmark_added[]; 26 | extern const char bookmarks_rm[]; 27 | extern const char no_bookmarks[]; 28 | extern const char inexistent_bookmark_quest[]; 29 | extern const char inexistent_bookmark[]; 30 | extern const char bookmark_already_present[]; 31 | extern const char bookmarks_cleared[]; 32 | 33 | extern const char no_selected_files[]; 34 | extern const char *file_sel[6]; 35 | extern const char selected_cleared[]; 36 | 37 | extern const char sure[]; 38 | 39 | extern const char already_searching[]; 40 | extern const char search_insert_name[]; 41 | extern const char search_archives[]; 42 | extern const char lazy_search[]; 43 | extern const char searched_string_minimum[]; 44 | extern const char too_many_found[]; 45 | extern const char no_found[]; 46 | extern const char already_search_mode[]; 47 | extern const char *searching_mess[2]; 48 | 49 | #ifdef LIBCUPS_PRESENT 50 | extern const char print_question[]; 51 | extern const char print_ok[]; 52 | extern const char print_fail[]; 53 | #endif 54 | 55 | extern const char archiving_mesg[]; 56 | 57 | extern const char ask_name[]; 58 | 59 | extern const char extr_question[]; 60 | 61 | extern const char pwd_archive[]; 62 | 63 | extern const char *thread_job_mesg[LONG_FILE_OPERATIONS]; 64 | extern const char *thread_str[LONG_FILE_OPERATIONS]; 65 | extern const char *thread_fail_str[LONG_FILE_OPERATIONS]; 66 | extern const char *short_msg[SHORT_FILE_OPERATIONS]; 67 | extern const char selected_mess[]; 68 | 69 | extern const char thread_running[]; 70 | extern const char quit_with_running_thread[]; 71 | 72 | extern const char pkg_quest[]; 73 | extern const char install_th_wait[]; 74 | extern const char install_success[]; 75 | extern const char install_failed[]; 76 | extern const char package_warn[]; 77 | extern const char device_mode_str[]; 78 | extern const char no_devices[]; 79 | extern const char dev_mounted[]; 80 | extern const char dev_unmounted[]; 81 | extern const char ext_dev_mounted[]; 82 | extern const char ext_dev_unmounted[]; 83 | extern const char device_added[]; 84 | extern const char device_removed[]; 85 | extern const char no_proc_mounts[]; 86 | extern const char polling[]; 87 | extern const char monitor_err[]; 88 | extern const char bookmarks_mode_str[]; 89 | extern const char search_mode_str[]; 90 | extern const char selected_mode_str[]; 91 | 92 | extern const char ac_online[]; 93 | extern const char power_fail[]; 94 | 95 | extern const char *insert_string[]; 96 | 97 | extern const char win_too_small[]; 98 | 99 | extern const char helper_title[]; 100 | extern const char helper_string[MODES][16][150]; 101 | -------------------------------------------------------------------------------- /inc/sysinfo.h: -------------------------------------------------------------------------------- 1 | #include "ui.h" 2 | #include 3 | #include 4 | 5 | int start_timer(void); 6 | void timer_func(void); 7 | void free_timer(void); 8 | -------------------------------------------------------------------------------- /inc/ui.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sysinfo.h" 4 | #include "devices.h" 5 | #include "string_constants.h" 6 | #include "quit.h" 7 | #include "utils.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define INFO_HEIGHT 4 18 | #define SEL_COL 3 19 | #define STAT_LENGTH 20 20 | #define PERM_LENGTH 10 21 | 22 | // colors for active win / fast browse mode 23 | #define ACTIVE_COL 3 24 | #define FAST_BROWSE_COL 5 25 | 26 | /* 27 | * inotify macros 28 | */ 29 | #define EVENT_SIZE (sizeof(struct inotify_event)) 30 | #define BUF_LEN (1024 * (EVENT_SIZE + 16)) 31 | 32 | void screen_init(void); 33 | void screen_end(void); 34 | void reset_win(int win); 35 | void new_tab(int win); 36 | void resize_tab(int win, int resizing); 37 | void delete_tab(int win); 38 | void scroll_down(int win, int lines); 39 | void scroll_up(int win, int lines); 40 | void trigger_show_helper_message(void); 41 | void trigger_stats(void); 42 | wint_t main_poll(WINDOW *win); 43 | void timer_event(void); 44 | void tab_refresh(int win); 45 | void update_special_mode(int num, char (*str)[PATH_MAX + 1], int mode); 46 | void show_special_tab(int num, char (*str)[PATH_MAX + 1], const char *title, int mode); 47 | void leave_special_mode(const char *str, int win); 48 | void print_info(const char *str, int i); 49 | void print_and_warn(const char *err, int line); 50 | void ask_user(const char *str, char *input, int d); 51 | void resize_win(void); 52 | void change_sort(void); 53 | void highlight_selected(const char *str, const char c, int win); 54 | void erase_selected_highlight(void); 55 | void update_colors(void); 56 | void update_time(int where); 57 | void update_sysinfo(int where); 58 | void update_batt(int online, int perc[], int num_of_batt, char name[][10], int where); 59 | void trigger_fullname_win(void); 60 | -------------------------------------------------------------------------------- /inc/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "log.h" 6 | #include "ui.h" 7 | 8 | void *remove_from_list(int *num, char (*str)[PATH_MAX + 1], int i); 9 | void *safe_realloc(const size_t size, char (*str)[PATH_MAX + 1]); 10 | int is_ext(const char *filename, const char *ext[], int size); 11 | int get_mimetype(const char *path, const char *test); 12 | int move_cursor_to_file(int start_idx, const char *filename, int win); 13 | void save_old_pos(int win); 14 | int is_present(const char *name, char (*str)[PATH_MAX + 1], int num, int len, int start_idx); 15 | void change_unit(float size, char *str); 16 | void leave_mode_helper(struct stat s); 17 | -------------------------------------------------------------------------------- /inc/worker_thread.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "inhibit.h" 4 | #ifdef LIBNOTIFY_PRESENT 5 | #include "notify.h" 6 | #endif 7 | 8 | struct thread_mesg { 9 | const char *str; 10 | int line; 11 | }; 12 | 13 | void init_job_queue(void); 14 | void destroy_job_queue(void); 15 | void init_thread(int type, int (* const f)(void)); 16 | -------------------------------------------------------------------------------- /msg/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(Gettext) 2 | if(GETTEXT_FOUND) 3 | add_subdirectory(it) 4 | add_subdirectory(de) 5 | add_subdirectory(fr) 6 | add_subdirectory(es_AR) 7 | else(GETTEXT_FOUND) 8 | message( 9 | "------ 10 | NOTE: Gettext not found. Translations will *not* be installed. 11 | ------") 12 | endif(GETTEXT_FOUND) 13 | -------------------------------------------------------------------------------- /msg/de/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(GLOB _po_files *.po) 2 | gettext_process_po_files(de ALL INSTALL_DESTINATION ${CMAKE_INSTALL_LOCALEDIR} PO_FILES ${_po_files}) 3 | -------------------------------------------------------------------------------- /msg/de/de.po: -------------------------------------------------------------------------------- 1 | # ncursesFM italian translated strings. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # Federico Di Pierro , 2016 5 | # 6 | #: ncursesFM 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: 3.0\n" 10 | "Report-Msgid-Bugs-To: nierro92@gmail.com\n" 11 | "POT-Creation-Date: 2016-06-26 16:00+0100\n" 12 | "PO-Revision-Date: 2017-09-18 16:00+0100\n" 13 | "Last-Translator: Matthias Beyer \n" 14 | "Language-Team: ncursesFM\n" 15 | "Language: de\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | 20 | msgid "y" 21 | msgstr "j" 22 | 23 | msgid "n" 24 | msgstr "n" 25 | 26 | msgid "ENTER" 27 | msgstr "ENTER" 28 | 29 | msgid "SPACE" 30 | msgstr "LEERTASTE" 31 | 32 | msgid "ARROW KEYS" 33 | msgstr "PFEILTASTEN" 34 | 35 | msgid "PG_UP/DOWN" 36 | msgstr "PG_UP/DOWN" 37 | 38 | msgid "DEL" 39 | msgstr "DEL" 40 | 41 | msgid "Press 'L' to trigger helper" 42 | msgstr "Drücke 'L' um die Hilfe aufzurufen" 43 | 44 | msgid "Remember: every shortcut in ncursesFM is case insensitive." 45 | msgstr "Vergiss nicht: ncursesFM achtet nicht auf Groß/Kleinschreibung." 46 | 47 | msgid "surf between folders or to open files." 48 | msgstr "Wechsle zwischen Ordnern oder offenen Dateien." 49 | 50 | msgid "It will eventually (un)mount your ISO files or install your distro downloaded packages." 51 | msgstr "Eventuel wird deine ISO ge(un)mounted oder heruntergelade Pakete installieren." 52 | 53 | msgid "enable fast browse mode: it lets you jump between files by just typing their name." 54 | msgstr "Aktiviere schnell-browse mode: Wechsle zwischen Dateien indem du ihren Namen eintippst." 55 | 56 | msgid "jump straight to first/last file." 57 | msgstr "Springe zur ersten/letzten Datei." 58 | 59 | msgid "check files fullname." 60 | msgstr "Überprüfe den vollen Namen der Dateien." 61 | 62 | msgid "trigger the showing of hidden files." 63 | msgstr "Zeige versteckte Dateien." 64 | 65 | msgid "see files stats." 66 | msgstr "Zeige Dateistatistiken." 67 | 68 | msgid "change sorting function: alphabetically (default), by size, by last modified or by type." 69 | msgstr "Ändere Sortierfunktion: Alphabetisch (standard), nach Größe, nach Modifizierungszeit oder nach Typ." 70 | 71 | msgid "select files. Once more to remove the file from selected files." 72 | msgstr "Selektiere Dateien. Nochmals benutzen um eine Datei von den selektierten Dateien zu entfernen." 73 | 74 | msgid "paste/cut." 75 | msgstr "einfügen/ausschneiden." 76 | 77 | msgid "compress." 78 | msgstr "komprimieren." 79 | 80 | msgid "remove." 81 | msgstr "löschen." 82 | 83 | msgid "extract." 84 | msgstr "auspacken." 85 | 86 | msgid "print." 87 | msgstr "drucken." 88 | 89 | msgid "switch to bookmarks mode." 90 | msgstr "Zum Lesenzeichen-Modus wechseln." 91 | 92 | msgid "add/remove current file to bookmarks." 93 | msgstr "aktuelle Datei zu Lesenzeichen hinzufügen/entfernen." 94 | 95 | msgid "rename current file/dir." 96 | msgstr "Aktuelle Datei/Verzeichnis umbenennen." 97 | 98 | msgid "create new file/dir." 99 | msgstr "Neue Datei/Verzeichnis anlegen." 100 | 101 | msgid "search for a file." 102 | msgstr "Datei suchen." 103 | 104 | msgid "create second tab." 105 | msgstr "neuen Tab anlegen." 106 | 107 | msgid "close second tab." 108 | msgstr "zweiten Tab schließen." 109 | 110 | msgid "switch between tabs." 111 | msgstr "zwischn Tabs wechseln." 112 | 113 | msgid "switch to device mode." 114 | msgstr "Zum Gerätemodus wechseln." 115 | 116 | msgid "switch to selected mode." 117 | msgstr "Zum Selektierungsmodus wechseln." 118 | 119 | msgid "quit." 120 | msgstr "Schließen." 121 | 122 | msgid "Just start typing your desired filename, to move right to its position." 123 | msgstr "Beginn einfach, den gewünschten Dateinamen zu tippen um zur richtigen Position zu gelagen." 124 | 125 | msgid "leave fast browse mode." 126 | msgstr "Verlasse Schnell-Browse-Modus." 127 | 128 | msgid "remove selected file from bookmarks." 129 | msgstr "Entferne selektierte Datei von Lesezeichen." 130 | 131 | msgid "remove all user bookmarks." 132 | msgstr "Alle Nutzer-Lesezeichen entfernen." 133 | 134 | msgid "move to the folder/file selected." 135 | msgstr "bewege zum selektieren Ordner/Datei." 136 | 137 | msgid "leave bookmarks mode." 138 | msgstr "Verlasse Lesezeichenmodus." 139 | 140 | msgid "leave search mode." 141 | msgstr "Verlasse Suchmodus." 142 | 143 | msgid "leave selected mode." 144 | msgstr "Verlasse Selektierungsmodus." 145 | 146 | msgid "(un)mount current device." 147 | msgstr "(un)mount aktuelles Gerät." 148 | 149 | msgid "move to current device mountpoint, mounting it if necessary." 150 | msgstr "Bewege dich zum aktuellen Geräteeinhängepunkt, einbinden wenn notwendig." 151 | 152 | msgid "leave device mode." 153 | msgstr "Verlasse Gerätemodus." 154 | 155 | msgid "remove current file selection." 156 | msgstr "Entferne aktuelle Dateiselektion." 157 | 158 | msgid "remove all selected files." 159 | msgstr "Lösche alle selektierten Dateien." 160 | 161 | msgid "You have to specify a valid editor in config file." 162 | msgstr "Du musst einen validen Edtiro in der Konfiguration angeben." 163 | 164 | msgid "A generic error occurred. Check log." 165 | msgstr "Ein generischer Error ist aufgetreten. Überprüfe den Log." 166 | 167 | msgid "Files will be sorted alphabetically now." 168 | msgstr "Dateien sind nun Alphabetisch geordnet." 169 | 170 | msgid "Files will be sorted by size now." 171 | msgstr "Dateien sind nun ach Größe geordnet." 172 | 173 | msgid "Files will be sorted by last access now." 174 | msgstr "Dateien sind nun nach letzter Zugriffszeit geordnet." 175 | 176 | msgid "Files will be sorted by type now." 177 | msgstr "Dateien sind nun nach Typ geordnet." 178 | 179 | msgid "Adding current file to bookmarks. Proceed? Y/n:> " 180 | msgstr "Aktuelle Datei wird den Lesezeichen hinzugefügt. Fortfahren? J/n:> " 181 | 182 | msgid "Removing current file from bookmarks. Proceed? Y/n:> " 183 | msgstr "Aktuell Datei wird von den Lesezeichen entfernt. Fortfahren? J/n:> " 184 | 185 | msgid "You cannot remove xdg defined user dirs." 186 | msgstr "Es können keine XDG definierten Nutzerordner entfernt werden." 187 | 188 | msgid "Could not open bookmarks file." 189 | msgstr "Konnte Lesezeichendatei nicht öffnen." 190 | 191 | msgid "Added to bookmarks!" 192 | msgstr "Den Lesezeichen hinzugefügt!" 193 | 194 | msgid "Removed from bookmarks!" 195 | msgstr "Von den Lesezeichen entfernt!" 196 | 197 | msgid "No bookmarks found." 198 | msgstr "Keine Lesezeichen gefunden." 199 | 200 | msgid "Bookmark was deleted as it was no more existent." 201 | msgstr "Lesezeichen wurde entfernt da es nicht mehr existiert." 202 | 203 | msgid "It seems current bookmark no longer exists. Remove it? Y/n:> " 204 | msgstr "Anscheinend existiert das Lesezeichen nicht mehr. Soll es gelöscht werden? J/n:> " 205 | 206 | msgid "Bookmark already present. Would you like to remove it? Y/n:> " 207 | msgstr "Lesezeichen existiert bereits. Soll es gelöscht werden? J/n:> " 208 | 209 | msgid "User bookmarks have been cleared." 210 | msgstr "Nutzerlesezeichen bereinigt." 211 | 212 | msgid "There are no selected files." 213 | msgstr "Keine Dateien selektiert." 214 | 215 | msgid "File selected." 216 | msgstr "Datei selektiert." 217 | 218 | msgid "File unselected." 219 | msgstr "Datei deselektiert." 220 | 221 | msgid "File unselected. Selected list empty." 222 | msgstr "Datei deselektiert. Selektionsliste leer." 223 | 224 | msgid "Every file in this directory has been selected." 225 | msgstr "Jede Datei in diesem Verzeichnis wurde selektiert." 226 | 227 | msgid "Every file in this directory has been unselected." 228 | msgstr "Jede Datei in diesem Verzeichnis wurde deselektiert." 229 | 230 | msgid "Every file in this directory has been unselected. Selected list empty." 231 | msgstr "Jede Datei in diesem Verzeichnis wurde deselektiert. Selektionsliste leer." 232 | 233 | msgid "List of selected files has been cleared." 234 | msgstr "Liste von selektierten Dateien wurde bereinigt." 235 | 236 | msgid "Are you serious? y/N:> " 237 | msgstr "Sicher? j/N:> " 238 | 239 | msgid "There's already a search in progress. Wait for it." 240 | msgstr "Es läuft bereits eine Suche. Warte auf diese." 241 | 242 | msgid "Insert filename to be found, at least 5 chars, max 20 chars.:> " 243 | msgstr "Zu findenden Dateiname, mindestens 5 Zeichen, maximal 20 Zeichen.:> " 244 | 245 | msgid "Do you want to search in archives too? y/N:> " 246 | msgstr "Soll auch in den Archiven gesucht werden? j/N> " 247 | 248 | msgid "Do you want a lazy search (less precise but faster)? y/N:>" 249 | msgstr "Faule suche (weniger genau aber schneller)? j/N:> " 250 | 251 | msgid "At least 5 chars..." 252 | msgstr "Mindestens 5 Zeichen..." 253 | 254 | msgid "Too many files found; try with a larger string." 255 | msgstr "Zu viele Dateien gefunden; versuche einen längeren text." 256 | 257 | msgid "No files found." 258 | msgstr "Keine Dateien gefunden." 259 | 260 | msgid "Searching..." 261 | msgstr "Suche..." 262 | 263 | msgid "Search finished. Press f anytime from normal mode to view the results." 264 | msgstr "Suche fertig. Drücke jederzeit f im normalen modus um die Ergebnisse anzuzeigen." 265 | 266 | msgid "Do you really want to print this file? Y/n:> " 267 | msgstr "Sicher das diese Datei gedruckt werden soll? J/n:> " 268 | 269 | msgid "Print job added." 270 | msgstr "Druckauftrag hinzugefügt." 271 | 272 | msgid "No printers available." 273 | msgstr "Keine Drucker verfügbar." 274 | 275 | msgid "Insert new file name (defaults to first entry name):> " 276 | msgstr "Gebe einen neuen Dateinamen ein (standard ist der name des ersten Eintrags):> " 277 | 278 | msgid "Insert new name:> " 279 | msgstr "Gebe neuen Namen ein:> " 280 | 281 | msgid "Do you really want to extract this archive? Y/n:> " 282 | msgstr "Dieses Archiv wirklich auspacken? J/n:> " 283 | 284 | msgid "Current archive is encrypted. Enter a pwd:> " 285 | msgstr "Aktuelles Archiv ist verschlüsselt. Password:> " 286 | 287 | msgid "Cutting..." 288 | msgstr "Ausschneiden..." 289 | 290 | msgid "Pasting..." 291 | msgstr "Einfügen..." 292 | 293 | msgid "Removing..." 294 | msgstr "Löschen..." 295 | 296 | msgid "Archiving..." 297 | msgstr "Archivieren..." 298 | 299 | msgid "Extracting..." 300 | msgstr "Auspacken..." 301 | 302 | msgid "Every file has been cut." 303 | msgstr "Jede Datei wurde ausgeschnitten." 304 | 305 | msgid "Every file has been pasted." 306 | msgstr "Jede Datei wurde eingefügt." 307 | 308 | msgid "File/dir removed." 309 | msgstr "Datei/Ordner gelöscht." 310 | 311 | msgid "Archive is ready." 312 | msgstr "Archiv ist fertig." 313 | 314 | msgid "Succesfully extracted." 315 | msgstr "Erfolgreich ausgepackt." 316 | 317 | msgid "Could not move" 318 | msgstr "Konnte nicht verschieben." 319 | 320 | msgid "Could not paste." 321 | msgstr "Konnte nicht einfügen." 322 | 323 | msgid "Could not remove every file." 324 | msgstr "Konnte nicht jede Datei löschen." 325 | 326 | msgid "Could not archive." 327 | msgstr "Konnte nicht archivieren." 328 | 329 | msgid "Could not extract every file." 330 | msgstr "Konnte nicht jede Datei auspacken." 331 | 332 | msgid "File created." 333 | msgstr "Datei angelegt." 334 | 335 | msgid "Dir created." 336 | msgstr "Ordner angelegt." 337 | 338 | msgid "File renamed." 339 | msgstr "Datei umbenannt." 340 | 341 | msgid "There are selected files." 342 | msgstr "Es gibt selektierte Dateien." 343 | 344 | msgid "There's already an active job. This job will be queued." 345 | msgstr "Es existiert bereits ein aktiver Auftrag. Dieser Auftrag wird angestellt." 346 | 347 | msgid "Queued jobs still running. Waiting..." 348 | msgstr "Angestellte Aufträge laufen noch. Warten..." 349 | 350 | msgid "Do you really want to install this package? y/N:> " 351 | msgstr "Sicher das dieses Paket installiert werden soll? j/N:> " 352 | 353 | msgid "Waiting for package installation to finish..." 354 | msgstr "Warte auf Paketinstallation..." 355 | 356 | msgid "Currently there is no check against wrong package arch: it will crash packagekit and ncursesfm." 357 | msgstr "Es gibt aktuell keine Möglichkeit eine falsche Paketarchitektur zu überprüfen: Dies wird packagekit oder ncursesfm zum Absturz bringen." 358 | 359 | msgid "Devices:" 360 | msgstr "Geräte:" 361 | 362 | msgid "Bookmarks:" 363 | msgstr "Lesezeichen:" 364 | 365 | msgid "Selected files:" 366 | msgstr "Selektierte Dateien:" 367 | 368 | # if you've got to switch format modifiers, see here: 369 | # http://www.gnu.org/software/gettext/manual/gettext.html#c_002dformat-Flag 370 | #, c-format 371 | msgid "%d files found searching %s:" 372 | msgstr "%d Dateien mit %s gefunden:" 373 | 374 | msgid "Installed." 375 | msgstr "Installiert." 376 | 377 | msgid "Could not install." 378 | msgstr "Konnte nicht installieren." 379 | 380 | msgid "No devices found." 381 | msgstr "Keine Geräte gefunden." 382 | 383 | # if you've got to switch format modifiers, see here: 384 | # http://www.gnu.org/software/gettext/manual/gettext.html#c_002dformat-Flag 385 | #, c-format 386 | msgid "%s mounted in: %s." 387 | msgstr "%s angebunden in: %s." 388 | 389 | msgid "%s unmounted." 390 | msgstr "%s ausgehängt." 391 | 392 | msgid "External tool has mounted %s." 393 | msgstr "Externes Werkzeug wurde eingehängt %s." 394 | 395 | msgid "External tool has unmounted %s." 396 | msgstr "Externes Werkzeug wurde ausgehängt %s." 397 | 398 | msgid "New device connected." 399 | msgstr "Neues Gerät verbunden." 400 | 401 | msgid "Device removed." 402 | msgstr "Gerät entfernt." 403 | 404 | msgid "Could not find /proc/mounts." 405 | msgstr "/proc/mounts konnte nicht gefunden werden." 406 | 407 | msgid "On AC" 408 | msgstr "Batteriebetrieb" 409 | 410 | msgid "No power supply info available." 411 | msgstr "Keine Stromanschlussinformation verfügbar." 412 | 413 | msgid "Window too small. Enlarge it." 414 | msgstr "Schaufenster zu klein. Vergrößern." 415 | 416 | msgid "Still polling for devices." 417 | msgstr "Noch Polling für Geräte." 418 | 419 | msgid "Monitor is not active. An error occurred, check log file." 420 | msgstr "Monitor ist nicht aktiv. Ein Fehler ist aufgetreten, Überprüfe den Log." 421 | -------------------------------------------------------------------------------- /msg/es_AR/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(GLOB _po_files *.po) 2 | gettext_process_po_files(es_AR ALL INSTALL_DESTINATION ${CMAKE_INSTALL_LOCALEDIR} PO_FILES ${_po_files}) 3 | -------------------------------------------------------------------------------- /msg/es_AR/es_AR.po: -------------------------------------------------------------------------------- 1 | # ncursesFM spanish translated strings. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # Federico Di Pierro , 2016 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: 3.0\n" 9 | "Report-Msgid-Bugs-To: nierro92@gmail.com\n" 10 | "POT-Creation-Date: 2016-07-03 00:00-0300\n" 11 | "PO-Revision-Date: 2019-01-14 21:03-0300\n" 12 | "Language-Team: ncursesFM\n" 13 | "Content-Type: text/plain; charset=UTF-8\n" 14 | "Content-Transfer-Encoding: 8bit\n" 15 | "Last-Translator: Daniel T. Borelli \n" 16 | "MIME-Version: 1.0\n" 17 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 18 | "Language: es_AR\n" 19 | "X-Generator: Poedit 2.2\n" 20 | "X-Poedit-SourceCharset: UTF-8\n" 21 | 22 | msgid "y" 23 | msgstr "s" 24 | 25 | msgid "n" 26 | msgstr "n" 27 | 28 | msgid "ENTER" 29 | msgstr "INTRO" 30 | 31 | msgid "SPACE" 32 | msgstr "ESPACIO" 33 | 34 | msgid "ARROW KEYS" 35 | msgstr "TECLAS DE FLECHAS" 36 | 37 | msgid "PG_UP/DOWN" 38 | msgstr "RePág/AvPág" 39 | 40 | msgid "DEL" 41 | msgstr "SUPR" 42 | 43 | msgid "Press 'L' to trigger helper" 44 | msgstr "Pulse 'L' para activar la ayuda" 45 | 46 | msgid "Remember: every shortcut in ncursesFM is case insensitive." 47 | msgstr "Recuerde: cada atajo en ncursesFM es insensible a mayúsculas." 48 | 49 | msgid "surf between folders or to open files." 50 | msgstr "permite acceder a carpetas o abrir archivos." 51 | 52 | msgid "It will eventually (un)mount your ISO files or install your distro downloaded packages." 53 | msgstr "Eventualmente puede (des)montar sus archivos ISO o instalar los paquetes descargados de su distribución." 54 | 55 | msgid "enable fast browse mode: it lets you jump between files by just typing their name." 56 | msgstr "habilita el modo de navegación rápida. Esto le permite saltar entre archivos con sólo escribir sus nombres." 57 | 58 | msgid "jump straight to first/last file." 59 | msgstr "salta hacia el primer o último archivo." 60 | 61 | msgid "check files fullname." 62 | msgstr "comprueba el nombre completo de los archivos." 63 | 64 | msgid "trigger the showing of hidden files." 65 | msgstr "muestra los archivos ocultos." 66 | 67 | msgid "see files stats." 68 | msgstr "ver las estadísticas de los archivos." 69 | 70 | msgid "change sorting function: alphabetically (default), by size, by last modified or by type." 71 | msgstr "cambia la función de ordenamiento: alfabético (predeterminado), por tamaño, última modificación o por tipo." 72 | 73 | msgid "select files. Once more to remove the file from selected files." 74 | msgstr "selecciona los archivos. Una vez más para eliminar el archivo de la lista de seleccionados." 75 | 76 | msgid "paste/cut." 77 | msgstr "pegar/cortar." 78 | 79 | msgid "compress." 80 | msgstr "comprimir." 81 | 82 | msgid "remove." 83 | msgstr "eliminar." 84 | 85 | msgid "extract." 86 | msgstr "extraer." 87 | 88 | msgid "print." 89 | msgstr "imprimir." 90 | 91 | msgid "switch to bookmarks mode." 92 | msgstr "cambia al modo de marcadores." 93 | 94 | msgid "add/remove current file to bookmarks." 95 | msgstr "agrega/quita el archivo actual de los marcadores." 96 | 97 | msgid "rename current file/dir." 98 | msgstr "renombra el archivo/directorio actual." 99 | 100 | msgid "create new file/dir." 101 | msgstr "crea un nuevo archivo/directorio." 102 | 103 | msgid "search for a file." 104 | msgstr "busca un archivo." 105 | 106 | msgid "create second tab." 107 | msgstr "crear una segunda pestaña." 108 | 109 | msgid "close second tab." 110 | msgstr "cierra la segunda pestaña." 111 | 112 | msgid "switch between tabs." 113 | msgstr "salta entre pestañas." 114 | 115 | msgid "switch to device mode." 116 | msgstr "cambia al modo de dispositivo." 117 | 118 | msgid "switch to selected mode." 119 | msgstr "cambia al modo de selección." 120 | 121 | msgid "quit." 122 | msgstr "salir." 123 | 124 | msgid "Just start typing your desired filename, to move right to its position." 125 | msgstr "Empiece a escribir el nombre del archivo que desee, para moverlo a su posición." 126 | 127 | msgid "leave fast browse mode." 128 | msgstr "salir del modo navegación." 129 | 130 | msgid "remove selected file from bookmarks." 131 | msgstr "remueve el archivo seleccionado de los marcadores." 132 | 133 | msgid "remove all user bookmarks." 134 | msgstr "remueve todos los marcadores del usuario." 135 | 136 | msgid "move to the folder/file selected." 137 | msgstr "mueve el directorio/archivo seleccionado." 138 | 139 | msgid "leave bookmarks mode." 140 | msgstr "terminar el modo de marcadores." 141 | 142 | msgid "leave search mode." 143 | msgstr "terminar el modo de búsqueda." 144 | 145 | msgid "leave selected mode." 146 | msgstr "terminar el modo de selección." 147 | 148 | msgid "(un)mount current device." 149 | msgstr "(des)montar el dispositivo actual." 150 | 151 | msgid "move to current device mountpoint, mounting it if necessary." 152 | msgstr "mueve el actual punto de montaje del dispositivo, lo monta si es necesario." 153 | 154 | msgid "leave device mode." 155 | msgstr "terminar el modo de dispositivo." 156 | 157 | msgid "remove current file selection." 158 | msgstr "elimina el archivo actual seleccionado." 159 | 160 | msgid "remove all selected files." 161 | msgstr "elimina todos los archivo seleccionados." 162 | 163 | msgid "You have to specify a valid editor in config file." 164 | msgstr "Usted debe especificar un editor válido en el archivo de configuración." 165 | 166 | msgid "A generic error occurred. Check log." 167 | msgstr "Ocurrió un error general. Compruebe el registro." 168 | 169 | msgid "Files will be sorted alphabetically now." 170 | msgstr "Los archivos ahora están ordenados alfabéticamente." 171 | 172 | msgid "Files will be sorted by size now." 173 | msgstr "Los archivos ahora están ordenados por tamaño." 174 | 175 | msgid "Files will be sorted by last access now." 176 | msgstr "Los archivos ahora están ordenados por el último acceso." 177 | 178 | msgid "Files will be sorted by type now." 179 | msgstr "Los archivos ahora están ordenados por tipo." 180 | 181 | msgid "Adding current file to bookmarks. Proceed? Y/n:> " 182 | msgstr "Agregando el archivo actual a los marcadores. ¿Proceder? S/n:> " 183 | 184 | msgid "Removing current file from bookmarks. Proceed? Y/n:> " 185 | msgstr "Quitando el archivo actual desde los marcadores. ¿Proceder? S/n:> " 186 | 187 | msgid "You cannot remove xdg defined user dirs." 188 | msgstr "Usted no puede remover los directorios xdg definidos del usuario." 189 | 190 | msgid "Could not open bookmarks file." 191 | msgstr "No se pudo abrir el archivo de marcadores." 192 | 193 | msgid "Added to bookmarks!" 194 | msgstr "Agregado a los marcadores!" 195 | 196 | msgid "Removed from bookmarks!" 197 | msgstr "Removido de los marcadores!" 198 | 199 | msgid "No bookmarks found." 200 | msgstr "Ningún marcador encontrado." 201 | 202 | msgid "Bookmark was deleted as it was no more existent." 203 | msgstr "El marcador actual se ha eliminado debido a que ya no existe." 204 | 205 | msgid "It seems current bookmark no longer exists. Remove it? Y/n:> " 206 | msgstr "Parece que el marcador actual ya no existe. ¿Quitarlo? S/n:> " 207 | 208 | msgid "Bookmark already present. Would you like to remove it? Y/n:> " 209 | msgstr "El marcador ya existe. ¿Desea quitarlo? S/n:> " 210 | 211 | msgid "User bookmarks have been cleared." 212 | msgstr "Los marcadores del usuario se han limpiado." 213 | 214 | msgid "There are no selected files." 215 | msgstr "No hay ningún archivo seleccionado." 216 | 217 | msgid "File selected." 218 | msgstr "Archivo seleccionado." 219 | 220 | msgid "File unselected." 221 | msgstr "Archivo deseleccionado." 222 | 223 | msgid "File unselected. Selected list empty." 224 | msgstr "Archivo deseleccionado. Lista de selección limpia." 225 | 226 | msgid "Every file in this directory has been selected." 227 | msgstr "Todos los archivos del directorio fueron seleccionados." 228 | 229 | msgid "Every file in this directory has been unselected." 230 | msgstr "Todos los archivos del directorio fueron deseleccionados." 231 | 232 | msgid "Every file in this directory has been unselected. Selected list empty." 233 | msgstr "Todos los archivos del directorio fueron deseleccionados. Lista de selección limpia." 234 | 235 | msgid "List of selected files has been cleared." 236 | msgstr "La lista de archivos seleccionados fue limpiada." 237 | 238 | msgid "Are you serious? y/N:> " 239 | msgstr "¿Está seguro? s/N:> " 240 | 241 | msgid "There's already a search in progress. Wait for it." 242 | msgstr "Actualmente existe una búsqueda en curso. Espere a que termine." 243 | 244 | msgid "Insert filename to be found, at least 5 chars, max 20 chars.:> " 245 | msgstr "Ingrese el nombre del archivo a buscar, al menos 5 letras, máximo 20 letras:> " 246 | 247 | msgid "Do you want to search in archives too? y/N:> " 248 | msgstr "¿Desea buscar en los archivos también? s/N:> " 249 | 250 | msgid "Do you want a lazy search (less precise but faster)? y/N:>" 251 | msgstr "¿Desea una búsqueda perezosa (menos precisa pero rápida)? s/N:>" 252 | 253 | msgid "At least 5 chars..." 254 | msgstr "Al menos 5 letras..." 255 | 256 | msgid "Too many files found; try with a larger string." 257 | msgstr "Se encontraron demasiados archivos; trate con una cadena mas larga." 258 | 259 | msgid "No files found." 260 | msgstr "Ningún archivo encontrado." 261 | 262 | msgid "Searching..." 263 | msgstr "Buscando..." 264 | 265 | msgid "Search finished. Press f anytime from normal mode to view the results." 266 | msgstr "Búsqueda finalizada. Presione f en cualquier momento desde el modo normal para ver los resultados." 267 | 268 | msgid "Do you really want to print this file? Y/n:> " 269 | msgstr "¿Realmente desea imprimir éste archivo? S/n:> " 270 | 271 | msgid "Print job added." 272 | msgstr "Trabajo de impresión agregado." 273 | 274 | msgid "No printers available." 275 | msgstr "Ninguna impresora disponible." 276 | 277 | msgid "Insert new file name (defaults to first entry name):> " 278 | msgstr "Ingrese el nuevo nombre del archivo (el nombre por defecto es el primer elemento):> " 279 | 280 | msgid "Insert new name:> " 281 | msgstr "Ingrese el nuevo nombre:> " 282 | 283 | msgid "Do you really want to extract this archive? Y/n:> " 284 | msgstr "¿Realmente quiere extraer éste archivo? S/n:> " 285 | 286 | msgid "Current archive is encrypted. Enter a pwd:> " 287 | msgstr "El archivo actual está encriptado. Ingrese la clave:> " 288 | 289 | msgid "Cutting..." 290 | msgstr "Cortando..." 291 | 292 | msgid "Pasting..." 293 | msgstr "Pegando..." 294 | 295 | msgid "Removing..." 296 | msgstr "Eliminando..." 297 | 298 | msgid "Archiving..." 299 | msgstr "Archivando..." 300 | 301 | msgid "Extracting..." 302 | msgstr "Extrayendo..." 303 | 304 | msgid "Every file has been cut." 305 | msgstr "Todos los archivo se han cortado." 306 | 307 | msgid "Every file has been pasted." 308 | msgstr "Todos los archivo se han pegado." 309 | 310 | msgid "File/dir removed." 311 | msgstr "Archivo/directorio eliminado." 312 | 313 | msgid "Archive is ready." 314 | msgstr "El archivo está listo." 315 | 316 | msgid "Succesfully extracted." 317 | msgstr "Extraído satisfactoriamente." 318 | 319 | msgid "Could not move" 320 | msgstr "No se pudo mover" 321 | 322 | msgid "Could not paste." 323 | msgstr "No se pudo pegar." 324 | 325 | msgid "Could not remove every file." 326 | msgstr "No se pudieron eliminar todos los archivos." 327 | 328 | msgid "Could not archive." 329 | msgstr "No se pudo crear el archivo." 330 | 331 | msgid "Could not extract every file." 332 | msgstr "No se pudieron extraer todos los archivos." 333 | 334 | msgid "File created." 335 | msgstr "Archivo creado." 336 | 337 | msgid "Dir created." 338 | msgstr "Directorio creado." 339 | 340 | msgid "File renamed." 341 | msgstr "Archivo renombrado." 342 | 343 | msgid "There are selected files." 344 | msgstr "Hay archivos seleccionados." 345 | 346 | msgid "There's already an active job. This job will be queued." 347 | msgstr "Existe actualmente un trabajo activo. Se pondrá en cola este trabajo." 348 | 349 | msgid "Queued jobs still running. Waiting..." 350 | msgstr "Trabajos en cola aún en marcha. Esperando..." 351 | 352 | msgid "Do you really want to install this package? y/N:> " 353 | msgstr "¿Realmente desea instalar éste paquete? s/N:> " 354 | 355 | msgid "Waiting for package installation to finish..." 356 | msgstr "Esperando que finalice la instalación del paquete..." 357 | 358 | msgid "Currently there is no check against wrong package arch: it will crash packagekit and ncursesfm." 359 | msgstr "En la actualidad no existe un control para la arquitectura del paquete: se terminará packagekit y ncursesfm." 360 | 361 | msgid "Devices:" 362 | msgstr "Dispositivos:" 363 | 364 | msgid "Bookmarks:" 365 | msgstr "Marcadores:" 366 | 367 | msgid "Selected files:" 368 | msgstr "Archivos seleccionados:" 369 | 370 | # if you've got to switch format modifiers, see here: 371 | # http://www.gnu.org/software/gettext/manual/gettext.html#c_002dformat-Flag 372 | #, c-format 373 | msgid "%d files found searching %s:" 374 | msgstr "Se han encontrado %d buscando %s:" 375 | 376 | msgid "Installed." 377 | msgstr "Instalado." 378 | 379 | msgid "Could not install." 380 | msgstr "No se pudo instalar." 381 | 382 | msgid "No devices found." 383 | msgstr "Ningún dispositivo encontrado." 384 | 385 | # if you've got to switch format modifiers, see here: 386 | # http://www.gnu.org/software/gettext/manual/gettext.html#c_002dformat-Flag 387 | #, c-format 388 | msgid "%s mounted in: %s." 389 | msgstr "%s montado en: %s." 390 | 391 | msgid "%s unmounted." 392 | msgstr "%s desmontado." 393 | 394 | msgid "External tool has mounted %s." 395 | msgstr "Una herramienta externa ha montado %s." 396 | 397 | msgid "External tool has unmounted %s." 398 | msgstr "Una herramienta externa ha desmontado %s." 399 | 400 | msgid "New device connected." 401 | msgstr "Nuevo dispositivo conectado." 402 | 403 | msgid "Device removed." 404 | msgstr "Dispositivo removido." 405 | 406 | msgid "Could not find /proc/mounts." 407 | msgstr "No se pudo encontrar /proc/mounts." 408 | 409 | msgid "On AC" 410 | msgstr "AC conectado" 411 | 412 | msgid "No power supply info available." 413 | msgstr "Ninguna información de energía disponible." 414 | 415 | msgid "Window too small. Enlarge it." 416 | msgstr "La ventana es demasiado pequeña. Agrande la misma." 417 | 418 | msgid "Still polling for devices." 419 | msgstr "Comprobación de dispositivos." 420 | 421 | msgid "Monitor is not active. An error occurred, check log file." 422 | msgstr "El monitor no está activo. Ocurrió un error, vea el archivo de registro." 423 | -------------------------------------------------------------------------------- /msg/fr/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(GLOB _po_files *.po) 2 | gettext_process_po_files(fr ALL INSTALL_DESTINATION ${CMAKE_INSTALL_LOCALEDIR} PO_FILES ${_po_files}) 3 | -------------------------------------------------------------------------------- /msg/fr/fr.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: 3.0\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2017-09-18 17:09+0200\n" 12 | "PO-Revision-Date: 2017-09-18 22:37+0100\n" 13 | "Last-Translator: Alain Aupeix \n" 14 | "Language-Team: ncursesFM\n" 15 | "Language: fr\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | 20 | msgid "y" 21 | msgstr "o" 22 | 23 | msgid "n" 24 | msgstr "" 25 | 26 | msgid "ENTER" 27 | msgstr "ENTRÉE" 28 | 29 | msgid "ESPACE" 30 | msgstr "" 31 | 32 | msgid "ARROW KEYS" 33 | msgstr "" 34 | 35 | msgid "PG_UP/DOWN" 36 | msgstr "" 37 | 38 | msgid "DEL" 39 | msgstr "SUPPR" 40 | 41 | msgid "Press 'L' to trigger helper" 42 | msgstr "Appuyez sur 'L' pour voir/cacher l'aide" 43 | 44 | msgid "Remember: every shortcut in ncursesFM is case insensitive." 45 | msgstr "Rappel: tous les raccourcis de nCursesFM sont sensibles à la casse" 46 | 47 | msgid "surf between folders or to open files." 48 | msgstr "naviguer parmi les dossiers ou ouvrir les fichiers" 49 | 50 | msgid "It will eventually (un)mount your ISO files or install your distro downloaded packages." 51 | msgstr "" 52 | 53 | msgid "enable fast browse mode: it lets you jump between files by just typing their name." 54 | msgstr "" 55 | 56 | msgid "jump straight to first/last file." 57 | msgstr "saute au premier/dernier élément." 58 | 59 | msgid "check files fullname." 60 | msgstr "" 61 | 62 | msgid "trigger the showing of hidden files." 63 | msgstr "voir/cacher les fichiers cachés." 64 | 65 | msgid "see files stats." 66 | msgstr "affiche les statistiques des fichiers" 67 | 68 | msgid "change sorting function: alphabetically (default), by size, by last modified or by type." 69 | msgstr "Trier : alphabétique (par défaut), taille, dernière modification ou type." 70 | 71 | msgid "select files. Once more to remove the file from selected files." 72 | msgstr "sélection/désélection des fichiers" 73 | 74 | msgid "paste/cut." 75 | msgstr "coller/couper" 76 | 77 | msgid "compress." 78 | msgstr "compresser." 79 | 80 | msgid "remove." 81 | msgstr "supprimer." 82 | 83 | msgid "extract." 84 | msgstr "extraire." 85 | 86 | msgid "print." 87 | msgstr "imprimer." 88 | 89 | msgid "switch to bookmarks mode." 90 | msgstr "basculer en mode signet" 91 | 92 | msgid "add/remove current file to bookmarks." 93 | msgstr "ajout/suppression du fichier courant des signets" 94 | 95 | msgid "rename current file/dir." 96 | msgstr "renommer le fichier/dossier courant" 97 | 98 | msgid "create new file/dir." 99 | msgstr "créer un nouveau fichier/dossier" 100 | 101 | msgid "search for a file." 102 | msgstr "chercher un fichier" 103 | 104 | msgid "create second tab." 105 | msgstr "créer un second onglet" 106 | 107 | msgid "close second tab." 108 | msgstr "fermer le second onglet" 109 | 110 | msgid "switch between tabs." 111 | msgstr "changer d'onglet" 112 | 113 | msgid "switch to device mode." 114 | msgstr "basculer en mode périphérique." 115 | 116 | msgid "switch to selected mode." 117 | msgstr "basculer vers le mode sélectionné." 118 | 119 | msgid "quit." 120 | msgstr "Quitter" 121 | 122 | msgid "Just start typing your desired filename, to move right to its position." 123 | msgstr "Taper le nom du fichier cherché pour y accéder." 124 | 125 | msgid "leave fast browse mode." 126 | msgstr "quitter le mode de navigation rapide." 127 | 128 | msgid "remove selected file from bookmarks." 129 | msgstr "supprimer le fichier sélectionné des signets." 130 | 131 | msgid "remove all user bookmarks." 132 | msgstr "supprimer tous les signets utilisateurs" 133 | 134 | msgid "move to the folder/file selected." 135 | msgstr "passer au dossier/fichier sélectionné." 136 | 137 | msgid "leave bookmarks mode." 138 | msgstr "quitter le mode signet." 139 | 140 | msgid "leave search mode." 141 | msgstr "quitter le mode recherche." 142 | 143 | msgid "leave selected mode." 144 | msgstr "quitter le mode sélectionné" 145 | 146 | msgid "(un)mount current device." 147 | msgstr "(dé)monter le périphérique courant" 148 | 149 | msgid "move to current device mountpoint, mounting it if necessary." 150 | msgstr "passer au point de montage actuel de l'appareil, le monter si nécessaire." 151 | 152 | msgid "leave device mode." 153 | msgstr "quitter le mode périphérique." 154 | 155 | msgid "remove current file selection." 156 | msgstr "supprimer la sélection de fichiers en cours" 157 | 158 | msgid "remove all selected files." 159 | msgstr "supprimer tous les fichiers sélectionnés." 160 | 161 | msgid "You have to specify a valid editor in config file." 162 | msgstr "Vous devez spécifier un éditeur valide dans le fichier de configuration" 163 | 164 | msgid "A generic error occurred. Check log." 165 | msgstr "Une erreur générique est survenue. Vérifier le fichier journal" 166 | 167 | msgid "Files will be sorted alphabetically now." 168 | msgstr "Les fichiers seront triés par ordre alphabétique" 169 | 170 | msgid "Files will be sorted by size now." 171 | msgstr "Les fichiers seront triés par ordre de taille" 172 | 173 | msgid "Files will be sorted by last access now." 174 | msgstr "Les fichiers seront triés par ordre de dernier accès" 175 | 176 | msgid "Files will be sorted by type now." 177 | msgstr "Les fichiers seront triés par ordre de type" 178 | 179 | msgid "Adding current file to bookmarks. Proceed? Y/n:> " 180 | msgstr "Ajout du fichier courant aux signets. Procéder? O/n:> " 181 | 182 | msgid "Removing current file from bookmarks. Proceed? Y/n:> " 183 | msgstr "Suppression du fichier courant des signets. Procéder? O/n:> " 184 | 185 | msgid "You cannot remove xdg defined user dirs." 186 | msgstr "Vous ne pouvez pas supprimer le fichier de définition des dossiers xdg" 187 | 188 | msgid "Could not open bookmarks file." 189 | msgstr "Ouverture du fichier des signets impossible." 190 | 191 | msgid "Added to bookmarks!" 192 | msgstr "Ajouté aux signets!" 193 | 194 | msgid "Removed from bookmarks!" 195 | msgstr "Supprimé des signets!" 196 | 197 | msgid "No bookmarks found." 198 | msgstr "Aucun signet trouvé." 199 | 200 | msgid "Bookmark was deleted as it was no more existent." 201 | msgstr "Signet supprimé, car le fichier n'existe plus." 202 | 203 | msgid "It seems current bookmark no longer exists. Remove it? Y/n:> " 204 | msgstr "Il semble que le signet courant n'existe plus. Le supprimer? O/n:> " 205 | 206 | msgid "Bookmark already present. Would you like to remove it? Y/n:> " 207 | msgstr "Signet déjà existant. Voulez-le supprimer? O/n:> " 208 | 209 | msgid "User bookmarks have been cleared." 210 | msgstr "Les signets utilisateur ont été supprimés" 211 | 212 | msgid "There are no selected files." 213 | msgstr "Aucun fichier sélectionné" 214 | 215 | msgid "File selected." 216 | msgstr "Fichier sélectionné." 217 | 218 | msgid "File unselected." 219 | msgstr "Fichier désélectionné." 220 | 221 | msgid "File unselected. Selected list empty." 222 | msgstr "Fichier désélectionné. Liste de sélection vide." 223 | 224 | msgid "Every file in this directory has been selected." 225 | msgstr "Tous les fichiers de ce répertoire ont été sélectionnés." 226 | 227 | msgid "Every file in this directory has been unselected." 228 | msgstr "Tous les fichiers de ce répertoire ont été désélectionnés." 229 | 230 | msgid "Every file in this directory has been unselected. Selected list empty." 231 | msgstr "Tous les fichiers de ce répertoire ont été sélectionnés. Liste de sélection vide." 232 | 233 | msgid "List of selected files has been cleared." 234 | msgstr "La liste de sélection a été vidée" 235 | 236 | msgid "Are you serious? y/N:> " 237 | msgstr "Êtes-vous sûr? o/N:> " 238 | 239 | msgid "There's already a search in progress. Wait for it." 240 | msgstr "Recherche en cours actuellement. Veuillez patienter." 241 | 242 | msgid "Insert filename to be found, at least 5 chars, max 20 chars.:> " 243 | msgstr "Saisissez le nom du fichier à chercher, de 5 à 20 caractères:> " 244 | 245 | msgid "Do you want to search in archives too? y/N:> " 246 | msgstr "Rechercher également dans les archives? o/N:> " 247 | 248 | msgid "Do you want a lazy search (less precise but faster)? y/N:>" 249 | msgstr "Voulez vous d'une recherche paresseuse (moins précise, mais plus rapide)? o/N:>" 250 | 251 | msgid "At least 5 chars..." 252 | msgstr "Au moins 5 caractères..." 253 | 254 | msgid "Too many files found; try with a larger string." 255 | msgstr "Trop de fichiers trouvés; saisissez une chaîne plus longue." 256 | 257 | msgid "No files found." 258 | msgstr "Aucun fichier trouvé." 259 | 260 | msgid "Searching..." 261 | msgstr "Recherche en cours..." 262 | 263 | msgid "Search finished. Press f anytime from normal mode to view the results." 264 | msgstr "Recherche terminée. Tapez f en mode normal pour afficher les résultats." 265 | 266 | msgid "Do you really want to print this file? Y/n:> " 267 | msgstr "Voulez-vous imprimer ce fichier? O/n:> " 268 | 269 | msgid "Print job added." 270 | msgstr "Travail d'impression ajouté." 271 | 272 | msgid "No printers available." 273 | msgstr "Aucune imprimante disponible." 274 | 275 | msgid "Insert new file name (defaults to first entry name):> " 276 | msgstr "Saisissez le nouveau nom de fichier (par défaut, le nom actuel):> " 277 | 278 | msgid "Insert new name:> " 279 | msgstr "Saisissez le nouveau nom:> " 280 | 281 | msgid "Do you really want to extract this archive? Y/n:> " 282 | msgstr "Extraire les fichiers de cette archive? O/n:> " 283 | 284 | msgid "Current archive is encrypted. Enter a pwd:> " 285 | msgstr "L'archive courante est cryptée. Donnez le mot de passe:> " 286 | 287 | msgid "Cutting..." 288 | msgstr "Découpe..." 289 | 290 | msgid "Pasting..." 291 | msgstr "Collage..." 292 | 293 | msgid "Removing..." 294 | msgstr "Suppression..." 295 | 296 | msgid "Archiving..." 297 | msgstr "Création de l'archive" 298 | 299 | msgid "Extracting..." 300 | msgstr "Extraction" 301 | 302 | msgid "Every file has been cut." 303 | msgstr "Tous les fichiers ont été coupés." 304 | 305 | msgid "Every file has been pasted." 306 | msgstr "Tous les fichiers ont été collés." 307 | 308 | msgid "File/dir removed." 309 | msgstr "Fichier/dossier supprimé." 310 | 311 | msgid "Archive is ready." 312 | msgstr "L'archive est prête." 313 | 314 | msgid "Succesfully extracted." 315 | msgstr "Extraction réussie." 316 | 317 | msgid "Could not move" 318 | msgstr "Déplacement impossible" 319 | 320 | msgid "Could not paste." 321 | msgstr "Collage impossible." 322 | 323 | msgid "Could not remove every file." 324 | msgstr "Impossible de supprimer tous les fichiers." 325 | 326 | msgid "Could not archive." 327 | msgstr "Création impossible de l'archive." 328 | 329 | msgid "Could not extract every file." 330 | msgstr "Extraction de tous les fichiers impossible." 331 | 332 | msgid "File created." 333 | msgstr "Fichier créé." 334 | 335 | msgid "Dir created." 336 | msgstr "Dossier créé." 337 | 338 | msgid "File renamed." 339 | msgstr "Fichier renommé." 340 | 341 | msgid "There are selected files." 342 | msgstr "Il y a des fichiers sélectionnés." 343 | 344 | msgid "There's already an active job. This job will be queued." 345 | msgstr "Il y a déjà quelque chose en cours. Ce travail sera mis dans la queue." 346 | 347 | msgid "Queued jobs still running. Waiting..." 348 | msgstr "Queue des travaux en cours de traitement. Veuillez patienter..." 349 | 350 | msgid "Do you really want to install this package? y/N:> " 351 | msgstr "Voulez-vous réellement installer ce paquet? o/N:> " 352 | 353 | msgid "Waiting for package installation to finish..." 354 | msgstr "En attente de la fin de l'installation de ce paquet..." 355 | 356 | msgid "Currently there is no check against wrong package arch: it will crash packagekit and ncursesfm." 357 | msgstr "" 358 | 359 | msgid "Devices:" 360 | msgstr "Périphériques:" 361 | 362 | msgid "Bookmarks:" 363 | msgstr "Signets:" 364 | 365 | msgid "Selected files:" 366 | msgstr "Fichiers sélectionnés:" 367 | 368 | # if you've got to switch format modifiers, see here: 369 | # http://www.gnu.org/software/gettext/manual/gettext.html#c_002dformat-Flag 370 | #, c-format 371 | msgid "%d files found searching %s:" 372 | msgstr "%d fichiers trouvés en cherchant %s:" 373 | 374 | msgid "Installed." 375 | msgstr "Installé." 376 | 377 | msgid "Could not install." 378 | msgstr "Installation impossible." 379 | 380 | msgid "No devices found." 381 | msgstr "Aucun périphérique trouvé." 382 | 383 | # if you've got to switch format modifiers, see here: 384 | # http://www.gnu.org/software/gettext/manual/gettext.html#c_002dformat-Flag 385 | #, c-format 386 | msgid "%s mounted in: %s." 387 | msgstr "%s monté dans: %s" 388 | 389 | msgid "%s unmounted." 390 | msgstr "%s démonté." 391 | 392 | msgid "External tool has mounted %s." 393 | msgstr "Un outil externe a monté %s." 394 | 395 | msgid "External tool has unmounted %s." 396 | msgstr "Un outil externe a démonté %s." 397 | 398 | msgid "New device connected." 399 | msgstr "Nouveau périphérique connecté." 400 | 401 | msgid "Device removed." 402 | msgstr "Périphérique enlevé." 403 | 404 | msgid "Could not find /proc/mounts." 405 | msgstr "Impossible de trouver /proc/mounts." 406 | 407 | msgid "On AC" 408 | msgstr "Au démarrage" 409 | 410 | msgid "No power supply info available." 411 | msgstr "Aucune info sur l'alimentation disponible." 412 | 413 | msgid "Window too small. Enlarge it." 414 | msgstr "Fenêtre trop petite. Agrandissez-la." 415 | 416 | msgid "Still polling for devices." 417 | msgstr "Recherche des périphériques en cours." 418 | 419 | msgid "Monitor is not active. An error occurred, check log file." 420 | msgstr "Le moniteur n'est pas actif. Une erreur est survenue, vérifiez le fichier journal." 421 | -------------------------------------------------------------------------------- /msg/it/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(GLOB _po_files *.po) 2 | gettext_process_po_files(it ALL INSTALL_DESTINATION ${CMAKE_INSTALL_LOCALEDIR} PO_FILES ${_po_files}) 3 | -------------------------------------------------------------------------------- /msg/it/it.po: -------------------------------------------------------------------------------- 1 | # ncursesFM italian translated strings. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # Federico Di Pierro , 2016 5 | # 6 | #: ncursesFM 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: 3.0\n" 10 | "Report-Msgid-Bugs-To: nierro92@gmail.com\n" 11 | "POT-Creation-Date: 2016-05-28 17:09+0200\n" 12 | "PO-Revision-Date: 2017-09-18 22:37+0100\n" 13 | "Last-Translator: Federico Di Pierro \n" 14 | "Language-Team: ncursesFM\n" 15 | "Language: it\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | 20 | msgid "y" 21 | msgstr "s" 22 | 23 | msgid "n" 24 | msgstr "n" 25 | 26 | msgid "ENTER" 27 | msgstr "INVIO" 28 | 29 | msgid "SPACE" 30 | msgstr "SPAZIO" 31 | 32 | msgid "ARROW KEYS" 33 | msgstr "FRECCE" 34 | 35 | msgid "PG_UP/DOWN" 36 | msgstr "PG_UP/DOWN" 37 | 38 | msgid "DEL" 39 | msgstr "CANC" 40 | 41 | msgid "Press 'L' to trigger helper" 42 | msgstr "Premi 'L' per mostrare/nascondere l'aiuto" 43 | 44 | msgid "Remember: every shortcut in ncursesFM is case insensitive." 45 | msgstr "Ricorda: ogni scorciatoia in ncursesFM è case insensitive." 46 | 47 | msgid "surf between folders or to open files." 48 | msgstr "naviga tra le cartelle e apre i file." 49 | 50 | msgid "It will eventually (un)mount your ISO files or install your distro downloaded packages." 51 | msgstr "Eventualmente (s)monterà i tuoi file ISO o installerà i pacchetti scaricati." 52 | 53 | msgid "enable fast browse mode: it lets you jump between files by just typing their name." 54 | msgstr "abilita la fast browse mode: ti permette di muoverti tra i file scrivendo il loro nome." 55 | 56 | msgid "jump straight to first/last file." 57 | msgstr "vai direttamente al primo/ultimo file." 58 | 59 | msgid "check files fullname." 60 | msgstr "controlla il nome completo dei file." 61 | 62 | msgid "trigger the showing of hidden files." 63 | msgstr "mostra/nascondi file nascosti." 64 | 65 | msgid "see files stats." 66 | msgstr "controlla stat dei file." 67 | 68 | msgid "change sorting function: alphabetically (default), by size, by last modified or by type." 69 | msgstr "cambia la funzione di ordinamento: alfabeticamente (default), per grandezza, per ultimo accesso, o per tipo." 70 | 71 | msgid "select files. Once more to remove the file from selected files." 72 | msgstr "seleziona file. Un'altra volta per rimuovere il file dalla lista dei selezionati." 73 | 74 | 75 | msgid "paste/cut." 76 | msgstr "incolla/taglia." 77 | 78 | msgid "compress." 79 | msgstr "comprimi." 80 | 81 | msgid "remove." 82 | msgstr "rimuovi." 83 | 84 | msgid "extract." 85 | msgstr "estrai." 86 | 87 | msgid "print." 88 | msgstr "stampa." 89 | 90 | msgid "switch to bookmarks mode." 91 | msgstr "accedi modalità segnalibri." 92 | 93 | msgid "add/remove current file to bookmarks." 94 | msgstr "aggiungi/rimuovi file corrente dai segnalibri." 95 | 96 | msgid "rename current file/dir." 97 | msgstr "rinomina il file/cartella corrente." 98 | 99 | msgid "create new file/dir." 100 | msgstr "crea un nuovo file/nuova cartella." 101 | 102 | msgid "search for a file." 103 | msgstr "cerca." 104 | 105 | msgid "create second tab." 106 | msgstr "crea la seconda tab." 107 | 108 | msgid "close second tab." 109 | msgstr "chiudi la seconda tab." 110 | 111 | msgid "switch between tabs." 112 | msgstr "muoviti tra le tab." 113 | 114 | msgid "switch to device mode." 115 | msgstr "accedi modalità device." 116 | 117 | msgid "switch to selected mode." 118 | msgstr "accedi modalità selezionati." 119 | 120 | msgid "quit." 121 | msgstr "esci." 122 | 123 | msgid "Just start typing your desired filename, to move right to its position." 124 | msgstr "Inizia a scrivere il nome del file desiderato, per muoverti alla sua posizione." 125 | 126 | msgid "leave fast browse mode." 127 | msgstr "lascia la modalità fast browse." 128 | 129 | msgid "remove selected file from bookmarks." 130 | msgstr "rimuovi il file selezionato dai segnalibri." 131 | 132 | msgid "remove all user bookmarks." 133 | msgstr "rimuove tutti i segnalibri dell'utente." 134 | 135 | msgid "move to the folder/file selected." 136 | msgstr "muoviti alla cartella/file selezionato." 137 | 138 | msgid "leave bookmarks mode." 139 | msgstr "lascia la modalità segnalibri." 140 | 141 | msgid "leave search mode." 142 | msgstr "lascia la modalità ricerca." 143 | 144 | msgid "leave selected mode." 145 | msgstr "lascia la lista selezionati." 146 | 147 | msgid "(un)mount current device." 148 | msgstr "(s)monta il device corrente." 149 | 150 | msgid "move to current device mountpoint, mounting it if necessary." 151 | msgstr "muoviti al punto di mount del device corrente, montandolo se necessario." 152 | 153 | msgid "leave device mode." 154 | msgstr "lascia la modalità device." 155 | 156 | msgid "remove current file selection." 157 | msgstr "rimuove il file corrente dai selezionati." 158 | 159 | msgid "remove all selected files." 160 | msgstr "azzera i selezionati." 161 | 162 | msgid "You have to specify a valid editor in config file." 163 | msgstr "Devi specificare un editor valido nel file di configurazione." 164 | 165 | msgid "A generic error occurred. Check log." 166 | msgstr "È occorso un errore generico. Controlla il file di log." 167 | 168 | msgid "Files will be sorted alphabetically now." 169 | msgstr "File ordinati alfabeticamente." 170 | 171 | msgid "Files will be sorted by size now." 172 | msgstr "File ordinati per dimensione." 173 | 174 | msgid "Files will be sorted by last access now." 175 | msgstr "File ordinati per ultimo accesso." 176 | 177 | msgid "Files will be sorted by type now." 178 | msgstr "File ordinati per tipo." 179 | 180 | msgid "Adding current file to bookmarks. Proceed? Y/n:> " 181 | msgstr "Aggiungere il file corrente ai segnalibri? S/n:> " 182 | 183 | msgid "Removing current file from bookmarks. Proceed? Y/n:> " 184 | msgstr "Rimuovere il file corrente dai segnalibri? S/n:> " 185 | 186 | msgid "You cannot remove xdg defined user dirs." 187 | msgstr "Non puoi rimuovere le cartelle utente definite da xdg." 188 | 189 | msgid "Could not open bookmarks file." 190 | msgstr "Non sono riuscito ad aprire il file dei segnalibri." 191 | 192 | msgid "Added to bookmarks!" 193 | msgstr "Aggiunto ai segnalibri!" 194 | 195 | msgid "Removed from bookmarks!" 196 | msgstr "Rimosso dai segnalibri!" 197 | 198 | msgid "No bookmarks found." 199 | msgstr "Nessun segnalibro trovato." 200 | 201 | msgid "Bookmark was deleted as it was no more existent." 202 | msgstr "Il segnalibro corrente è stato rimosso perché non esisteva più." 203 | 204 | msgid "It seems current bookmark no longer exists. Remove it? Y/n:> " 205 | msgstr "Sembra che il segnalibro corrente non esista più. Rimuoverlo? S/n:> " 206 | 207 | msgid "Bookmark already present. Would you like to remove it? Y/n:> " 208 | msgstr "Segnalibro già presente. Desideri rimuoverlo? S/n:> " 209 | 210 | msgid "User bookmarks have been cleared." 211 | msgstr "Segnalibri dell'utente azzerati." 212 | 213 | msgid "There are no selected files." 214 | msgstr "Nessun file selezionato." 215 | 216 | msgid "File selected." 217 | msgstr "File selezionato." 218 | 219 | msgid "File unselected." 220 | msgstr "File deselezionato." 221 | 222 | msgid "File unselected. Selected list empty." 223 | msgstr "File deselezionato. Non ci sono più file selezionati." 224 | 225 | msgid "Every file in this directory has been selected." 226 | msgstr "Tutti i file nella cartella corrente sono stati selezionati." 227 | 228 | msgid "Every file in this directory has been unselected." 229 | msgstr "Tutti i file nella cartella corrente sono stati deselezionati." 230 | 231 | msgid "Every file in this directory has been unselected. Selected list empty." 232 | msgstr "Tutti i file nella cartella corrente sono stati deselezionati. Non ci sono più file selezionati." 233 | 234 | msgid "List of selected files has been cleared." 235 | msgstr "La lista dei file selezionati è stata azzerata." 236 | 237 | msgid "Are you serious? y/N:> " 238 | msgstr "Sei sicuro? s/N:> " 239 | 240 | msgid "There's already a search in progress. Wait for it." 241 | msgstr "C'è già una ricerca attiva. Attenti che finisca." 242 | 243 | msgid "Insert filename to be found, at least 5 chars, max 20 chars.:> " 244 | msgstr "Inserici il nome del file da cercare. Minimo 5 caratteri, massimo 20.:> " 245 | 246 | msgid "Do you want to search in archives too? y/N:> " 247 | msgstr "Desideri ricercare anche negli archivi? s/N> " 248 | 249 | msgid "Do you want a lazy search (less precise but faster)? y/N:>" 250 | msgstr "Desideri una ricerca pigra (meno precisa ma più veloce)? s/N:> " 251 | 252 | msgid "At least 5 chars..." 253 | msgstr "Almeno 5 caratteri..." 254 | 255 | msgid "Too many files found; try with a larger string." 256 | msgstr "Trovati troppi file; riprova con una stringa più lunga." 257 | 258 | msgid "No files found." 259 | msgstr "Nessun file trovato." 260 | 261 | msgid "Searching..." 262 | msgstr "Ricerca..." 263 | 264 | msgid "Search finished. Press f anytime from normal mode to view the results." 265 | msgstr "Ricerca finita. Premi f in modalità normale quando desideri vedere i risultati." 266 | 267 | msgid "Do you really want to print this file? Y/n:> " 268 | msgstr "Desideri davvero stampare questo file? S/n:> " 269 | 270 | msgid "Print job added." 271 | msgstr "Aggiunta una stampa." 272 | 273 | msgid "No printers available." 274 | msgstr "Nessuna stampante disponibile." 275 | 276 | msgid "Insert new file name (defaults to first entry name):> " 277 | msgstr "Inserisci il nuovo nome del file (di default il nome del primo elemento):> " 278 | 279 | msgid "Insert new name:> " 280 | msgstr "Inserisci il nuovo nome:> " 281 | 282 | msgid "Do you really want to extract this archive? Y/n:> " 283 | msgstr "Desideri veramente estrarre l'archivio? S/n:> " 284 | 285 | msgid "Current archive is encrypted. Enter a pwd:> " 286 | msgstr "L'archivio corrente è criptato. Inserisci una password:> " 287 | 288 | msgid "Cutting..." 289 | msgstr "Sposto..." 290 | 291 | msgid "Pasting..." 292 | msgstr "Incollo..." 293 | 294 | msgid "Removing..." 295 | msgstr "Rimuovo..." 296 | 297 | msgid "Archiving..." 298 | msgstr "Archivio..." 299 | 300 | msgid "Extracting..." 301 | msgstr "Estraggo..." 302 | 303 | msgid "Every file has been cut." 304 | msgstr "Tutti i file sono stati spostati." 305 | 306 | msgid "Every file has been pasted." 307 | msgstr "Tutti i file sono stati copiati." 308 | 309 | msgid "File/dir removed." 310 | msgstr "File/cartella rimosso." 311 | 312 | msgid "Archive is ready." 313 | msgstr "L'archivio è pronto." 314 | 315 | msgid "Succesfully extracted." 316 | msgstr "Estratto con successo." 317 | 318 | msgid "Could not move" 319 | msgstr "Impossibile spostare." 320 | 321 | msgid "Could not paste." 322 | msgstr "Impossibile incollare." 323 | 324 | msgid "Could not remove every file." 325 | msgstr "Impossibile rimuovere tutti i file." 326 | 327 | msgid "Could not archive." 328 | msgstr "Impossibile creare l'archivio." 329 | 330 | msgid "Could not extract every file." 331 | msgstr "Impossibile estrarre tutti i file." 332 | 333 | msgid "File created." 334 | msgstr "File creato." 335 | 336 | msgid "Dir created." 337 | msgstr "Cartella creata." 338 | 339 | msgid "File renamed." 340 | msgstr "File rinominato." 341 | 342 | msgid "There are selected files." 343 | msgstr "Ci sono dei file selezionati." 344 | 345 | msgid "There's already an active job. This job will be queued." 346 | msgstr "C'è già un lavoro attivo. Lavoro aggiunto in coda." 347 | 348 | msgid "Queued jobs still running. Waiting..." 349 | msgstr "Coda di lavori ancora in esecuzione. Pazienta..." 350 | 351 | msgid "Do you really want to install this package? y/N:> " 352 | msgstr "Desideri davvero installare questo pacchetto? s/N:> " 353 | 354 | msgid "Waiting for package installation to finish..." 355 | msgstr "Aspettando che l'installazione del pacchetto finisca..." 356 | 357 | msgid "Currently there is no check against wrong package arch: it will crash packagekit and ncursesfm." 358 | msgstr "Al momento non vi è alcun controllo per l'architettura del pacchetto: crasherà packagekit e ncursesfm." 359 | 360 | msgid "Devices:" 361 | msgstr "Dispositivi:" 362 | 363 | msgid "Bookmarks:" 364 | msgstr "Segnalibri:" 365 | 366 | msgid "Selected files:" 367 | msgstr "File selezionati:" 368 | 369 | # if you've got to switch format modifiers, see here: 370 | # http://www.gnu.org/software/gettext/manual/gettext.html#c_002dformat-Flag 371 | #, c-format 372 | msgid "%d files found searching %s:" 373 | msgstr "Trovati %d file cercando %s:" 374 | 375 | msgid "Installed." 376 | msgstr "Installato." 377 | 378 | msgid "Could not install." 379 | msgstr "Impossibile installare." 380 | 381 | msgid "No devices found." 382 | msgstr "Nessun dispositivo trovato." 383 | 384 | # if you've got to switch format modifiers, see here: 385 | # http://www.gnu.org/software/gettext/manual/gettext.html#c_002dformat-Flag 386 | #, c-format 387 | msgid "%s mounted in: %s." 388 | msgstr "%s montato in: %s." 389 | 390 | msgid "%s unmounted." 391 | msgstr "%s smontato." 392 | 393 | msgid "External tool has mounted %s." 394 | msgstr "Un tool esterno ha montato %s." 395 | 396 | msgid "External tool has unmounted %s." 397 | msgstr "Un tool esterno ha smontato %s." 398 | 399 | msgid "New device connected." 400 | msgstr "Nuovo dispositivo connesso." 401 | 402 | msgid "Device removed." 403 | msgstr "Dispositivo rimosso." 404 | 405 | msgid "Could not find /proc/mounts." 406 | msgstr "/proc/mounts non presente." 407 | 408 | msgid "On AC" 409 | msgstr "Collegato" 410 | 411 | msgid "No power supply info available." 412 | msgstr "Nessuna informazione energetica disponibile." 413 | 414 | msgid "Window too small. Enlarge it." 415 | msgstr "Finestra troppo piccola. Allargala." 416 | 417 | msgid "Still polling for devices." 418 | msgstr "Controllando i dispositivi." 419 | 420 | msgid "Monitor is not active. An error occurred, check log file." 421 | msgstr "Il monitor non è attivo. È occorso un errore, controlla il file di log." 422 | -------------------------------------------------------------------------------- /msg/ncursesFM.pot: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: 3.0\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2016-05-28 17:09+0200\n" 12 | "PO-Revision-Date: 2011-05-27 22:37+0100\n" 13 | "Last-Translator: TRANSLATOR \n" 14 | "Language-Team: ncursesFM\n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | 20 | msgid "y" 21 | msgstr "" 22 | 23 | msgid "n" 24 | msgstr "" 25 | 26 | msgid "ENTER" 27 | msgstr "" 28 | 29 | msgid "SPACE" 30 | msgstr "" 31 | 32 | msgid "ARROW KEYS" 33 | msgstr "" 34 | 35 | msgid "PG_UP/DOWN" 36 | msgstr "" 37 | 38 | msgid "DEL" 39 | msgstr "" 40 | 41 | msgid "Press 'L' to trigger helper" 42 | msgstr "" 43 | 44 | msgid "Remember: every shortcut in ncursesFM is case insensitive." 45 | msgstr "" 46 | 47 | msgid "surf between folders or to open files." 48 | msgstr "" 49 | 50 | msgid "It will eventually (un)mount your ISO files or install your distro downloaded packages." 51 | msgstr "" 52 | 53 | msgid "enable fast browse mode: it lets you jump between files by just typing their name." 54 | msgstr "" 55 | 56 | msgid "jump straight to first/last file." 57 | msgstr "" 58 | 59 | msgid "check files fullname." 60 | msgstr "" 61 | 62 | msgid "trigger the showing of hidden files." 63 | msgstr "" 64 | 65 | msgid "see files stats." 66 | msgstr "" 67 | 68 | msgid "change sorting function: alphabetically (default), by size, by last modified or by type." 69 | msgstr "" 70 | 71 | msgid "select files. Once more to remove the file from selected files." 72 | msgstr "" 73 | 74 | msgid "paste/cut." 75 | msgstr "" 76 | 77 | msgid "compress." 78 | msgstr "" 79 | 80 | msgid "remove." 81 | msgstr "" 82 | 83 | msgid "extract." 84 | msgstr "" 85 | 86 | msgid "print." 87 | msgstr "" 88 | 89 | msgid "switch to bookmarks mode." 90 | msgstr "" 91 | 92 | msgid "add/remove current file to bookmarks." 93 | msgstr "" 94 | 95 | msgid "rename current file/dir." 96 | msgstr "" 97 | 98 | msgid "create new file/dir." 99 | msgstr "" 100 | 101 | msgid "search for a file." 102 | msgstr "" 103 | 104 | msgid "create second tab." 105 | msgstr "" 106 | 107 | msgid "close second tab." 108 | msgstr "" 109 | 110 | msgid "switch between tabs." 111 | msgstr "" 112 | 113 | msgid "switch to device mode." 114 | msgstr "" 115 | 116 | msgid "switch to selected mode." 117 | msgstr "" 118 | 119 | msgid "quit." 120 | msgstr "" 121 | 122 | msgid "Just start typing your desired filename, to move right to its position." 123 | msgstr "" 124 | 125 | msgid "leave fast browse mode." 126 | msgstr "" 127 | 128 | msgid "remove selected file from bookmarks." 129 | msgstr "" 130 | 131 | msgid "remove all user bookmarks." 132 | msgstr "" 133 | 134 | msgid "move to the folder/file selected." 135 | msgstr "" 136 | 137 | msgid "leave bookmarks mode." 138 | msgstr "" 139 | 140 | msgid "leave search mode." 141 | msgstr "" 142 | 143 | msgid "leave selected mode." 144 | msgstr "" 145 | 146 | msgid "(un)mount current device." 147 | msgstr "" 148 | 149 | msgid "move to current device mountpoint, mounting it if necessary." 150 | msgstr "" 151 | 152 | msgid "leave device mode." 153 | msgstr "" 154 | 155 | msgid "remove current file selection." 156 | msgstr "" 157 | 158 | msgid "remove all selected files." 159 | msgstr "" 160 | 161 | msgid "You have to specify a valid editor in config file." 162 | msgstr "" 163 | 164 | msgid "A generic error occurred. Check log." 165 | msgstr "" 166 | 167 | msgid "Files will be sorted alphabetically now." 168 | msgstr "" 169 | 170 | msgid "Files will be sorted by size now." 171 | msgstr "" 172 | 173 | msgid "Files will be sorted by last access now." 174 | msgstr "" 175 | 176 | msgid "Files will be sorted by type now." 177 | msgstr "" 178 | 179 | msgid "Adding current file to bookmarks. Proceed? Y/n:> " 180 | msgstr "" 181 | 182 | msgid "Removing current file from bookmarks. Proceed? Y/n:> " 183 | msgstr "" 184 | 185 | msgid "You cannot remove xdg defined user dirs." 186 | msgstr "" 187 | 188 | msgid "Could not open bookmarks file." 189 | msgstr "" 190 | 191 | msgid "Added to bookmarks!" 192 | msgstr "" 193 | 194 | msgid "Removed from bookmarks!" 195 | msgstr "" 196 | 197 | msgid "No bookmarks found." 198 | msgstr "" 199 | 200 | msgid "Bookmark was deleted as it was no more existent." 201 | msgstr "" 202 | 203 | msgid "It seems current bookmark no longer exists. Remove it? Y/n:> " 204 | msgstr "" 205 | 206 | msgid "Bookmark already present. Would you like to remove it? Y/n:> " 207 | msgstr "" 208 | 209 | msgid "User bookmarks have been cleared." 210 | msgstr "" 211 | 212 | msgid "There are no selected files." 213 | msgstr "" 214 | 215 | msgid "File selected." 216 | msgstr "" 217 | 218 | msgid "File unselected." 219 | msgstr "" 220 | 221 | msgid "File unselected. Selected list empty." 222 | msgstr "" 223 | 224 | msgid "Every file in this directory has been selected." 225 | msgstr "" 226 | 227 | msgid "Every file in this directory has been unselected." 228 | msgstr "" 229 | 230 | msgid "Every file in this directory has been unselected. Selected list empty." 231 | msgstr "" 232 | 233 | msgid "List of selected files has been cleared." 234 | msgstr "" 235 | 236 | msgid "Are you serious? y/N:> " 237 | msgstr "" 238 | 239 | msgid "There's already a search in progress. Wait for it." 240 | msgstr "" 241 | 242 | msgid "Insert filename to be found, at least 5 chars, max 20 chars.:> " 243 | msgstr "" 244 | 245 | msgid "Do you want to search in archives too? y/N:> " 246 | msgstr "" 247 | 248 | msgid "Do you want a lazy search (less precise but faster)? y/N:>" 249 | msgstr "" 250 | 251 | msgid "At least 5 chars..." 252 | msgstr "" 253 | 254 | msgid "Too many files found; try with a larger string." 255 | msgstr "" 256 | 257 | msgid "No files found." 258 | msgstr "" 259 | 260 | msgid "Searching..." 261 | msgstr "" 262 | 263 | msgid "Search finished. Press f anytime from normal mode to view the results." 264 | msgstr "" 265 | 266 | msgid "Do you really want to print this file? Y/n:> " 267 | msgstr "" 268 | 269 | msgid "Print job added." 270 | msgstr "" 271 | 272 | msgid "No printers available." 273 | msgstr "" 274 | 275 | msgid "Insert new file name (defaults to first entry name):> " 276 | msgstr "" 277 | 278 | msgid "Insert new name:> " 279 | msgstr "" 280 | 281 | msgid "Do you really want to extract this archive? Y/n:> " 282 | msgstr "" 283 | 284 | msgid "Current archive is encrypted. Enter a pwd:> " 285 | msgstr "" 286 | 287 | msgid "Cutting..." 288 | msgstr "" 289 | 290 | msgid "Pasting..." 291 | msgstr "" 292 | 293 | msgid "Removing..." 294 | msgstr "" 295 | 296 | msgid "Archiving..." 297 | msgstr "" 298 | 299 | msgid "Extracting..." 300 | msgstr "" 301 | 302 | msgid "Every file has been cut." 303 | msgstr "" 304 | 305 | msgid "Every file has been pasted." 306 | msgstr "" 307 | 308 | msgid "File/dir removed." 309 | msgstr "" 310 | 311 | msgid "Archive is ready." 312 | msgstr "" 313 | 314 | msgid "Succesfully extracted." 315 | msgstr "" 316 | 317 | msgid "Could not move" 318 | msgstr "" 319 | 320 | msgid "Could not paste." 321 | msgstr "" 322 | 323 | msgid "Could not remove every file." 324 | msgstr "" 325 | 326 | msgid "Could not archive." 327 | msgstr "" 328 | 329 | msgid "Could not extract every file." 330 | msgstr "" 331 | 332 | msgid "File created." 333 | msgstr "" 334 | 335 | msgid "Dir created." 336 | msgstr "" 337 | 338 | msgid "File renamed." 339 | msgstr "" 340 | 341 | msgid "There are selected files." 342 | msgstr "" 343 | 344 | msgid "There's already an active job. This job will be queued." 345 | msgstr "" 346 | 347 | msgid "Queued jobs still running. Waiting..." 348 | msgstr "" 349 | 350 | msgid "Do you really want to install this package? y/N:> " 351 | msgstr "" 352 | 353 | msgid "Waiting for package installation to finish..." 354 | msgstr "" 355 | 356 | msgid "Currently there is no check against wrong package arch: it will crash packagekit and ncursesfm." 357 | msgstr "" 358 | 359 | msgid "Devices:" 360 | msgstr "" 361 | 362 | msgid "Bookmarks:" 363 | msgstr "" 364 | 365 | msgid "Selected files:" 366 | msgstr "" 367 | 368 | # if you've got to switch format modifiers, see here: 369 | # http://www.gnu.org/software/gettext/manual/gettext.html#c_002dformat-Flag 370 | #, c-format 371 | msgid "%d files found searching %s:" 372 | msgstr "" 373 | 374 | msgid "Installed." 375 | msgstr "" 376 | 377 | msgid "Could not install." 378 | msgstr "" 379 | 380 | msgid "No devices found." 381 | msgstr "" 382 | 383 | # if you've got to switch format modifiers, see here: 384 | # http://www.gnu.org/software/gettext/manual/gettext.html#c_002dformat-Flag 385 | #, c-format 386 | msgid "%s mounted in: %s." 387 | msgstr "" 388 | 389 | msgid "%s unmounted." 390 | msgstr "" 391 | 392 | msgid "External tool has mounted %s." 393 | msgstr "" 394 | 395 | msgid "External tool has unmounted %s." 396 | msgstr "" 397 | 398 | msgid "New device connected." 399 | msgstr "" 400 | 401 | msgid "Device removed." 402 | msgstr "" 403 | 404 | msgid "Could not find /proc/mounts." 405 | msgstr "" 406 | 407 | msgid "On AC" 408 | msgstr "" 409 | 410 | msgid "No power supply info available." 411 | msgstr "" 412 | 413 | msgid "Window too small. Enlarge it." 414 | msgstr "" 415 | 416 | msgid "Still polling for devices." 417 | msgstr "" 418 | 419 | msgid "Monitor is not active. An error occurred, check log file." 420 | msgstr "" 421 | -------------------------------------------------------------------------------- /ncursesFM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FedeDP/ncursesFM/19b01d334b58137ef0d1fe7520a22d366e648ca3/ncursesFM.png -------------------------------------------------------------------------------- /src/archiver.c: -------------------------------------------------------------------------------- 1 | #include "../inc/archiver.h" 2 | 3 | static void archiver_func(void); 4 | static int recursive_archive(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf); 5 | #if ARCHIVE_VERSION_NUMBER >= 3002000 6 | static const char *passphrase_callback(struct archive *a, void *_client_data); 7 | #endif 8 | static int try_extractor(const char *tmp); 9 | static void extractor_thread(struct archive *a, const char *current_dir); 10 | 11 | static struct archive *archive; 12 | static int distance_from_root; 13 | 14 | /* 15 | * It tries to create a new archive to write inside it, 16 | * it fails if it cannot add the proper filter, or cannot set proper format, or 17 | * if it cannot open thread_h->full_path (ie, the desired pathname of the new archive) 18 | */ 19 | int create_archive(void) { 20 | archive = archive_write_new(); 21 | if ((archive_write_add_filter_gzip(archive) == ARCHIVE_OK) && 22 | (archive_write_set_format_pax_restricted(archive) == ARCHIVE_OK) && 23 | (archive_write_open_filename(archive, thread_h->full_path) == ARCHIVE_OK)) { 24 | archiver_func(); 25 | return 0; 26 | } 27 | ERROR(archive_error_string(archive)); 28 | archive_write_free(archive); 29 | archive = NULL; 30 | return -1; 31 | } 32 | 33 | /* 34 | * For each of the selected files, calculates the distance from root and calls nftw with recursive_archive. 35 | * Example: archiving /home/me/Scripts/ folder -> it contains {/x.sh, /foo/bar}. 36 | * recursive_archive has to create the entry exactly like /desired/path/name.tgz/{x.sh, foo/bar} 37 | * it copies as entry_name the pointer to current path + distance_from_root + 1, in our case: 38 | * path is /home/me/Scripts/x.sh and (path + distance_from_root + 1) points exatcly to x.sh. 39 | * The entry will be written to the new archive, and then data will be copied. 40 | */ 41 | static void archiver_func(void) { 42 | char path[PATH_MAX + 1] = {0}; 43 | 44 | for (int i = 0; i < thread_h->num_selected; i++) { 45 | strncpy(path, thread_h->selected_files[i], PATH_MAX); 46 | distance_from_root = strlen(dirname(path)); 47 | nftw(thread_h->selected_files[i], recursive_archive, 64, FTW_MOUNT | FTW_PHYS); 48 | } 49 | archive_write_free(archive); 50 | archive = NULL; 51 | } 52 | 53 | static int recursive_archive(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) { 54 | char entry_name[PATH_MAX + 1] = {0}; 55 | int fd; 56 | struct archive_entry *entry = archive_entry_new(); 57 | 58 | strncpy(entry_name, path + distance_from_root + 1, PATH_MAX); 59 | archive_entry_set_pathname(entry, entry_name); 60 | archive_entry_copy_stat(entry, sb); 61 | archive_write_header(archive, entry); 62 | archive_entry_free(entry); 63 | fd = open(path, O_RDONLY); 64 | if (fd != -1) { 65 | char buff[BUFF_SIZE] = {0}; 66 | int len; 67 | 68 | len = read(fd, buff, sizeof(buff)); 69 | while (len > 0) { 70 | archive_write_data(archive, buff, len); 71 | len = read(fd, buff, sizeof(buff)); 72 | } 73 | close(fd); 74 | } 75 | return 0; 76 | } 77 | 78 | int extract_file(void) { 79 | int ret = 0; 80 | 81 | for (int i = 0; i < thread_h->num_selected; i++) { 82 | if (is_ext(thread_h->selected_files[i], arch_ext, NUM(arch_ext))) { 83 | ret += try_extractor(thread_h->selected_files[i]); 84 | } else { 85 | ret--; 86 | } 87 | } 88 | return !ret ? 0 : -1; 89 | } 90 | 91 | #if ARCHIVE_VERSION_NUMBER >= 3002000 92 | static const char *passphrase_callback(struct archive *a, void *_client_data) { 93 | uint64_t u = 1; 94 | 95 | if (eventfd_write(archive_cb_fd[0], u) == -1) { 96 | return NULL; 97 | } 98 | if (eventfd_read(archive_cb_fd[1], &u) == -1) { 99 | return NULL; 100 | } 101 | if (quit || passphrase[0] == 27) { 102 | return NULL; 103 | } 104 | return passphrase; 105 | } 106 | #endif 107 | 108 | static int try_extractor(const char *tmp) { 109 | struct archive *a; 110 | 111 | a = archive_read_new(); 112 | archive_read_support_filter_all(a); 113 | archive_read_support_format_all(a); 114 | #if ARCHIVE_VERSION_NUMBER >= 3002000 115 | archive_read_set_passphrase_callback(a, NULL, passphrase_callback); 116 | #endif 117 | if ((a) && (archive_read_open_filename(a, tmp, BUFF_SIZE) == ARCHIVE_OK)) { 118 | char path[PATH_MAX + 1] = {0}; 119 | 120 | strncpy(path, tmp, PATH_MAX); 121 | char *current_dir = dirname(path); 122 | extractor_thread(a, current_dir); 123 | return 0; 124 | } 125 | archive_read_free(a); 126 | return -1; 127 | } 128 | 129 | /* 130 | * calculates current_dir path, then creates the write_disk_archive that 131 | * will read from the selected archives and will write files on disk. 132 | * While there are headers inside the archive being read, it goes on copying data from 133 | * the read archive to the disk. 134 | */ 135 | static void extractor_thread(struct archive *a, const char *current_dir) { 136 | struct archive *ext; 137 | struct archive_entry *entry; 138 | int flags = ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL | ARCHIVE_EXTRACT_FFLAGS; 139 | char buff[BUFF_SIZE], fullpathname[PATH_MAX + 1]; 140 | char name[PATH_MAX + 1] = {0}, tmp_name[PATH_MAX + 1] = {0}; 141 | 142 | ext = archive_write_disk_new(); 143 | archive_write_disk_set_options(ext, flags); 144 | archive_write_disk_set_standard_lookup(ext); 145 | while (archive_read_next_header(a, &entry) != ARCHIVE_EOF) { 146 | strncpy(name, archive_entry_pathname(entry), PATH_MAX); 147 | int num = 0; 148 | /* avoid overwriting a file/dir in path if it has the same name of a file being extracted there */ 149 | if (strchr(name, '/')) { 150 | strncpy(tmp_name, strchr(name, '/'), PATH_MAX); 151 | } else { 152 | tmp_name[0] = '\0'; 153 | } 154 | int len = strlen(name) - strlen(tmp_name); 155 | while (access(name, F_OK) == 0) { 156 | num++; 157 | snprintf(name + len, PATH_MAX - len, "%d%s", num, tmp_name); 158 | } 159 | snprintf(fullpathname, PATH_MAX, "%s/%s", current_dir, name); 160 | archive_entry_set_pathname(entry, fullpathname); 161 | archive_write_header(ext, entry); 162 | len = archive_read_data(a, buff, sizeof(buff)); 163 | while (len > 0) { 164 | archive_write_data(ext, buff, len); 165 | len = archive_read_data(a, buff, sizeof(buff)); 166 | } 167 | } 168 | archive_read_free(a); 169 | archive_write_free(ext); 170 | } 171 | -------------------------------------------------------------------------------- /src/bookmarks.c: -------------------------------------------------------------------------------- 1 | #include "../inc/bookmarks.h" 2 | 3 | static void get_xdg_dirs(void); 4 | static void remove_bookmark(int idx); 5 | 6 | static int num_bookmarks, xdg_bookmarks; 7 | static char home_dir[PATH_MAX + 1]; 8 | static char fullpath[PATH_MAX + 1]; 9 | static char (*bookmarks)[PATH_MAX + 1]; 10 | 11 | void get_bookmarks(void) { 12 | FILE *f; 13 | const char *bookmarks_file = "ncursesFM-bookmarks"; 14 | 15 | strncpy(home_dir, getpwuid(getuid())->pw_dir, PATH_MAX); 16 | 17 | if (getenv("XDG_CONFIG_HOME")) { 18 | snprintf(fullpath, PATH_MAX, "%s/%s", getenv("XDG_CONFIG_HOME"), bookmarks_file); 19 | } else { 20 | snprintf(fullpath, PATH_MAX, "%s/.config/%s", home_dir, bookmarks_file); 21 | } 22 | get_xdg_dirs(); 23 | if ((f = fopen(fullpath, "r"))) { 24 | char str[PATH_MAX + 1] = {0}; 25 | 26 | while (fgets(str, PATH_MAX, f)) { 27 | bookmarks = safe_realloc(++num_bookmarks, bookmarks); 28 | strncpy(bookmarks[num_bookmarks - 1], str, PATH_MAX); 29 | } 30 | fclose(f); 31 | } else { 32 | WARN(bookmarks_file_err); 33 | } 34 | } 35 | 36 | static void get_xdg_dirs(void) { 37 | FILE *f; 38 | char line[1000], file_path[PATH_MAX + 1] = {0}; 39 | 40 | if (getenv("XDG_CONFIG_HOME")) { 41 | snprintf(file_path, PATH_MAX, "%s/.user-dirs.dirs", getenv("XDG_CONFIG_HOME")); 42 | } else { 43 | snprintf(file_path, PATH_MAX, "%s/.config/user-dirs.dirs", home_dir); 44 | } 45 | 46 | if ((f = fopen(file_path, "r"))) { 47 | char str[PATH_MAX + 1] = {0}; 48 | 49 | while (fgets(line, sizeof(line), f)) { 50 | // avoid comments 51 | if (*line == '#') { 52 | continue; 53 | } 54 | strncpy(str, strchr(line, '/') + 1, PATH_MAX); 55 | str[strlen(str) - 2] = '\0'; // -1 for newline - 1 for closing Double quotation mark 56 | bookmarks = safe_realloc(++num_bookmarks, bookmarks); 57 | snprintf(bookmarks[num_bookmarks - 1], PATH_MAX, "%s/%s", home_dir, str); 58 | } 59 | fclose(f); 60 | xdg_bookmarks = num_bookmarks; 61 | } 62 | } 63 | 64 | void add_file_to_bookmarks(const char *str) { 65 | FILE *f; 66 | char c; 67 | 68 | int present = is_present(str, bookmarks, num_bookmarks, -1, 0); 69 | if (present != -1) { 70 | if (config.safe == FULL_SAFE) { 71 | ask_user(_(bookmark_already_present), &c, 1); 72 | if (c == _(no)[0] || c == 27) { 73 | return; 74 | } 75 | } 76 | return remove_bookmark(present); 77 | } 78 | if (config.safe == FULL_SAFE) { 79 | ask_user(_(bookmarks_add_quest), &c, 1); 80 | if (c == _(no)[0] || c == 27) { 81 | return; 82 | } 83 | } 84 | if ((f = fopen(fullpath, "a+"))) { 85 | fprintf(f, "%s\n", str); 86 | fclose(f); 87 | print_info(_(bookmark_added), INFO_LINE); 88 | bookmarks = safe_realloc(++num_bookmarks, bookmarks); 89 | strncpy(bookmarks[num_bookmarks - 1], str, PATH_MAX); 90 | update_special_mode(num_bookmarks, bookmarks, bookmarks_); 91 | } else { 92 | print_info(_(bookmarks_file_err), ERR_LINE); 93 | } 94 | } 95 | 96 | void remove_bookmark_from_file(void) { 97 | char c; 98 | 99 | if (ps[active].curr_pos < xdg_bookmarks) { 100 | print_info(_(bookmarks_xdg_err), ERR_LINE); 101 | } else { 102 | if (config.safe == FULL_SAFE) { 103 | ask_user(_(bookmarks_rm_quest), &c, 1); 104 | if (c == _(no)[0] || c == 27) { 105 | return; 106 | } 107 | } 108 | remove_bookmark(ps[active].curr_pos); 109 | } 110 | } 111 | 112 | static void remove_bookmark(int idx) { 113 | FILE *f; 114 | 115 | if ((f = fopen(fullpath, "w"))) { 116 | bookmarks = remove_from_list(&num_bookmarks, bookmarks, idx); 117 | print_info(_(bookmarks_rm), INFO_LINE); 118 | for (idx = xdg_bookmarks; idx < num_bookmarks; idx++) { 119 | fprintf(f, "%s\n", bookmarks[idx]); 120 | } 121 | fclose(f); 122 | update_special_mode(num_bookmarks, bookmarks, bookmarks_); 123 | } else { 124 | print_info(_(bookmarks_file_err), ERR_LINE); 125 | } 126 | } 127 | 128 | void show_bookmarks(void) { 129 | if (num_bookmarks) { 130 | show_special_tab(num_bookmarks, bookmarks, bookmarks_mode_str, bookmarks_); 131 | } else { 132 | print_info(_(no_bookmarks), INFO_LINE); 133 | } 134 | } 135 | 136 | void manage_enter_bookmarks(struct stat current_file_stat) { 137 | char c; 138 | 139 | if (access(str_ptr[active][ps[active].curr_pos], F_OK ) != -1 ) { 140 | leave_mode_helper(current_file_stat); 141 | } else { 142 | if (config.safe == FULL_SAFE) { 143 | ask_user(_(inexistent_bookmark_quest), &c, 1); 144 | if (c == _(no)[0] || c == 27) { 145 | return; 146 | } 147 | } 148 | remove_bookmark(ps[active].curr_pos); 149 | print_info(_(inexistent_bookmark), INFO_LINE); 150 | } 151 | } 152 | 153 | void remove_all_user_bookmarks(void) { 154 | for (int i = num_bookmarks - 1; i >= xdg_bookmarks; i--) { 155 | remove_bookmark(i); 156 | } 157 | print_info(_(bookmarks_cleared), INFO_LINE); 158 | } 159 | 160 | void free_bookmarks(void) { 161 | if (bookmarks) { 162 | free(bookmarks); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/config.c: -------------------------------------------------------------------------------- 1 | #include "../inc/config.h" 2 | 3 | static void read_config_file(const char *dir); 4 | 5 | void parse_cmd(int argc, char * const argv[]) { 6 | int idx = 0, opt; 7 | struct option opts[] = 8 | { 9 | {"editor", 1, 0, 0}, 10 | {"starting_dir", 1, 0, 0}, 11 | {"helper_win", 1, 0, 0}, 12 | {"loglevel", 1, 0, 0}, 13 | {"persistent_log", 1, 0, 0}, 14 | {"low_level", 1, 0, 0}, 15 | {"safe", 1, 0, 0}, 16 | #ifdef LIBNOTIFY_PRESENT 17 | {"silent", 1, 0, 0}, 18 | #endif 19 | {"inhibit", 1, 0, 0}, 20 | {"automount", 1, 0, 0}, 21 | {0, 0, 0, 0} 22 | }; 23 | 24 | while ((opt = getopt_long(argc, argv, "", opts, &idx)) != -1) { 25 | if (optarg) { 26 | switch (idx) { 27 | case 0: 28 | strncpy(config.editor, optarg, PATH_MAX); 29 | break; 30 | case 1: 31 | strncpy(config.starting_dir, optarg, PATH_MAX); 32 | break; 33 | case 2: 34 | config.starting_helper = atoi(optarg); 35 | break; 36 | case 3: 37 | config.loglevel = atoi(optarg); 38 | break; 39 | case 4: 40 | config.persistent_log = atoi(optarg); 41 | break; 42 | case 5: 43 | config.bat_low_level = atoi(optarg); 44 | break; 45 | case 6: 46 | config.safe = atoi(optarg); 47 | break; 48 | #ifdef LIBNOTIFY_PRESENT 49 | case 7: 50 | config.silent = atoi(optarg); 51 | break; 52 | case 8: 53 | config.inhibit = atoi(optarg); 54 | break; 55 | case 9: 56 | config.automount = atoi(optarg); 57 | break; 58 | #else 59 | case 7: 60 | config.inhibit = atoi(optarg); 61 | break; 62 | case 8: 63 | config.automount = atoi(optarg); 64 | break; 65 | #endif 66 | } 67 | } 68 | } 69 | } 70 | 71 | void load_config_files(void) { 72 | char config_path[PATH_MAX + 1] = {0}; 73 | 74 | // Read global conf file in /etc/default 75 | read_config_file(CONFDIR); 76 | 77 | // Try to get XDG_CONFIG_HOME from env 78 | if (getenv("XDG_CONFIG_HOME")) { 79 | strncpy(config_path, getenv("XDG_CONFIG_HOME"), PATH_MAX); 80 | } else { 81 | // fallback to ~/.config/ 82 | snprintf(config_path, PATH_MAX, "%s/.config", getpwuid(getuid())->pw_dir); 83 | } 84 | read_config_file(config_path); 85 | } 86 | 87 | static void read_config_file(const char *dir) { 88 | config_t cfg; 89 | char config_file_name[PATH_MAX + 1] = {0}; 90 | const char *str_editor, *str_starting_dir, *str_cursor, *sysinfo; 91 | 92 | snprintf(config_file_name, PATH_MAX, "%s/ncursesFM.conf", dir); 93 | if (access(config_file_name, F_OK ) == -1) { 94 | fprintf(stderr, "Config file %s not found.\n", config_file_name); 95 | return; 96 | } 97 | config_init(&cfg); 98 | if (config_read_file(&cfg, config_file_name) == CONFIG_TRUE) { 99 | if (config_lookup_string(&cfg, "editor", &str_editor) == CONFIG_TRUE) { 100 | strncpy(config.editor, str_editor, PATH_MAX); 101 | } 102 | config_lookup_int(&cfg, "show_hidden", &config.show_hidden); 103 | if (config_lookup_string(&cfg, "starting_directory", &str_starting_dir) == CONFIG_TRUE) { 104 | strncpy(config.starting_dir, str_starting_dir, PATH_MAX); 105 | } 106 | config_lookup_int(&cfg, "use_default_starting_dir_second_tab", &config.second_tab_starting_dir); 107 | config_lookup_int(&cfg, "starting_helper", &config.starting_helper); 108 | #ifdef LIBNOTIFY_PRESENT 109 | config_lookup_int(&cfg, "silent", &config.silent); 110 | #endif 111 | config_lookup_int(&cfg, "inhibit", &config.inhibit); 112 | config_lookup_int(&cfg, "automount", &config.automount); 113 | config_lookup_int(&cfg, "loglevel", &config.loglevel); 114 | config_lookup_int(&cfg, "persistent_log", &config.persistent_log); 115 | config_lookup_int(&cfg, "bat_low_level", &config.bat_low_level); 116 | if (config_lookup_string(&cfg, "cursor_chars", &str_cursor) == CONFIG_TRUE) { 117 | mbstowcs(config.cursor_chars, str_cursor, 2); 118 | } 119 | if (config_lookup_string(&cfg, "sysinfo_layout", &sysinfo) == CONFIG_TRUE) { 120 | strncpy(config.sysinfo_layout, sysinfo, sizeof(config.sysinfo_layout)); 121 | } 122 | config_lookup_int(&cfg, "safe", &config.safe); 123 | } else { 124 | fprintf(stderr, "Config file: %s at line %d.\n", 125 | config_error_text(&cfg), 126 | config_error_line(&cfg)); 127 | } 128 | config_destroy(&cfg); 129 | } 130 | 131 | void config_checks(void) { 132 | if ((strlen(config.starting_dir)) && (access(config.starting_dir, F_OK) == -1)) { 133 | memset(config.starting_dir, 0, strlen(config.starting_dir)); 134 | } 135 | if (!strlen(config.editor) || (access(config.editor, X_OK) == -1)) { 136 | memset(config.editor, 0, strlen(config.editor)); 137 | WARN("no editor defined. Trying to get one from env."); 138 | const char *str; 139 | 140 | if ((str = getenv("EDITOR"))) { 141 | strncpy(config.editor, str, PATH_MAX); 142 | } else { 143 | WARN("no editor env var found."); 144 | } 145 | } 146 | if ((config.loglevel < LOG_ERR) || (config.loglevel > NO_LOG)) { 147 | config.loglevel = LOG_ERR; 148 | } 149 | if (config.safe < UNSAFE || config.safe > FULL_SAFE) { 150 | config.safe = FULL_SAFE; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/devices.c: -------------------------------------------------------------------------------- 1 | #include "../inc/devices.h" 2 | 3 | static int mount_fs(const char *str, int mount); 4 | static void set_autoclear(const char *loopname); 5 | static int is_iso_mounted(const char *filename, char *loop_dev); 6 | static void iso_backing_file(char *s, const char *name); 7 | static int init_mbus(void); 8 | static int check_udisks(void); 9 | static int add_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error); 10 | static int remove_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error); 11 | static int change_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error); 12 | static int change_power_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error); 13 | static void enumerate_block_devices(void); 14 | static int get_mount_point(const char *dev_path, char *path); 15 | static void change_mounted_status(int pos, const char *name); 16 | static void fix_tab_cwd(void); 17 | static int add_device(struct udev_device *dev, const char *name); 18 | static int remove_device(const char *name); 19 | 20 | static char mount_str[PATH_MAX + 1]; 21 | static struct udev *udev; 22 | static int number_of_devices; 23 | static char (*devices)[PATH_MAX + 1]; 24 | static sd_bus *bus; 25 | 26 | /* 27 | * Open the bus, and call "method" string on UDisks2.Filesystem. 28 | */ 29 | static int mount_fs(const char *str, int mount) { 30 | sd_bus_error error = SD_BUS_ERROR_NULL; 31 | sd_bus_message *mess = NULL; 32 | sd_bus *mount_bus = NULL; 33 | const char *path; 34 | char obj_path[PATH_MAX + 1] = "/org/freedesktop/UDisks2/block_devices/"; 35 | char tmp[30], method[10]; 36 | int r, ret = -1; 37 | char mounted_path[PATH_MAX + 1] = {0}; 38 | int len, len2; 39 | 40 | r = sd_bus_open_system(&mount_bus); 41 | if (r < 0) { 42 | print_and_warn(strerror(-r), ERR_LINE); 43 | goto finish; 44 | } 45 | if (mount) { 46 | if (get_mount_point(str, mounted_path) == -1) { 47 | ERROR("Could not get mount point."); 48 | } 49 | // calculate root dir of mounted path 50 | strncpy(mounted_path, dirname(mounted_path), PATH_MAX); 51 | len = strlen(mounted_path); 52 | len2 = strlen(ps[active].my_cwd); 53 | // if active win is inside mounted path 54 | if (!strncmp(ps[active].my_cwd, mounted_path, len) && len2 > len) { 55 | // move away process' cwd from mounted path as it would raise an error while unmounting 56 | chdir(mounted_path); 57 | } 58 | strcpy(method, "Unmount"); 59 | } else { 60 | strcpy(method, "Mount"); 61 | } 62 | strcat(obj_path, strrchr(str, '/') + 1); 63 | sprintf(tmp, "calling %s on bus.", method); 64 | INFO(tmp); 65 | r = sd_bus_call_method(mount_bus, 66 | "org.freedesktop.UDisks2", 67 | obj_path, 68 | "org.freedesktop.UDisks2.Filesystem", 69 | method, 70 | &error, 71 | &mess, 72 | "a{sv}", 73 | NULL); 74 | if (r < 0) { 75 | /* if it was not succesful, restore old_cwd */ 76 | chdir(ps[active].my_cwd); 77 | print_and_warn(error.message, ERR_LINE); 78 | goto finish; 79 | } 80 | ret = 1; 81 | if (!mount) { 82 | sd_bus_message_read(mess, "s", &path); 83 | snprintf(mount_str, PATH_MAX, _(dev_mounted), str, path); 84 | INFO("Mounted."); 85 | /* if it's a loop dev, set autoclear after mounting it */ 86 | if (!strncmp(str, "/dev/loop", strlen("/dev/loop"))) { 87 | set_autoclear(str); 88 | } 89 | } else { 90 | /* 91 | * save back in ps[active].my_cwd, process' cwd 92 | * to be sure we carry the right path 93 | */ 94 | getcwd(ps[active].my_cwd, PATH_MAX); 95 | snprintf(mount_str, PATH_MAX, _(dev_unmounted), str); 96 | INFO("Unmounted."); 97 | } 98 | 99 | finish: 100 | close_bus(&error, mess, mount_bus); 101 | return ret; 102 | } 103 | 104 | /* 105 | * Open the bus and setup the loop device, 106 | * then set "SetAutoclear" option; this way the loop device 107 | * will be automagically cleared when no more in use. 108 | */ 109 | void isomount(const char *str) { 110 | sd_bus_error error = SD_BUS_ERROR_NULL; 111 | sd_bus_message *mess = NULL; 112 | sd_bus *iso_bus = NULL; 113 | const char *obj_path; 114 | int r, fd; 115 | char loop_dev[PATH_MAX + 1]; 116 | 117 | int mount = is_iso_mounted(str, loop_dev); 118 | /* it was already present */ 119 | if (mount != -1) { 120 | /* 121 | * if it was mounted, then unmount it. 122 | * if it is was unmounted, just mount it (ie: someone else 123 | * created this loop dev probably without autoclear true) 124 | */ 125 | mount_fs(loop_dev, mount); 126 | return; 127 | } 128 | fd = open(str, O_RDONLY); 129 | r = sd_bus_open_system(&iso_bus); 130 | if (r < 0) { 131 | print_and_warn(strerror(-r), ERR_LINE); 132 | goto finish; 133 | } 134 | INFO("calling LoopSetup method on bus."); 135 | r = sd_bus_call_method(iso_bus, 136 | "org.freedesktop.UDisks2", 137 | "/org/freedesktop/UDisks2/Manager", 138 | "org.freedesktop.UDisks2.Manager", 139 | "LoopSetup", 140 | &error, 141 | &mess, 142 | "ha{sv}", 143 | fd, 144 | NULL); 145 | if (r < 0) { 146 | print_and_warn(error.message, ERR_LINE); 147 | goto finish; 148 | } 149 | sd_bus_message_read(mess, "o", &obj_path); 150 | 151 | finish: 152 | close(fd); 153 | close_bus(&error, mess, iso_bus); 154 | } 155 | 156 | static void set_autoclear(const char *loopname) { 157 | sd_bus_error error = SD_BUS_ERROR_NULL; 158 | sd_bus_message *mess = NULL; 159 | sd_bus *iso_bus = NULL; 160 | int r; 161 | 162 | r = sd_bus_open_system(&iso_bus); 163 | if (r < 0) { 164 | print_and_warn(strerror(-r), ERR_LINE); 165 | } else { 166 | char obj_path[PATH_MAX + 1] = "/org/freedesktop/UDisks2/block_devices/"; 167 | 168 | strcat(obj_path, strrchr(loopname, '/') + 1); 169 | INFO("calling SetAutoClear on bus."); 170 | r = sd_bus_call_method(iso_bus, 171 | "org.freedesktop.UDisks2", 172 | obj_path, 173 | "org.freedesktop.UDisks2.Loop", 174 | "SetAutoclear", 175 | &error, 176 | NULL, 177 | "ba{sv}", 178 | TRUE, 179 | NULL); 180 | if (r < 0) { 181 | print_and_warn(error.message, ERR_LINE); 182 | } 183 | } 184 | close_bus(&error, mess, iso_bus); 185 | } 186 | 187 | /* 188 | * Check if iso is already mounted. 189 | * For each /dev/loop device present, calls iso_backing_file on bus. 190 | * As soon as it finds a matching backing_file with current file's filename, 191 | * it breaks the cycle and returns its mounted status; 192 | */ 193 | static int is_iso_mounted(const char *filename, char loop_dev[PATH_MAX + 1]) { 194 | struct udev *tmp_udev; 195 | struct udev_enumerate *enumerate; 196 | struct udev_list_entry *devlist, *dev_list_entry; 197 | char s[PATH_MAX + 1] = {0}; 198 | char resolved_path[PATH_MAX + 1]; 199 | int mount = -1; 200 | 201 | realpath(filename, resolved_path); 202 | tmp_udev = udev_new(); 203 | enumerate = udev_enumerate_new(tmp_udev); 204 | udev_enumerate_add_match_subsystem(enumerate, "block"); 205 | udev_enumerate_add_match_property(enumerate, "ID_FS_TYPE", "udf"); 206 | udev_enumerate_scan_devices(enumerate); 207 | devlist = udev_enumerate_get_list_entry(enumerate); 208 | udev_list_entry_foreach(dev_list_entry, devlist) { 209 | const char *path = udev_list_entry_get_name(dev_list_entry); 210 | struct udev_device *dev = udev_device_new_from_syspath(tmp_udev, path); 211 | strncpy(loop_dev, udev_device_get_devnode(dev), PATH_MAX); 212 | iso_backing_file(s, loop_dev); 213 | udev_device_unref(dev); 214 | if (!strcmp(s, resolved_path)) { 215 | mount = get_mount_point(loop_dev, NULL); 216 | break; 217 | } 218 | } 219 | udev_enumerate_unref(enumerate); 220 | udev_unref(tmp_udev); 221 | return mount; 222 | } 223 | 224 | /* 225 | * Given a loop device, returns the iso file mounted on it. 226 | */ 227 | static void iso_backing_file(char *s, const char *name) { 228 | sd_bus_error error = SD_BUS_ERROR_NULL; 229 | sd_bus_message *mess = NULL; 230 | sd_bus *iso_bus = NULL; 231 | int r; 232 | uint8_t bytes = '\0'; 233 | char obj_path[PATH_MAX + 1] = "/org/freedesktop/UDisks2/block_devices/"; 234 | 235 | r = sd_bus_open_system(&iso_bus); 236 | if (r < 0) { 237 | print_and_warn(strerror(-r), ERR_LINE); 238 | goto finish; 239 | } 240 | strcat(obj_path, strrchr(name, '/') + 1); 241 | INFO("getting BackingFile property on bus."); 242 | r = sd_bus_get_property(iso_bus, 243 | "org.freedesktop.UDisks2", 244 | obj_path, 245 | "org.freedesktop.UDisks2.Loop", 246 | "BackingFile", 247 | &error, 248 | &mess, 249 | "ay"); 250 | if (r < 0) { 251 | print_and_warn(error.message, ERR_LINE); 252 | goto finish; 253 | } 254 | r = sd_bus_message_enter_container(mess, SD_BUS_TYPE_ARRAY, "y"); 255 | if (r < 0) { 256 | print_and_warn(strerror(-r), ERR_LINE); 257 | goto finish; 258 | } 259 | while ((sd_bus_message_read(mess, "y", &bytes)) > 0) { 260 | sprintf(s + strlen(s), "%c", (char)bytes); 261 | } 262 | 263 | finish: 264 | close_bus(&error, mess, iso_bus); 265 | } 266 | 267 | /* 268 | * Starts udisks2 bus monitor and returns the 269 | * fd associated with the bus connection. 270 | */ 271 | int start_monitor(void) { 272 | udev = udev_new(); 273 | if (!udev) { 274 | WARN("could not create udev."); 275 | goto fail; 276 | } 277 | if (init_mbus()) { 278 | goto fail; 279 | } 280 | enumerate_block_devices(); 281 | if (quit) { 282 | goto fail; 283 | } 284 | device_init = DEVMON_READY; 285 | INFO("started device monitor."); 286 | return sd_bus_get_fd(bus); 287 | 288 | fail: 289 | device_init = DEVMON_OFF; 290 | return -1; 291 | } 292 | 293 | /* 294 | * Add matches to bus; first 3 matches are related to udisks2 signals, 295 | * last match is related to upower signals. 296 | * If last add_match fails, it won't be fatal (ie: monitor will start anyway) 297 | */ 298 | static int init_mbus(void) { 299 | int r; 300 | 301 | r = sd_bus_open_system(&bus); 302 | if (r < 0) { 303 | goto fail; 304 | } 305 | r = check_udisks(); 306 | if (r < 0) { 307 | WARN("UDisks2 not present. Disabling udisks2 monitor."); 308 | goto fail; 309 | } 310 | INFO("found udisks2."); 311 | r = sd_bus_add_match(bus, NULL, 312 | "type='signal'," 313 | "sender='org.freedesktop.UDisks2'," 314 | "interface='org.freedesktop.DBus.ObjectManager'," 315 | "member='InterfacesAdded'", 316 | add_callback, NULL); 317 | if (r < 0) { 318 | goto fail; 319 | } 320 | r = sd_bus_add_match(bus, NULL, 321 | "type='signal'," 322 | "sender='org.freedesktop.UDisks2'," 323 | "interface='org.freedesktop.DBus.ObjectManager'," 324 | "member='InterfacesRemoved'", 325 | remove_callback, NULL); 326 | if (r < 0) { 327 | goto fail; 328 | } 329 | r = sd_bus_add_match(bus, NULL, 330 | "type='signal'," 331 | "sender='org.freedesktop.UDisks2'," 332 | "interface='org.freedesktop.DBus.Properties'," 333 | "member='PropertiesChanged'", 334 | change_callback, NULL); 335 | if (r < 0) { 336 | goto fail; 337 | } 338 | r = sd_bus_add_match(bus, NULL, 339 | "type='signal'," 340 | "sender='org.freedesktop.UPower'," 341 | "interface='org.freedesktop.DBus.Properties'," 342 | "member='PropertiesChanged'", 343 | change_power_callback, NULL); 344 | if (r < 0) { 345 | WARN("UPower PropertiesChanged signals disabled."); 346 | } 347 | return 0; 348 | 349 | fail: 350 | WARN(strerror(-r)); 351 | return r; 352 | } 353 | 354 | /* 355 | * Ping udisks2 just to check if it is present 356 | */ 357 | static int check_udisks(void) { 358 | INFO("checking for udisks2"); 359 | return sd_bus_call_method(bus, 360 | "org.freedesktop.UDisks2", 361 | "/org/freedesktop/UDisks2", 362 | "org.freedesktop.DBus.Peer", 363 | "Ping", 364 | NULL, 365 | NULL, 366 | NULL); 367 | } 368 | 369 | /* 370 | * Once received InterfaceAdded signal, 371 | * call add_device on the newly attached device. 372 | */ 373 | static int add_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { 374 | int r; 375 | const char *path; 376 | const char obj[] = "/org/freedesktop/UDisks2/block_devices/"; 377 | char devname[PATH_MAX + 1] = {0}; 378 | char name[NAME_MAX + 1] = {0}; 379 | struct udev_device *dev; 380 | 381 | r = sd_bus_message_read(m, "o", &path); 382 | if (r < 0) { 383 | WARN(strerror(-r)); 384 | } else { 385 | if (!strncmp(obj, path, strlen(obj))) { 386 | INFO("InterfaceAdded signal received!"); 387 | strncpy(name, strrchr(path, '/') + 1, NAME_MAX); 388 | dev = udev_device_new_from_subsystem_sysname(udev, "block", name); 389 | if (dev) { 390 | snprintf(devname, PATH_MAX, "/dev/%s", name); 391 | r = add_device(dev, devname); 392 | udev_device_unref(dev); 393 | if (!quit && r != -1) { 394 | update_special_mode(number_of_devices, devices, device_); 395 | } 396 | } 397 | } else { 398 | INFO("signal discarded."); 399 | } 400 | } 401 | return 0; 402 | } 403 | 404 | /* 405 | * Once received the InterfaceRemoved signal, 406 | * call remove_device on the dev. 407 | */ 408 | static int remove_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { 409 | int r; 410 | const char *path; 411 | const char obj[] = "/org/freedesktop/UDisks2/block_devices/"; 412 | char devname[PATH_MAX + 1] = {0}; 413 | char name[NAME_MAX + 1] = {0}; 414 | 415 | r = sd_bus_message_read(m, "o", &path); 416 | if (r < 0) { 417 | WARN(strerror(-r)); 418 | } else { 419 | if (!strncmp(obj, path, strlen(obj))) { 420 | INFO("InterfaceRemoved signal received!"); 421 | strncpy(name, strrchr(path, '/') + 1, NAME_MAX); 422 | snprintf(devname, PATH_MAX, "/dev/%s", name); 423 | r = remove_device(devname); 424 | if (!quit && r != -1) { 425 | update_special_mode(number_of_devices, devices, device_); 426 | } 427 | } else { 428 | INFO("signal discarded."); 429 | } 430 | } 431 | return 0; 432 | } 433 | 434 | /* 435 | * If message received == org.freedesktop.UDisks2.Filesystem, then 436 | * only possible signal is "mounted status has changed". 437 | * So, change mounted status of device (device name = sd_bus_message_get_path(m)) 438 | */ 439 | static int change_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { 440 | int r; 441 | const char *path; 442 | const char obj[] = "org.freedesktop.UDisks2.Filesystem"; 443 | char devname[PATH_MAX + 1] = {0}; 444 | 445 | r = sd_bus_message_read(m, "s", &path); 446 | if (r < 0) { 447 | WARN(strerror(-r)); 448 | } else { 449 | if (!strncmp(obj, path, strlen(obj))) { 450 | INFO("PropertiesChanged UDisks2 signal received!"); 451 | const char *name = sd_bus_message_get_path(m); 452 | snprintf(devname, PATH_MAX, "/dev/%s", strrchr(name, '/') + 1); 453 | int present = is_present(devname, devices, number_of_devices, strlen(devname), 0); 454 | if (present != -1) { 455 | change_mounted_status(present, devname); 456 | update_special_mode(present, NULL, device_); 457 | } 458 | } else { 459 | INFO("signal discarded."); 460 | } 461 | } 462 | return 0; 463 | } 464 | 465 | /* 466 | * Monitor UPower too, for AC (dis)connected events. 467 | * It won't monitor battery level changes (THIS IS NOT A BATTERY MONITOR!). 468 | * After receiving a signal, just calls timer_event() to update time/battery. 469 | */ 470 | static int change_power_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { 471 | const char obj[] = "/org/freedesktop/UPower"; 472 | 473 | const char *path = sd_bus_message_get_path(m); 474 | if (!strcmp(path, obj)) { 475 | INFO("PropertiesChanged UPower signal received!"); 476 | timer_event(); 477 | } 478 | return 0; 479 | } 480 | 481 | void devices_bus_process(void) { 482 | sd_bus_process(bus, NULL); 483 | } 484 | 485 | /* 486 | * Updates current tab str_ptr and number of files, 487 | * set device_mode and special_mode bits, 488 | * change tab title, and calls reset_win() 489 | */ 490 | void show_devices_tab(void) { 491 | if (number_of_devices) { 492 | show_special_tab(number_of_devices, devices, device_mode_str, device_); 493 | } else { 494 | print_info(_(no_devices), INFO_LINE); 495 | } 496 | } 497 | 498 | /* 499 | * Scan "block" subsystem for devices. 500 | * For each device check if it is a really mountable external fs, 501 | * add it to devices{} and increment number_of_devices. 502 | */ 503 | static void enumerate_block_devices(void) { 504 | struct udev_enumerate *enumerate; 505 | struct udev_list_entry *devlist, *dev_list_entry; 506 | 507 | enumerate = udev_enumerate_new(udev); 508 | udev_enumerate_add_match_subsystem(enumerate, "block"); 509 | udev_enumerate_scan_devices(enumerate); 510 | devlist = udev_enumerate_get_list_entry(enumerate); 511 | udev_list_entry_foreach(dev_list_entry, devlist) { 512 | const char *path = udev_list_entry_get_name(dev_list_entry); 513 | struct udev_device *dev; 514 | 515 | if ((dev = udev_device_new_from_syspath(udev, path))) { 516 | const char *name = udev_device_get_devnode(dev); 517 | add_device(dev, name); 518 | udev_device_unref(dev); 519 | if (quit) { 520 | break; 521 | } 522 | } 523 | } 524 | udev_enumerate_unref(enumerate); 525 | } 526 | 527 | /* 528 | * Getting mountpoint for a device through mtab. 529 | */ 530 | static int get_mount_point(const char *dev_path, char *path) { 531 | struct mntent *part; 532 | FILE *mtab; 533 | int ret = 0; 534 | 535 | if (!(mtab = setmntent("/proc/mounts", "r"))) { 536 | WARN(no_proc_mounts); 537 | print_info(_(no_proc_mounts), ERR_LINE); 538 | return -1; 539 | } 540 | while ((part = getmntent(mtab))) { 541 | if ((part->mnt_fsname) && (!strcmp(part->mnt_fsname, dev_path))) { 542 | if (path) { 543 | strncpy(path, part->mnt_dir, PATH_MAX); 544 | } 545 | ret = 1; 546 | break; 547 | } 548 | } 549 | endmntent(mtab); 550 | return ret; 551 | } 552 | 553 | /* 554 | * Check what action should be performed (mount or unmount), 555 | * if unmount action, checks if tabs are inside the mounted device; 556 | * if that's the case, change cwd to path just before the mountpoint, 557 | * then proceed with the action. 558 | * If not active tab was inside mounted folder, after the unmount action, 559 | * it moves the tab to the path just before the mountpoint. 560 | */ 561 | void manage_mount_device(void) { 562 | int mount; 563 | int pos = ps[active].curr_pos; 564 | int len = strlen(devices[pos]); 565 | char *ptr = strchr(devices[pos], ','); 566 | char name[PATH_MAX + 1]; 567 | 568 | strncpy(name, devices[pos], PATH_MAX); 569 | name[len - strlen(ptr)] = '\0'; 570 | mount = devices[pos][len - 1] - '0'; 571 | mount_fs(name, mount); 572 | } 573 | 574 | /* 575 | * Pressing enter on a device will move to its mountpoint. 576 | * If device is not mounted, it will mount it too. 577 | */ 578 | void manage_enter_device(void) { 579 | int mount, ret = 1; 580 | int pos = ps[active].curr_pos; 581 | int len = strlen(devices[pos]); 582 | char *ptr = strchr(devices[pos], ','); 583 | char dev_path[PATH_MAX + 1] = {0}, name[PATH_MAX + 1] = {0}; 584 | 585 | mount = devices[pos][len - 1] - '0'; 586 | strncpy(dev_path, devices[pos], PATH_MAX); 587 | dev_path[len - strlen(ptr)] = '\0'; 588 | if (!mount) { 589 | ret = mount_fs(dev_path, mount); 590 | } 591 | if (ret == 1 && get_mount_point(dev_path, name) != -1) { 592 | memset(ps[active].old_file, 0, strlen(ps[active].old_file)); 593 | leave_special_mode(name, active); 594 | } 595 | } 596 | 597 | static void change_mounted_status(int pos, const char *name) { 598 | int len = strlen(devices[pos]); 599 | int mount = devices[pos][len - 1] - '0'; 600 | sprintf(devices[pos] + len - 1, "%d", !mount); 601 | if (!strlen(mount_str)) { 602 | if (mount) { 603 | snprintf(mount_str, PATH_MAX, _(ext_dev_unmounted), name); 604 | } else { 605 | snprintf(mount_str, PATH_MAX, _(ext_dev_mounted), name); 606 | } 607 | } 608 | print_info(_(mount_str), INFO_LINE); 609 | memset(mount_str, 0, strlen(mount_str)); 610 | // if mount == 1, it means we're unmounting 611 | // and we need to check !active tab 612 | // to see if it was inside mountpoint 613 | // and in case, move it away 614 | if (mount) { 615 | fix_tab_cwd(); 616 | } 617 | } 618 | 619 | /* 620 | * If a tab was inside unmounted mount point, 621 | * move it away, and if it is in normal mode, 622 | * refresh its dir 623 | * 624 | */ 625 | static void fix_tab_cwd(void) { 626 | for (int j = 0; j < cont; j++) { 627 | int i = 0; 628 | while (access(ps[j].my_cwd, F_OK) != 0 && strlen(ps[j].my_cwd)) { 629 | i++; 630 | strncpy(ps[j].my_cwd, dirname(ps[j].my_cwd), PATH_MAX); 631 | } 632 | if (i && ps[j].mode <= fast_browse_) { 633 | change_dir(ps[j].my_cwd, j); 634 | } 635 | } 636 | } 637 | 638 | void free_device_monitor(void) { 639 | INFO("freeing device monitor resources..."); 640 | if (bus) { 641 | sd_bus_flush_close_unref(bus); 642 | } 643 | if (devices) { 644 | free(devices); 645 | } 646 | if (udev) { 647 | udev_unref(udev); 648 | } 649 | INFO("freed."); 650 | } 651 | 652 | /* 653 | * Check if dev must be ignored, then 654 | * check if it is a mountable fs (eg: a dvd reader without a dvd inserted, is NOT a moutable fs), 655 | * and its mount status. Then add the device to devices. 656 | * If the dev is not mounted and it is a loop_dev or if config.automount is == 1, 657 | * it will mount the device. 658 | */ 659 | static int add_device(struct udev_device *dev, const char *name) { 660 | int mount; 661 | const char *ignore = udev_device_get_property_value(dev, "UDISKS_IGNORE"); 662 | const char *fs_type = udev_device_get_property_value(dev, "ID_FS_TYPE"); 663 | 664 | if (!fs_type || (ignore && !strcmp(ignore, "1"))) { 665 | mount = -1; 666 | } else { 667 | mount = get_mount_point(name, NULL); 668 | } 669 | if (mount != -1) { 670 | devices = safe_realloc(number_of_devices + 1, devices); 671 | if (!quit) { 672 | if (udev_device_get_property_value(dev, "ID_MODEL")) { 673 | snprintf(devices[number_of_devices], PATH_MAX, "%s, %s, Mounted: %d", 674 | name, udev_device_get_property_value(dev, "ID_MODEL"), mount); 675 | } else { 676 | snprintf(devices[number_of_devices], PATH_MAX, "%s, Mounted: %d", 677 | name, mount); 678 | } 679 | number_of_devices++; 680 | INFO(device_added); 681 | print_info(_(device_added), INFO_LINE); 682 | int is_loop_dev = !strncmp(name, "/dev/loop", strlen("/dev/loop")); 683 | if (device_init != DEVMON_STARTING && (!mount && (config.automount || is_loop_dev))) { 684 | mount_fs(name, mount); 685 | } 686 | } 687 | } 688 | return mount; 689 | } 690 | 691 | static int remove_device(const char *name) { 692 | int i = is_present(name, devices, number_of_devices, strlen(name), 0); 693 | 694 | if (i != -1) { 695 | devices = remove_from_list(&number_of_devices, devices, i); 696 | if (!quit) { 697 | print_info(_(device_removed), INFO_LINE); 698 | INFO(device_removed); 699 | } 700 | } 701 | return i; 702 | } 703 | 704 | void show_devices_stat(int i, int win, char *str) { 705 | const int flags[] = { ST_MANDLOCK, ST_NOATIME, ST_NODEV, 706 | ST_NODIRATIME, ST_NOEXEC, ST_NOSUID, 707 | ST_RDONLY, ST_RELATIME, ST_SYNCHRONOUS}; 708 | struct statvfs stat; 709 | char dev_path[PATH_MAX + 1] = {0}, path[PATH_MAX + 1] = {0}; 710 | char s[20] = {0}; 711 | uint64_t total; 712 | 713 | int len = strlen(devices[i]); 714 | int mount = devices[i][len - 1] - '0'; 715 | strncpy(dev_path, devices[i], PATH_MAX); 716 | char *ptr = strchr(devices[i], ','); 717 | dev_path[len - strlen(ptr)] = '\0'; 718 | // if device is mounted 719 | if (mount && get_mount_point(dev_path, path) != -1) { 720 | const char *fl[] = {"mand", "noatime", "nodev", "nodiratime", 721 | "noexec", "nosuid", "ro", "relatime", "sync"}; 722 | 723 | statvfs(path, &stat); 724 | uint64_t free = stat.f_bavail * stat.f_bsize; 725 | change_unit((float) free, s); 726 | sprintf(str, "Free: %s/", s); 727 | memset(s, 0, strlen(s)); 728 | total = stat.f_blocks * stat.f_frsize; 729 | change_unit((float) total, s); 730 | strcat(str, s); 731 | sprintf(str + strlen(str), ", mount opts:"); 732 | for (int j = 0; j < 9; j++) { 733 | if (stat.f_flag & flags[j]) { 734 | sprintf(str + strlen(str), " %s", fl[j]); 735 | } 736 | } 737 | if (!(stat.f_flag & ST_RDONLY)) { 738 | sprintf(str + strlen(str), " rw"); 739 | } 740 | if (!(stat.f_flag & ST_SYNCHRONOUS)) { 741 | sprintf(str + strlen(str), " async"); 742 | } 743 | } else { 744 | // use udev system to get device size 745 | struct udev_device *dev; 746 | char *name = strrchr(dev_path, '/') + 1; 747 | dev = udev_device_new_from_subsystem_sysname(udev, "block", name); 748 | if (dev && udev_device_get_sysattr_value(dev, "size")) { 749 | total = strtol(udev_device_get_sysattr_value(dev, "size"), NULL, 10); 750 | total = (total / (uint64_t) 2) * (uint64_t)1024; 751 | change_unit((float)total, s); 752 | sprintf(str, "Total size: %s", s); 753 | udev_device_unref(dev); 754 | } 755 | } 756 | } 757 | -------------------------------------------------------------------------------- /src/fm.c: -------------------------------------------------------------------------------- 1 | #include "../inc/fm.h" 2 | 3 | static void xdg_open(const char *str); 4 | static void open_file(const char *str); 5 | static int new_file(const char *name); 6 | static int new_dir(const char *name); 7 | static int rename_file_folders(const char *name); 8 | static void select_file(const char *str); 9 | static void select_all(void); 10 | static void deselect_all(void); 11 | static void cpr(const char *tmp); 12 | static int recursive_copy(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf); 13 | static int recursive_remove(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf); 14 | static void rmrf(const char *path); 15 | 16 | static const char *pkg_ext[] = {".pkg.tar.xz", ".deb", ".rpm"}; 17 | static int distance_from_root, is_selecting; 18 | static int (*const short_func[SHORT_FILE_OPERATIONS])(const char *) = { 19 | new_file, new_dir, rename_file_folders 20 | }; 21 | 22 | int change_dir(const char *str, int win) { 23 | int ret = 0; 24 | const int event_mask = IN_MODIFY | IN_ATTRIB | IN_CREATE | IN_DELETE | IN_MOVE; 25 | 26 | if (chdir(str) != -1) { 27 | getcwd(ps[win].my_cwd, PATH_MAX); 28 | strncpy(ps[win].title, ps[win].my_cwd, PATH_MAX); 29 | tab_refresh(win); 30 | inotify_rm_watch(ps[win].inot.fd, ps[win].inot.wd); 31 | ps[win].inot.wd = inotify_add_watch(ps[win].inot.fd, ps[win].my_cwd, event_mask); 32 | // reset selecting status (double space to select all) 33 | // when changing dir 34 | is_selecting = 0; 35 | } else { 36 | print_info(strerror(errno), ERR_LINE); 37 | ret = -1; 38 | } 39 | // force process dir to be active tab's one 40 | if (win != active) { 41 | chdir(ps[active].my_cwd); 42 | } 43 | return ret; 44 | } 45 | 46 | void change_tab(void) { 47 | active = !active; 48 | chdir(ps[active].my_cwd); 49 | update_colors(); 50 | } 51 | 52 | void switch_hidden(void) { 53 | ps[active].show_hidden = !ps[active].show_hidden; 54 | save_old_pos(active); 55 | tab_refresh(active); 56 | } 57 | 58 | /* 59 | * Check if it is an iso, then try to mount it. 60 | * If it is a package, ask user to mount it. 61 | * If it is an archive, initialize a thread job to extract it. 62 | * if compiled with X11 support, and xdg-open is found, open the file, 63 | * else open the file with config.editor. 64 | */ 65 | void manage_file(const char *str) { 66 | if (get_mimetype(str, "iso9660")) { 67 | isomount(str); 68 | return; 69 | } 70 | if (is_ext(str, pkg_ext, NUM(pkg_ext))) { 71 | char c; 72 | if (config.safe != UNSAFE) { 73 | print_info(_(package_warn), INFO_LINE); 74 | ask_user(_(pkg_quest), &c, 1); 75 | print_info("", INFO_LINE); 76 | if (c != _(yes)[0]) { 77 | return; 78 | } 79 | } 80 | pthread_create(&install_th, NULL, install_package, (void *)str); 81 | return; 82 | } 83 | if (has_desktop && !access("/usr/bin/xdg-open", X_OK)) { 84 | xdg_open(str); 85 | } else { 86 | open_file(str); 87 | } 88 | } 89 | 90 | /* 91 | * If we're in a desktop environment, open the file with xdg-open and redirect its output to /dev/null 92 | * not to make my ncurses screen dirty. 93 | */ 94 | static void xdg_open(const char *str) { 95 | if (vfork() == 0) { 96 | // redirect stdout and stderr to null 97 | int fd = open("/dev/null",O_WRONLY); 98 | dup2(fd, 1); 99 | dup2(fd, 2); 100 | close(fd); 101 | execl("/usr/bin/xdg-open", "/usr/bin/xdg-open", str, (char *) 0); 102 | } 103 | } 104 | 105 | /* 106 | * if config.editor is set opens the file with it. 107 | */ 108 | static void open_file(const char *str) { 109 | if ((!get_mimetype(str, "text/")) && (!get_mimetype(str, "x-empty"))) { 110 | return; 111 | } 112 | if (strlen(config.editor)) { 113 | endwin(); 114 | pid_t pid = vfork(); 115 | if (pid == 0) { 116 | execl(config.editor, config.editor, str, (char *) 0); 117 | } else { 118 | waitpid(pid, NULL, 0); 119 | // trigger a fake resize to restore previous win state 120 | // understand why this is needed now (it wasn't needed some time ago as 121 | // a refresh() was, and should be, enough) 122 | // refresh(); 123 | ungetch(KEY_RESIZE); 124 | 125 | } 126 | } else { 127 | print_info(_(editor_missing), ERR_LINE); 128 | } 129 | } 130 | 131 | /* 132 | * Ask user for "new_name" var to be passed to short_func[], 133 | * then calls short_func[index]; only refresh UI if the call was successful. 134 | * Notifies user. 135 | */ 136 | void fast_file_operations(const int index) { 137 | char new_name[NAME_MAX + 1] = {0}; 138 | 139 | ask_user(_(ask_name), new_name, NAME_MAX); 140 | if (!strlen(new_name) || new_name[0] == 27) { 141 | return; 142 | } 143 | int r = short_func[index](new_name); 144 | if (r == 0) { 145 | print_info(_(short_msg[index]), INFO_LINE); 146 | } else { 147 | print_info(strerror(-r), ERR_LINE); 148 | } 149 | } 150 | 151 | int new_file(const char *name) { 152 | int fd = open(name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 153 | if (fd != -1) { 154 | close(fd); 155 | return 0; 156 | } 157 | return fd; 158 | } 159 | 160 | static int new_dir(const char *name) { 161 | return mkdir(name, 0700); 162 | } 163 | 164 | static int rename_file_folders(const char *name) { 165 | return rename(ps[active].nl[ps[active].curr_pos], name); 166 | } 167 | 168 | /* 169 | * For each file to be removed, checks if we have write perm on it, then 170 | * remove it. 171 | * If at least 1 file was removed, the call is considered successful. 172 | */ 173 | int remove_file(void) { 174 | int ok = 0; 175 | 176 | for (int i = 0; i < thread_h->num_selected; i++) { 177 | if (access(thread_h->selected_files[i], W_OK) == 0) { 178 | ok++; 179 | rmrf(thread_h->selected_files[i]); 180 | } 181 | } 182 | return (ok ? 0 : -1); 183 | } 184 | 185 | /* 186 | * If there are no selected files, or the file user selected wasn't already selected (remove_from_list() returns 0), 187 | * add this file to select list. 188 | */ 189 | void manage_space_press(const char *str) { 190 | int idx; 191 | char c; 192 | int i = is_present(str, selected, num_selected, -1, 0); 193 | 194 | if (i == -1) { 195 | select_file(str); 196 | idx = 0; 197 | c = '*'; 198 | } else { 199 | selected = remove_from_list(&num_selected, selected, i); 200 | c = ' '; 201 | num_selected ? (idx = 1) : (idx = 2); 202 | } 203 | print_info(_(file_sel[idx]), INFO_LINE); 204 | highlight_selected(str, c, active); 205 | if (!strcmp(ps[active].my_cwd, ps[!active].my_cwd)) { 206 | highlight_selected(str, c, !active); 207 | } 208 | update_special_mode(num_selected, selected, selected_); 209 | } 210 | 211 | static void select_file(const char *str) { 212 | selected = safe_realloc(++num_selected, selected); 213 | if (quit) { 214 | return; 215 | } 216 | memset(selected[num_selected - 1], 0, PATH_MAX + 1); 217 | strncpy(selected[num_selected - 1], str, PATH_MAX); 218 | } 219 | 220 | void manage_all_space_press(void) { 221 | int idx; 222 | 223 | is_selecting = !is_selecting; 224 | if (is_selecting) { 225 | select_all(); 226 | idx = 3; 227 | } else { 228 | deselect_all(); 229 | selected ? (idx = 4) : (idx = 5); 230 | } 231 | print_info(_(file_sel[idx]), INFO_LINE); 232 | update_special_mode(num_selected, selected, selected_); 233 | } 234 | 235 | static void select_all(void) { 236 | for (int i = 0; i < ps[active].number_of_files; i++) { 237 | if (strcmp(strrchr(ps[active].nl[i], '/') + 1, "..")) { 238 | if (is_present(ps[active].nl[i], selected, num_selected, -1, 0) != -1) { 239 | continue; 240 | } 241 | select_file(ps[active].nl[i]); 242 | highlight_selected(ps[active].nl[i], '*', active); 243 | if (!strcmp(ps[active].my_cwd, ps[!active].my_cwd)) { 244 | highlight_selected(ps[active].nl[i], '*', !active); 245 | } 246 | } 247 | } 248 | } 249 | 250 | static void deselect_all(void) { 251 | for (int i = 0; i < ps[active].number_of_files; i++) { 252 | int j = is_present(ps[active].nl[i], selected, num_selected, -1, 0); 253 | if (j != -1) { 254 | selected = remove_from_list(&num_selected, selected, j); 255 | highlight_selected(ps[active].nl[i], ' ', active); 256 | if (!strcmp(ps[active].my_cwd, ps[!active].my_cwd)) { 257 | highlight_selected(ps[active].nl[i], ' ', !active); 258 | } 259 | } 260 | } 261 | } 262 | 263 | void remove_selected(void) { 264 | int idx; 265 | 266 | if (!strcmp(ps[active].my_cwd, ps[!active].my_cwd)) { 267 | highlight_selected(selected[ps[active].curr_pos], ' ', !active); 268 | } 269 | selected = remove_from_list(&num_selected, selected, ps[active].curr_pos); 270 | if (num_selected) { 271 | idx = 1; 272 | } else { 273 | idx = 2; 274 | } 275 | update_special_mode(num_selected, selected, selected_); 276 | print_info(_(file_sel[idx]), INFO_LINE); 277 | } 278 | 279 | void remove_all_selected(void) { 280 | for (int i = 0; i < num_selected; i++) { 281 | if (cont == 2) { 282 | highlight_selected(selected[i], ' ', !active); 283 | } 284 | } 285 | free(selected); 286 | selected = NULL; 287 | num_selected = 0; 288 | update_special_mode(num_selected, selected, selected_); 289 | print_info(_(selected_cleared), INFO_LINE); 290 | } 291 | 292 | void show_selected(void) { 293 | if (num_selected) { 294 | show_special_tab(num_selected, selected, selected_mode_str, selected_); 295 | } else { 296 | print_info(_(no_selected_files), INFO_LINE); 297 | } 298 | } 299 | 300 | void free_selected(void) { 301 | if (selected) { 302 | free(selected); 303 | } 304 | } 305 | 306 | /* 307 | * For each file being pasted, it performs a check: 308 | * it checks if file is being pasted in the same dir 309 | * from where it was copied. If it is the case, it does not copy it. 310 | */ 311 | int paste_file(void) { 312 | char path[PATH_MAX + 1] = {0}; 313 | 314 | for (int i = 0; i < thread_h->num_selected; i++) { 315 | strncpy(path, thread_h->selected_files[i], PATH_MAX); 316 | char *copied_file_dir = dirname(path); 317 | if (strcmp(thread_h->full_path, copied_file_dir)) { 318 | cpr(thread_h->selected_files[i]); 319 | } 320 | } 321 | return 0; 322 | } 323 | 324 | /* 325 | * Same check as paste_file func plus: 326 | * it checks if copied file dir and directory where the file is being moved 327 | * are on the same FS; if it is the case, it only renames it. 328 | * Else, the function has to copy it and rm copied file. 329 | */ 330 | int move_file(void) { 331 | char pasted_file[PATH_MAX + 1] = {0}, path[PATH_MAX + 1] = {0}; 332 | struct stat file_stat_copied, file_stat_pasted; 333 | 334 | lstat(thread_h->full_path, &file_stat_pasted); 335 | for (int i = 0; i < thread_h->num_selected; i++) { 336 | strncpy(path, thread_h->selected_files[i], PATH_MAX); 337 | char *copied_file_dir = dirname(path); 338 | if (strcmp(thread_h->full_path, copied_file_dir)) { 339 | lstat(copied_file_dir, &file_stat_copied); 340 | if (file_stat_copied.st_dev == file_stat_pasted.st_dev) { // if on the same fs, just rename the file 341 | snprintf(pasted_file, PATH_MAX, "%s%s", 342 | thread_h->full_path, 343 | strrchr(thread_h->selected_files[i], '/')); 344 | if (rename(thread_h->selected_files[i], pasted_file) == - 1) { 345 | print_info(strerror(errno), ERR_LINE); 346 | } 347 | } else { // copy file and remove original file 348 | cpr(thread_h->selected_files[i]); 349 | rmrf(thread_h->selected_files[i]); 350 | } 351 | } 352 | } 353 | return 0; 354 | } 355 | 356 | /* 357 | * It calculates the "distance_from_root" of the current file, where root is 358 | * the directory from where it is being copied (eg: copying /home/me/Scripts/ -> root is /home/me/). 359 | * Then calls nftw with recursive_copy. 360 | * distance_from_root is needed to make pasted_file relative to thread_h->full_path (ie: the folder where we're pasting). 361 | * In fact pasted_file will be "thread_h->full_path/(path + distance_from_root)". 362 | * If path is /home/me/Scripts/foo, and root is the same as above (/home/me/), in the pasted folder we'll have: 363 | * 1) /path/to/pasted/folder/Scripts/, 364 | * 2) /path/to/pasted/folder/Scripts/me, that is exactly what we wanted. 365 | */ 366 | static void cpr(const char *tmp) { 367 | char path[PATH_MAX + 1] = {0}; 368 | 369 | strncpy(path, tmp, PATH_MAX); 370 | distance_from_root = strlen(dirname(path)); 371 | nftw(tmp, recursive_copy, 64, FTW_MOUNT | FTW_PHYS); 372 | } 373 | 374 | static int recursive_copy(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) { 375 | int ret = 0; 376 | char pasted_file[PATH_MAX + 1] = {0}; 377 | 378 | snprintf(pasted_file, PATH_MAX, "%s%s", thread_h->full_path, path + distance_from_root); 379 | if (typeflag == FTW_D) { 380 | mkdir(pasted_file, sb->st_mode); 381 | } else { 382 | int fd_to = open(pasted_file, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, sb->st_mode); 383 | int fd_from = open(path, O_RDONLY); 384 | if ((fd_to != -1) && (fd_from != -1)) { 385 | int len; 386 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,5,0) // if linux >= 4.5 let's use copy_file_range 387 | struct stat stat; 388 | 389 | fstat(fd_from, &stat); 390 | len = stat.st_size; 391 | do { 392 | ret = copy_file_range(fd_from, NULL, fd_to, NULL, len, 0); 393 | if (ret == -1) { 394 | ret = -1; 395 | break; 396 | } 397 | len -= ret; 398 | } while (len > 0); 399 | #else 400 | int buff[BUFF_SIZE]; 401 | len = read(fd_from, buff, sizeof(buff)); 402 | while (len > 0) { 403 | if (write(fd_to, buff, len) != len) { 404 | ret = -1; 405 | break; 406 | } 407 | len = read(fd_from, buff, sizeof(buff)); 408 | } 409 | #endif 410 | } 411 | close(fd_to); 412 | close(fd_from); 413 | } 414 | return (ret == -1) ? ret : 0; 415 | } 416 | 417 | static int recursive_remove(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) { 418 | return remove(path); 419 | } 420 | /* 421 | * Calls recursive_remove starting from the depth: so this func will remove 422 | * files and folders from the bottom -> eg: removing /home/me/Scripts, where scripts has 423 | * {/foo/bar, /acme}, will remove "/acme", then "/foo/bar", and lastly "/foo/". 424 | * It is needed to assure that we remove directories when they're already empty. 425 | */ 426 | static void rmrf(const char *path) { 427 | nftw(path, recursive_remove, 64, FTW_DEPTH | FTW_PHYS | FTW_MOUNT); 428 | } 429 | 430 | /* 431 | * It calculates the time diff since previous call. If it is lower than 0,5s, 432 | * will start from last char of fast_browse_str, otherwise it will start from scratch. 433 | * For every file in current dir, checks if fast_browse_str and current file name are equals for 434 | * at least strlen(fast_browse_str chars). Then moves the cursor to the right name. 435 | * If nothing was found, deletes fast_browse_str. 436 | */ 437 | void fast_browse(wint_t c) { 438 | static struct timeval timer; 439 | static wchar_t fast_browse_str[NAME_MAX + 1] = {0}; 440 | 441 | const int FAST_BROWSE_THRESHOLD = 500000; // 0.5s 442 | const int MILLION = 1000000; 443 | 444 | int i = 0; 445 | char mbstr[NAME_MAX + 1] = {0}; 446 | uint64_t diff = (MILLION * timer.tv_sec) + timer.tv_usec; 447 | 448 | gettimeofday(&timer, NULL); 449 | diff = MILLION * (timer.tv_sec) + timer.tv_usec - diff; 450 | if ((diff < FAST_BROWSE_THRESHOLD) && (wcslen(fast_browse_str))) { 451 | i = ps[active].curr_pos; 452 | } else { 453 | wmemset(fast_browse_str, 0, NAME_MAX + 1); 454 | } 455 | fast_browse_str[wcslen(fast_browse_str)] = c; 456 | wcstombs(mbstr, fast_browse_str, NAME_MAX + 1); 457 | print_info(mbstr, INFO_LINE); 458 | if (!move_cursor_to_file(i, mbstr, active)) { 459 | wmemset(fast_browse_str, 0, NAME_MAX + 1); 460 | } 461 | } 462 | -------------------------------------------------------------------------------- /src/inhibit.c: -------------------------------------------------------------------------------- 1 | #include "../inc/inhibit.h" 2 | 3 | /* 4 | * Thanks elogind project for some hints to improve my old implementation. 5 | * https://github.com/andywingo/elogind/blob/master/src/login/inhibit.c 6 | */ 7 | int inhibit_suspend(const char *str) { 8 | sd_bus *bus; 9 | sd_bus_message *reply = NULL; 10 | sd_bus_error error = SD_BUS_ERROR_NULL; 11 | int r = sd_bus_open_system(&bus); 12 | int fd; 13 | 14 | if (r < 0) { 15 | WARN(strerror(-r)); 16 | goto finish; 17 | } 18 | INFO("calling Inhibit method on bus."); 19 | r = sd_bus_call_method(bus, 20 | "org.freedesktop.login1", 21 | "/org/freedesktop/login1", 22 | "org.freedesktop.login1.Manager", 23 | "Inhibit", 24 | &error, 25 | &reply, 26 | "ssss", 27 | "sleep:idle", 28 | "ncursesFM", 29 | str, 30 | "block"); 31 | if (r < 0) { 32 | WARN(error.message); 33 | goto finish; 34 | } 35 | r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_UNIX_FD, &fd); 36 | if (r < 0) { 37 | WARN(strerror(-r)); 38 | goto finish; 39 | } 40 | r = fcntl(fd, F_DUPFD, 3); 41 | INFO("power management functions inhibition started."); 42 | 43 | finish: 44 | close_bus(&error, reply, bus); 45 | return r; 46 | } 47 | 48 | void close_bus(sd_bus_error *error, sd_bus_message *mess, sd_bus *bus) { 49 | if (mess) { 50 | sd_bus_message_unref(mess); 51 | } 52 | if (error) { 53 | sd_bus_error_free(error); 54 | } 55 | if (bus) { 56 | sd_bus_flush_close_unref(bus); 57 | } 58 | } 59 | 60 | void stop_inhibition(int fd) { 61 | INFO("power management functions inhibition stopped."); 62 | close(fd); 63 | } 64 | -------------------------------------------------------------------------------- /src/install_package.c: -------------------------------------------------------------------------------- 1 | #include "../inc/install_package.h" 2 | 3 | static int match_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error); 4 | 5 | /* 6 | * First of all, creates a transaction; 7 | * then reads the newly created bus path, 8 | * adds a match to the bus to wait until installation is really finished before notifying the user. 9 | * finally, calls InstallFiles method, and processes the bus while finished == 0. 10 | */ 11 | void *install_package(void *str) { 12 | sd_bus_error error = SD_BUS_ERROR_NULL; 13 | sd_bus_message *mess = NULL; 14 | sd_bus *install_bus = NULL; 15 | const char *path; 16 | int r, inhibit_fd = -1, finished = 0; 17 | 18 | r = sd_bus_open_system(&install_bus); 19 | if (r < 0) { 20 | print_and_warn(strerror(-r), ERR_LINE); 21 | goto finish; 22 | } 23 | INFO("calling CreateTransaction on bus."); 24 | r = sd_bus_call_method(install_bus, 25 | "org.freedesktop.PackageKit", 26 | "/org/freedesktop/PackageKit", 27 | "org.freedesktop.PackageKit", 28 | "CreateTransaction", 29 | &error, 30 | &mess, 31 | NULL); 32 | if (r < 0) { 33 | print_and_warn(error.message, ERR_LINE); 34 | goto finish; 35 | } 36 | if (config.inhibit) { 37 | inhibit_fd = inhibit_suspend("Package installation..."); 38 | } 39 | sd_bus_message_read(mess, "o", &path); 40 | r = sd_bus_add_match(install_bus, NULL, "type='signal',interface='org.freedesktop.PackageKit.Transaction',member='Finished'", match_callback, &finished); 41 | if (r < 0) { 42 | print_and_warn(strerror(-r), ERR_LINE); 43 | goto finish; 44 | } 45 | sd_bus_flush(install_bus); 46 | INFO("calling InstallFiles on bus."); 47 | r = sd_bus_call_method(install_bus, 48 | "org.freedesktop.PackageKit", 49 | path, 50 | "org.freedesktop.PackageKit.Transaction", 51 | "InstallFiles", 52 | &error, 53 | NULL, 54 | "tas", 55 | 0, 56 | 1, 57 | (char *)str); 58 | if (r < 0) { 59 | print_and_warn(error.message, ERR_LINE); 60 | goto finish; 61 | } 62 | while (!finished) { 63 | r = sd_bus_process(install_bus, NULL); 64 | if (r > 0) { 65 | continue; 66 | } 67 | r = sd_bus_wait(install_bus, (uint64_t) -1); 68 | if (r < 0) { 69 | break; 70 | } 71 | } 72 | 73 | finish: 74 | if (config.inhibit) { 75 | stop_inhibition(inhibit_fd); 76 | } 77 | close_bus(&error, mess, install_bus); 78 | pthread_detach(pthread_self()); 79 | pthread_exit(NULL); 80 | } 81 | 82 | static int match_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { 83 | unsigned int ret; 84 | #ifdef LIBNOTIFY_PRESENT 85 | char *str; 86 | #endif 87 | 88 | *(int *)userdata = 1; 89 | sd_bus_message_read(m, "u", &ret); 90 | if (ret == 1) { 91 | print_info(_(install_success), INFO_LINE); 92 | #ifdef LIBNOTIFY_PRESENT 93 | str = _(install_success); 94 | #endif 95 | } else { 96 | print_info(_(install_failed), ERR_LINE); 97 | #ifdef LIBNOTIFY_PRESENT 98 | str = _(install_failed); 99 | #endif 100 | } 101 | #ifdef LIBNOTIFY_PRESENT 102 | send_notification(str); 103 | #endif 104 | return 0; 105 | } 106 | -------------------------------------------------------------------------------- /src/log.c: -------------------------------------------------------------------------------- 1 | #include "../inc/log.h" 2 | 3 | static FILE *log_file; 4 | static pthread_mutex_t log_mutex; 5 | 6 | static void log_current_options(void); 7 | 8 | void open_log(void) { 9 | const char log_name[] = "ncursesfm.log"; 10 | 11 | if (config.loglevel != NO_LOG) { 12 | char log_path[PATH_MAX + 1] = {0}; 13 | 14 | snprintf(log_path, PATH_MAX, "%s/.%s", getpwuid(getuid())->pw_dir, log_name); 15 | if (config.persistent_log) { 16 | log_file = fopen(log_path, "a+"); 17 | } else { 18 | log_file = fopen(log_path, "w"); 19 | } 20 | if (log_file) { 21 | pthread_mutex_init(&log_mutex, NULL); 22 | log_current_options(); 23 | } 24 | } 25 | } 26 | 27 | static void log_current_options(void) { 28 | time_t t; 29 | struct tm tm; 30 | 31 | t = time(NULL); 32 | tm = *localtime(&t); 33 | fprintf(log_file, "%d-%d-%d %02d:%02d:%02d\n\n", tm.tm_year + 1900, tm.tm_mon + 1, 34 | tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); 35 | fprintf(log_file, "NcursesFM %s\n", VERSION); 36 | fprintf(log_file, "\nBuild options:\n"); 37 | fprintf(log_file, "* CONFDIR: %s\n", CONFDIR); 38 | fprintf(log_file, "* BINDIR: %s\n", BINDIR); 39 | fprintf(log_file, "* LOCALEDIR: %s\n", LOCALEDIR); 40 | fprintf(log_file, "* LIBX11_PRESENT: "); 41 | #ifdef LIBX11_PRESENT 42 | fprintf(log_file, "true\n"); 43 | #else 44 | fprintf(log_file, "false\n"); 45 | #endif 46 | fprintf(log_file, "* LIBCUPS_PRESENT: "); 47 | #ifdef LIBCUPS_PRESENT 48 | fprintf(log_file, "true\n"); 49 | #else 50 | fprintf(log_file, "false\n"); 51 | #endif 52 | fprintf(log_file, "\nStarting options:\n"); 53 | fprintf(log_file, "* Editor: %s\n", config.editor); 54 | fprintf(log_file, "* Starting directory: %s\n", config.starting_dir); 55 | fprintf(log_file, "* Second tab starting dir: %d\n", config.second_tab_starting_dir); 56 | fprintf(log_file, "* Inhibition: %d\n", config.inhibit); 57 | fprintf(log_file, "* Automount: %d\n", config.automount); 58 | fprintf(log_file, "* Starting with helper window: %d\n", config.starting_helper); 59 | fprintf(log_file, "* Log level: %d\n", config.loglevel); 60 | fprintf(log_file, "* Log persistency: %d\n", config.persistent_log); 61 | fprintf(log_file, "* Low battery threshold: %d\n", config.bat_low_level); 62 | fprintf(log_file, "* Cursor chars: \"%ls\"\n", config.cursor_chars); 63 | fprintf(log_file, "* Sysinfo layout: \"%s\"\n", config.sysinfo_layout); 64 | fprintf(log_file, "* Safe level: %d\n\n", config.safe); 65 | } 66 | 67 | void log_message(const char *filename, int lineno, const char *funcname, 68 | const char *log_msg, char type, int log_level) { 69 | pid_t pid; 70 | time_t t; 71 | 72 | if ((log_file) && (log_level <= config.loglevel)) { 73 | t = time(NULL); 74 | struct tm tm = *localtime(&t); 75 | pid = syscall(SYS_gettid); 76 | pthread_mutex_lock(&log_mutex); 77 | fprintf(log_file, "(%c) thread: %d, %02d:%02d:%02d, %-50s%s:%d (%s)\n", 78 | type, pid, tm.tm_hour, tm.tm_min, tm.tm_sec, log_msg, filename, lineno, funcname); 79 | fflush(log_file); 80 | pthread_mutex_unlock(&log_mutex); 81 | } 82 | } 83 | 84 | void close_log(void) { 85 | if (log_file) { 86 | pthread_mutex_lock(&log_mutex); 87 | fclose(log_file); 88 | pthread_mutex_unlock(&log_mutex); 89 | pthread_mutex_destroy(&log_mutex); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | /* BEGIN_COMMON_COPYRIGHT_HEADER 2 | * 3 | * NcursesFM: file manager in C with ncurses UI for linux. 4 | * https://github.com/FedeDP/ncursesFM 5 | * 6 | * Copyright (C) 2016 Federico Di Pierro 7 | * 8 | * This file is part of ncursesFM. 9 | * ncursesFM is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program. If not, see . 21 | * 22 | * END_COMMON_COPYRIGHT_HEADER */ 23 | 24 | #include "../inc/bookmarks.h" 25 | #include "../inc/config.h" 26 | 27 | static void locale_init(void); 28 | static int set_signals(void); 29 | static void set_pollfd(void); 30 | static void sigsegv_handler(int signum); 31 | static void check_desktop(void); 32 | static void helper_function(int argc, char * const argv[]); 33 | static void main_loop(void); 34 | static void add_new_tab(void); 35 | static void check_device_mode(void); 36 | static void manage_enter(struct stat current_file_stat); 37 | static void manage_enter_search(struct stat current_file_stat); 38 | static void manage_space(const char *str); 39 | static void manage_quit(void); 40 | static void switch_search(void); 41 | static void check_remove(void (*f)(void)); 42 | static int check_init(int index); 43 | static int check_access(void); 44 | static void go_root_dir(void); 45 | 46 | /* 47 | * pointers to long_file_operations functions, used in main loop; 48 | */ 49 | static int (*const long_func[LONG_FILE_OPERATIONS])(void) = { 50 | move_file, paste_file, remove_file, create_archive, extract_file 51 | }; 52 | 53 | int main(int argc, char * const argv[]) 54 | { 55 | locale_init(); 56 | helper_function(argc, argv); 57 | load_config_files(); 58 | parse_cmd(argc, argv); 59 | open_log(); 60 | config_checks(); 61 | get_bookmarks(); 62 | set_pollfd(); 63 | init_job_queue(); 64 | #ifdef LIBNOTIFY_PRESENT 65 | init_notify(); 66 | #endif 67 | if (!quit) { 68 | screen_init(); 69 | main_loop(); 70 | } 71 | program_quit(); 72 | } 73 | 74 | static void locale_init(void) { 75 | setlocale(LC_ALL, ""); 76 | if (strcmp(nl_langinfo(CODESET), "UTF-8")) { 77 | fprintf(stderr, "Please use an utf8 locale.\n"); 78 | } 79 | bindtextdomain("ncursesFM", LOCALEDIR); 80 | textdomain("ncursesFM"); 81 | } 82 | 83 | static int set_signals(void) { 84 | sigset_t mask; 85 | 86 | // when receiving segfault signal, 87 | // call our sigsegv handler that just logs 88 | // a debug message before dying 89 | signal(SIGSEGV, sigsegv_handler); 90 | 91 | sigemptyset(&mask); 92 | sigaddset(&mask, SIGINT); 93 | sigaddset(&mask, SIGTERM); 94 | sigprocmask(SIG_BLOCK, &mask, NULL); 95 | 96 | return signalfd(-1, &mask, 0); 97 | } 98 | 99 | static void set_pollfd(void) { 100 | #if ARCHIVE_VERSION_NUMBER >= 3002000 101 | nfds = 7; 102 | #else 103 | nfds = 6; 104 | #endif 105 | nfds++; 106 | 107 | main_p = malloc(nfds * sizeof(struct pollfd)); 108 | main_p[GETCH_IX] = (struct pollfd) { 109 | .fd = STDIN_FILENO, 110 | .events = POLLIN, 111 | }; 112 | 113 | // do not start timer if no sysinfo info is enabled 114 | if (strlen(config.sysinfo_layout)) { 115 | main_p[TIMER_IX] = (struct pollfd) { 116 | .fd = start_timer(), 117 | .events = POLLIN, 118 | }; 119 | } 120 | 121 | // inotify watcher init for each tab 122 | ps[0].inot.fd = inotify_init(); 123 | ps[1].inot.fd = inotify_init(); 124 | main_p[INOTIFY_IX1] = (struct pollfd) { 125 | .fd = ps[0].inot.fd, 126 | .events = POLLIN, 127 | }; 128 | main_p[INOTIFY_IX2] = (struct pollfd) { 129 | .fd = ps[1].inot.fd, 130 | .events = POLLIN, 131 | }; 132 | 133 | // info init. This is needed to let 134 | // multiple threads print an information string 135 | // without any issue. 136 | pipe(info_fd); 137 | main_p[INFO_IX] = (struct pollfd) { 138 | .fd = info_fd[0], 139 | .events = POLLIN, 140 | }; 141 | 142 | // signalfd init to catch external signals 143 | main_p[SIGNAL_IX] = (struct pollfd) { 144 | .fd = set_signals(), 145 | .events = POLLIN, 146 | }; 147 | 148 | #if ARCHIVE_VERSION_NUMBER >= 3002000 149 | // NONBLOCK needed for EXTRACTOR_TH workaround when blocked 150 | // inside a eventf read -> archive_cb_fd[0] is fd read by main_poll 151 | // this way it will return -1 if no data is waiting to be read. 152 | archive_cb_fd[0] = eventfd(0, EFD_NONBLOCK); 153 | archive_cb_fd[1] = eventfd(0, 0); 154 | main_p[ARCHIVE_IX] = (struct pollfd) { 155 | .fd = archive_cb_fd[0], 156 | .events = POLLIN, 157 | }; 158 | #endif 159 | 160 | // device monitor watcher 161 | main_p[DEVMON_IX] = (struct pollfd) { 162 | .fd = start_monitor(), 163 | .events = POLLIN, 164 | }; 165 | } 166 | 167 | /* 168 | * If received a sigsegv, only log a message then 169 | * set sigsegv signal handler to default (SIG_DFL), 170 | * and send again the signal to the process. 171 | */ 172 | static void sigsegv_handler(int signum) { 173 | ERROR("received sigsegv signal. Aborting."); 174 | close_log(); 175 | signal(signum, SIG_DFL); 176 | kill(getpid(), signum); 177 | } 178 | 179 | /* 180 | * Check if ncursesFM is run by desktop environment (X or wayland) 181 | * or from a getty 182 | */ 183 | static void check_desktop(void) { 184 | has_desktop = getenv("XDG_SESSION_TYPE") != NULL; 185 | } 186 | 187 | static void helper_function(int argc, char * const argv[]) { 188 | if ((argc > 1) && (!strcmp(argv[1], "--help"))) { 189 | printf("\n NcursesFM\n"); 190 | printf(" Version: %s\n", VERSION); 191 | printf("\n Copyright (C) 2016 Federico Di Pierro (https://github.com/FedeDP):\n"); 192 | printf(" This program comes with ABSOLUTELY NO WARRANTY;\n"); 193 | printf(" This is free software, and you are welcome to redistribute it under certain conditions;\n"); 194 | printf(" It is GPL licensed. Have a look at COPYING file.\n\n"); 195 | printf("\tIt supports following cmdline options (they will override conf file settings):\n"); 196 | printf("\t* --editor /path/to/editor to set an editor for current session. Fallbacks to $EDITOR env var.\n"); 197 | printf("\t* --starting_dir /path/to/dir to set a starting directory for current session. Defaults to current dir.\n"); 198 | printf("\t* --helper_win {0,1} to switch (off,on) starting helper message. Defaults to 1.\n"); 199 | printf("\t* --inhibit {0,1} to switch {off,on} powermanagement functions while a job is being processed. Defaults to 0.\n"); 200 | printf("\t* --automount {0,1} to switch {off,on} automounting of external drives/usb sticks. Defaults to 0.\n"); 201 | printf("\t* --loglevel {0,1,2,3} to change loglevel. Defaults to 0.\n"); 202 | printf("\t\t* 0 to log only errors.\n\t\t* 1 to log warn messages and errors.\n"); 203 | printf("\t\t* 2 to log info messages too.\n\t\t* 3 to disable logs.\n"); 204 | printf("\t* --persistent_log {0,1} to switch {off,on} persistent logs across program restarts. Defaults to 0.\n"); 205 | printf("\t* --low_level {$level} to set low battery signal's threshold. Defaults to 15%%.\n"); 206 | printf("\t* --safe {0,1,2} to change safety level. Defaults to 2.\n"); 207 | printf("\t\t* 0 don't ask ay confirmation.\n"); 208 | printf("\t\t* 1 ask confirmation for file remotions/packages installs/printing files.\n"); 209 | printf("\t\t* 2 ask confirmation for every action.\n\n"); 210 | printf(" Have a look at /etc/default/ncursesFM.conf to set your global defaults.\n"); 211 | printf(" You can copy default conf file to $HOME/.config/ncursesFM.conf to set your user defaults.\n"); 212 | printf(" Just use arrow keys to move up and down, and enter to change directory or open a file.\n"); 213 | printf(" Press 'L' while in program to view a more detailed helper message.\n\n"); 214 | exit(EXIT_SUCCESS); 215 | } 216 | 217 | /* some default values */ 218 | config.starting_helper = 1; 219 | config.bat_low_level = 15; 220 | config.safe = FULL_SAFE; 221 | device_init = DEVMON_STARTING; 222 | wcscpy(config.cursor_chars, L"->"); 223 | /* 224 | * default sysinfo layout: 225 | * Clock 226 | * Process monitor 227 | * Battery monitor 228 | */ 229 | strcpy(config.sysinfo_layout, "CPB"); 230 | check_desktop(); 231 | } 232 | 233 | /* 234 | * When in fast_browse_mode do not enter switch case; 235 | * if device_mode or search_mode are active on current window, 236 | * only 'q', 'l', or 't' (and enter, that is not printable char) can be called. 237 | * else stat current file and enter switch case. 238 | */ 239 | static void main_loop(void) { 240 | int index; 241 | char *ptr; 242 | 243 | /* 244 | * x to move, 245 | * v to paste, 246 | * r to remove, 247 | * b to compress, 248 | * z to extract 249 | */ 250 | const char long_table[] = "xvrbz"; 251 | 252 | /* 253 | * n, d to create new file/dir 254 | * o to rename. 255 | */ 256 | const char short_table[] = "ndo"; 257 | 258 | /* 259 | * l switch helper_win, 260 | * t new tab, 261 | * m only in device_mode to {un}mount device, 262 | * r in bookmarks_mode/selected mode to remove file from bookmarks/selected. 263 | * s to show stat 264 | * i to trigger fullname win 265 | */ 266 | const char special_mode_allowed_chars[] = "ltmrsi"; 267 | 268 | /* 269 | * Not graphical wchars: 270 | * arrow KEYS, needed to move cursor. 271 | * KEY_RESIZE to catch resize signals. 272 | * PG_UP/DOWN to move straight to first/last file. 273 | * KEY_MOUSE to catch mouse events 274 | * 32 -> space to select files 275 | * 127, backspace to go up to root folder 276 | */ 277 | wchar_t not_graph_wchars[12]; 278 | swprintf(not_graph_wchars, 12, L"%lc%lc%lc%lc%lc%lc%lc%lc%lc%lc%lc", KEY_UP, KEY_DOWN, KEY_RIGHT, 279 | KEY_LEFT, KEY_RESIZE, KEY_PPAGE, 280 | KEY_NPAGE, KEY_MOUSE, (unsigned long)32, 281 | (unsigned long)127, KEY_BACKSPACE); 282 | 283 | MEVENT event; 284 | #if NCURSES_MOUSE_VERSION > 1 285 | mousemask(BUTTON1_RELEASED | BUTTON2_RELEASED | BUTTON3_RELEASED | BUTTON4_PRESSED | BUTTON5_PRESSED, NULL); 286 | #else 287 | mousemask(BUTTON1_RELEASED | BUTTON2_RELEASED | BUTTON3_RELEASED, NULL); 288 | #endif 289 | 290 | while (!quit) { 291 | wint_t c = main_poll(ps[active].mywin.fm); 292 | if ((ps[active].mode == fast_browse_) && iswgraph(c) && !wcschr(not_graph_wchars, c)) { 293 | fast_browse(c); 294 | continue; 295 | } 296 | c = tolower(c); 297 | if (ps[active].mode > fast_browse_ && (isprint(c) && !strchr(special_mode_allowed_chars, c))) { 298 | continue; 299 | } 300 | struct stat current_file_stat = {0}; 301 | stat(str_ptr[active][ps[active].curr_pos], ¤t_file_stat); 302 | switch (c) { 303 | case KEY_UP: 304 | scroll_up(active, 1); 305 | break; 306 | case KEY_DOWN: 307 | scroll_down(active, 1); 308 | break; 309 | case KEY_RIGHT: 310 | case KEY_LEFT: 311 | if (cont == MAX_TABS) { 312 | change_tab(); 313 | } 314 | break; 315 | case KEY_PPAGE: 316 | scroll_up(active, ps[active].curr_pos); 317 | break; 318 | case KEY_NPAGE: 319 | scroll_down(active, ps[active].number_of_files - ps[active].curr_pos); 320 | break; 321 | case 127: case KEY_BACKSPACE: // backspace to go to root folder 322 | if (ps[active].mode <= fast_browse_) { 323 | go_root_dir(); 324 | } 325 | break; 326 | case 'h': // h to show hidden files 327 | switch_hidden(); 328 | break; 329 | case 10: // enter to change dir or open a file. 330 | manage_enter(current_file_stat); 331 | break; 332 | case 't': // t to open second tab 333 | if (cont < MAX_TABS) { 334 | add_new_tab(); 335 | change_tab(); 336 | } 337 | break; 338 | case 'w': // w to close second tab 339 | if (active) { 340 | cont--; 341 | delete_tab(active); 342 | resize_tab(!active, 0); 343 | change_tab(); 344 | } 345 | break; 346 | case 32: // space to select files 347 | manage_space(str_ptr[active][ps[active].curr_pos]); 348 | break; 349 | case 'l': // show helper mess 350 | trigger_show_helper_message(); 351 | break; 352 | case 's': // show stat about files (size and perms) 353 | trigger_stats(); 354 | break; 355 | case 'e': // add file to bookmarks 356 | add_file_to_bookmarks(str_ptr[active][ps[active].curr_pos]); 357 | break; 358 | case 'f': // f to search 359 | switch_search(); 360 | break; 361 | #ifdef LIBCUPS_PRESENT 362 | case 'p': // p to print 363 | if ((S_ISREG(current_file_stat.st_mode)) && !(current_file_stat.st_mode & S_IXUSR)) { 364 | print_support(str_ptr[active][ps[active].curr_pos]); 365 | } 366 | break; 367 | #endif 368 | case 'm': // m to mount/unmount fs 369 | check_device_mode(); 370 | break; 371 | case ',': // , to enable fast browse mode 372 | show_special_tab(ps[active].number_of_files, NULL, ps[active].title, fast_browse_); 373 | break; 374 | case 27: /* ESC to exit/leave special mode */ 375 | manage_quit(); 376 | break; 377 | case KEY_RESIZE: 378 | resize_win(); 379 | break; 380 | case 9: // TAB to change sorting function 381 | if (ps[active].mode <= fast_browse_) { 382 | change_sort(); 383 | } 384 | break; 385 | case 'i': // i to view current file fullname (in case it is too long) 386 | trigger_fullname_win(); 387 | break; 388 | case 'n': case 'd': case 'o': // fast operations do not require another thread. 389 | if (check_access()) { 390 | ptr = strchr(short_table, c); 391 | index = SHORT_FILE_OPERATIONS - strlen(ptr); 392 | fast_file_operations(index); 393 | } 394 | break; 395 | case 'g': // g to show bookmarks 396 | show_bookmarks(); 397 | break; 398 | case 'k': // k to show selected files 399 | show_selected(); 400 | break; 401 | case KEY_DC: // del to delete all selected files in selected mode/ all user bookmarks in bookmark mode 402 | if (ps[active].mode == bookmarks_) { 403 | check_remove(remove_all_user_bookmarks); 404 | } else if (ps[active].mode == selected_) { 405 | check_remove(remove_all_selected); 406 | } 407 | break; 408 | case KEY_MOUSE: 409 | if(getmouse(&event) == OK) { 410 | if (event.bstate & BUTTON1_RELEASED) { 411 | /* left click will send an enter event */ 412 | manage_enter(current_file_stat); 413 | } else if (event.bstate & BUTTON2_RELEASED) { 414 | /* middle click will send a space event */ 415 | manage_space(str_ptr[active][ps[active].curr_pos]); 416 | } else if (event.bstate & BUTTON3_RELEASED) { 417 | /* right click will send a back to root dir event */ 418 | if (ps[active].mode <= fast_browse_) { 419 | go_root_dir(); 420 | } 421 | } 422 | /* scroll up and down events associated with mouse wheel */ 423 | #if NCURSES_MOUSE_VERSION > 1 424 | else if (event.bstate & BUTTON4_PRESSED) { 425 | scroll_up(active, 1); 426 | } else if (event.bstate & BUTTON5_PRESSED) { 427 | scroll_down(active, 1); 428 | } 429 | #endif 430 | } 431 | break; 432 | default: 433 | ptr = strchr(long_table, c); 434 | if (ptr) { 435 | if (ps[active].mode == normal) { 436 | index = LONG_FILE_OPERATIONS - strlen(ptr); 437 | if (check_init(index)) { 438 | init_thread(index, long_func[index]); 439 | } 440 | // in mode != normal, only 'r' to remove is accepted while in bookmarks/selected mode 441 | } else if (ps[active].mode == bookmarks_) { 442 | remove_bookmark_from_file(); 443 | } else if (ps[active].mode == selected_) { 444 | check_remove(remove_selected); 445 | } 446 | } 447 | break; 448 | } 449 | } 450 | } 451 | 452 | static void add_new_tab(void) { 453 | cont++; 454 | resize_tab(0, 0); 455 | new_tab(cont - 1); 456 | } 457 | 458 | static void check_device_mode(void) { 459 | if (device_init == DEVMON_STARTING) { 460 | print_info(_(polling), INFO_LINE); 461 | } else if (device_init == DEVMON_READY && ps[active].mode == normal) { 462 | show_devices_tab(); 463 | } else if (device_init == DEVMON_OFF) { 464 | print_info(_(monitor_err), INFO_LINE); 465 | } else if (ps[active].mode == device_) { 466 | manage_mount_device(); 467 | } 468 | } 469 | 470 | static void manage_enter(struct stat current_file_stat) { 471 | if (ps[active].mode == search_) { 472 | manage_enter_search(current_file_stat); 473 | } 474 | else if (ps[active].mode == device_) { 475 | manage_enter_device(); 476 | } 477 | else if (ps[active].mode == bookmarks_) { 478 | manage_enter_bookmarks(current_file_stat); 479 | } else if (ps[active].mode == selected_) { 480 | leave_mode_helper(current_file_stat); 481 | } else if (S_ISDIR(current_file_stat.st_mode)) { 482 | change_dir(str_ptr[active][ps[active].curr_pos], active); 483 | } else { 484 | manage_file(str_ptr[active][ps[active].curr_pos]); 485 | } 486 | } 487 | 488 | static void manage_enter_search(struct stat current_file_stat) { 489 | char *str = NULL; 490 | char path[PATH_MAX + 1] = {0}; 491 | 492 | strncpy(path, sv.found_searched[ps[active].curr_pos], PATH_MAX); 493 | if (!S_ISDIR(current_file_stat.st_mode)) { 494 | int index = search_enter_press(path); 495 | /* save in str current file's name */ 496 | str = path + index + 1; 497 | /* check if this file was an archive and cut useless path inside archive */ 498 | char *ptr = strchr(str, '/'); 499 | if (ptr) { 500 | str[strlen(str) - strlen(ptr)] = '\0'; 501 | } 502 | strncpy(ps[active].old_file, str, NAME_MAX); 503 | path[index] = '\0'; 504 | } else { 505 | memset(ps[active].old_file, 0, strlen(ps[active].old_file)); 506 | } 507 | leave_search_mode(path); 508 | } 509 | 510 | static void manage_space(const char *str) { 511 | if (ps[active].mode > fast_browse_) { 512 | return; 513 | } 514 | 515 | int all = !strcmp(strrchr(str, '/') + 1, ".."); 516 | 517 | if (all) { 518 | manage_all_space_press(); 519 | } else { 520 | manage_space_press(str); 521 | } 522 | } 523 | 524 | static void manage_quit(void) { 525 | if (ps[active].mode == search_) { 526 | leave_search_mode(ps[active].my_cwd); 527 | } else if (ps[active].mode > fast_browse_) { 528 | leave_special_mode(ps[active].my_cwd, active); 529 | } else if (ps[active].mode == fast_browse_) { 530 | leave_special_mode(NULL, active); 531 | print_info("", INFO_LINE); // clear fast browse string from info line 532 | } else { 533 | quit = NORM_QUIT; 534 | } 535 | } 536 | 537 | static void switch_search(void) { 538 | if (sv.searching == NO_SEARCH) { 539 | search(); 540 | } else if (sv.searching == SEARCHING) { 541 | print_info(_(already_searching), INFO_LINE); 542 | } else if (sv.searching == SEARCHED) { 543 | list_found(); 544 | } 545 | } 546 | 547 | static void check_remove(void (*f)(void)) { 548 | char c; 549 | ask_user(_(sure), &c, 1); 550 | if (c != _(yes)[0]) { 551 | return; 552 | } 553 | f(); 554 | } 555 | 556 | static int check_init(int index) { 557 | char x; 558 | 559 | if (!selected) { 560 | print_info(_(no_selected_files), ERR_LINE); 561 | return 0; 562 | } 563 | if (index == EXTRACTOR_TH && config.safe == FULL_SAFE) { 564 | ask_user(_(extr_question), &x, 1); 565 | if (x == _(no)[0] || x == 27) { 566 | return 0; 567 | } 568 | } 569 | if (index != RM_TH) { 570 | return check_access(); 571 | } 572 | if (config.safe != UNSAFE) { 573 | ask_user(_(sure), &x, 1); 574 | if (x != _(yes)[0]) { 575 | return 0; 576 | } 577 | } 578 | return 1; 579 | } 580 | 581 | static int check_access(void) { 582 | if (access(ps[active].my_cwd, W_OK) == -1) { 583 | print_info(strerror(errno), ERR_LINE); 584 | return 0; 585 | } 586 | return 1; 587 | } 588 | 589 | static void go_root_dir(void) { 590 | char root[PATH_MAX + 1] = {0}; 591 | 592 | strncpy(root, ps[active].my_cwd, PATH_MAX); 593 | strcat(root, "/.."); 594 | change_dir(root, active); 595 | } 596 | -------------------------------------------------------------------------------- /src/notify.c: -------------------------------------------------------------------------------- 1 | #ifdef LIBNOTIFY_PRESENT 2 | 3 | #include "../inc/notify.h" 4 | 5 | static NotifyNotification *n = NULL; 6 | static const char title[] = "NcursesFM"; 7 | 8 | void init_notify(void) { 9 | notify_init(title); 10 | } 11 | 12 | void send_notification(const char *mesg) { 13 | if (!config.silent && has_desktop) { 14 | n = notify_notification_new(title, mesg, 0); 15 | notify_notification_set_timeout(n, NOTIFY_EXPIRES_DEFAULT); 16 | notify_notification_show(n, 0); 17 | } 18 | } 19 | 20 | void destroy_notify(void) { 21 | if (n) { 22 | notify_notification_close(n, NULL); 23 | } 24 | notify_uninit(); 25 | } 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /src/print.c: -------------------------------------------------------------------------------- 1 | #ifdef LIBCUPS_PRESENT 2 | 3 | #include "../inc/print.h" 4 | 5 | static void print_file(const char *filename); 6 | 7 | void print_support(const char *str) { 8 | char c; 9 | 10 | if (config.safe != UNSAFE) { 11 | ask_user(_(print_question), &c, 1); 12 | if (c != _(yes)[0]) { 13 | return; 14 | } 15 | } 16 | print_file(str); 17 | } 18 | 19 | /* 20 | * If there are cups printers available, take the default printer and print the file. 21 | */ 22 | static void print_file(const char *filename) { 23 | cups_dest_t *dests; 24 | #ifdef LIBNOTIFY_PRESENT 25 | const char *str; 26 | #endif 27 | int num_dests = cupsGetDests(&dests); 28 | 29 | if (num_dests > 0) { 30 | cups_dest_t *default_dest = cupsGetDest(NULL, NULL, num_dests, dests); 31 | int r = cupsPrintFile(default_dest->name, filename, "ncursesFM job", 32 | default_dest->num_options, default_dest->options); 33 | if (r) { 34 | print_info(_(print_ok), INFO_LINE); 35 | #ifdef LIBNOTIFY_PRESENT 36 | str = _(print_ok); 37 | #endif 38 | } else { 39 | print_info(ippErrorString(cupsLastError()), ERR_LINE); 40 | #ifdef LIBNOTIFY_PRESENT 41 | str = ippErrorString(cupsLastError()); 42 | #endif 43 | } 44 | } else { 45 | print_info(_(print_fail), ERR_LINE); 46 | #ifdef LIBNOTIFY_PRESENT 47 | str = _(print_fail); 48 | #endif 49 | } 50 | 51 | #ifdef LIBNOTIFY_PRESENT 52 | send_notification(str); 53 | #endif 54 | } 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /src/quit.c: -------------------------------------------------------------------------------- 1 | #include "../inc/quit.h" 2 | 3 | static void free_everything(void); 4 | static void quit_thread_func(void); 5 | static void quit_worker_th(void); 6 | static void quit_install_th(void); 7 | static void quit_search_th(void); 8 | static void close_fds(void); 9 | 10 | int program_quit(void) { 11 | free_everything(); 12 | screen_end(); 13 | close_fds(); 14 | quit_thread_func(); 15 | destroy_job_queue(); 16 | #ifdef LIBNOTIFY_PRESENT 17 | destroy_notify(); 18 | #endif 19 | if (quit == MEM_ERR_QUIT || quit == GENERIC_ERR_QUIT) { 20 | fprintf(stderr, "%s\n", generic_error); 21 | ERROR("program exited with errors."); 22 | close_log(); 23 | exit(EXIT_FAILURE); 24 | } 25 | INFO("program exited without errors."); 26 | close_log(); 27 | exit(EXIT_SUCCESS); 28 | } 29 | 30 | static void free_everything(void) { 31 | free_device_monitor(); 32 | free_timer(); 33 | free(main_p); 34 | free_selected(); 35 | free_bookmarks(); 36 | } 37 | 38 | static void quit_thread_func(void) { 39 | quit_worker_th(); 40 | quit_install_th(); 41 | quit_search_th(); 42 | } 43 | 44 | static void quit_worker_th(void) { 45 | if ((worker_th) && (pthread_kill(worker_th, 0) != ESRCH)) { 46 | INFO(quit_with_running_thread); 47 | printf("%s\n", quit_with_running_thread); 48 | pthread_join(worker_th, NULL); 49 | INFO("worker th exited without errors."); 50 | printf("Jobs queue ended.\n"); 51 | } 52 | } 53 | 54 | static void quit_install_th(void) { 55 | if ((install_th) && (pthread_kill(install_th, 0) != ESRCH)) { 56 | printf("%s\n", install_th_wait); 57 | INFO(install_th_wait); 58 | pthread_join(install_th, NULL); 59 | printf("Installation finished.\n"); 60 | INFO("package installation finished. Leaving."); 61 | } 62 | } 63 | 64 | static void quit_search_th(void) { 65 | if ((search_th) && (pthread_kill(search_th, 0) != ESRCH)) { 66 | INFO("waiting for search thread to leave..."); 67 | pthread_join(search_th, NULL); 68 | INFO("search th left."); 69 | } 70 | } 71 | 72 | static void close_fds(void) { 73 | close(ps[0].inot.fd); 74 | close(ps[1].inot.fd); 75 | close(info_fd[0]); 76 | close(info_fd[1]); 77 | #if ARCHIVE_VERSION_NUMBER >= 3002000 78 | close(archive_cb_fd[0]); 79 | close(archive_cb_fd[1]); 80 | #endif 81 | } 82 | -------------------------------------------------------------------------------- /src/search.c: -------------------------------------------------------------------------------- 1 | #include "../inc/search.h" 2 | 3 | static int recursive_search(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf); 4 | static int search_inside_archive(const char *path); 5 | static void *search_thread(void *x); 6 | 7 | void search(void) { 8 | ask_user(_(search_insert_name), sv.searched_string, 20); 9 | if (strlen(sv.searched_string) < 5 || sv.searched_string[0] == 27) { 10 | if (strlen(sv.searched_string) > 0 && sv.searched_string[0] != 27) { 11 | print_info(_(searched_string_minimum), ERR_LINE); 12 | } 13 | } else { 14 | char c; 15 | 16 | sv.found_cont = 0; 17 | sv.search_archive = 0; 18 | sv.search_lazy = 0; 19 | ask_user(_(search_archives), &c, 1); 20 | if (c == 27) { 21 | return; 22 | } 23 | if (c == _(yes)[0]) { 24 | sv.search_archive = 1; 25 | } 26 | /* 27 | * Don't ask user if he wants a lazy search 28 | * if he's searching a hidden file as 29 | * lazy search won't search hidden files. 30 | */ 31 | if (sv.searched_string[0] != '.') { 32 | ask_user(_(lazy_search), &c, 1); 33 | if (c == 27) { 34 | return; 35 | } 36 | if (c == _(yes)[0]) { 37 | sv.search_lazy = 1; 38 | } 39 | } 40 | sv.searching = SEARCHING; 41 | print_info("", SEARCH_LINE); 42 | pthread_create(&search_th, NULL, search_thread, NULL); 43 | } 44 | } 45 | 46 | static int recursive_search(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) { 47 | char *fixed_str; 48 | int len, r = 0, ret = FTW_CONTINUE; 49 | /* 50 | * if searching "pippo" from "/home/pippo", 51 | * avoid saving "/home/pippo" as a result. 52 | */ 53 | if (ftwbuf->level == 0) { 54 | return ret; 55 | } 56 | fixed_str = strrchr(path, '/') + 1; 57 | /* 58 | * if lazy: avoid checking hidden files and 59 | * inside hidden folders 60 | */ 61 | if (sv.search_lazy && fixed_str[0] == '.') { 62 | return FTW_SKIP_SUBTREE; 63 | } 64 | if ((sv.search_archive) && (is_ext(fixed_str, arch_ext, NUM(arch_ext)))) { 65 | return search_inside_archive(path); 66 | } 67 | len = strlen(sv.searched_string); 68 | if (!sv.search_lazy) { 69 | r = !strncmp(fixed_str, sv.searched_string, len); 70 | } else if (strcasestr(fixed_str, sv.searched_string)) { 71 | r = 1; 72 | } 73 | if (r) { 74 | strncpy(sv.found_searched[sv.found_cont], path, PATH_MAX); 75 | sv.found_cont++; 76 | if (sv.found_cont == MAX_NUMBER_OF_FOUND) { 77 | ret = FTW_STOP; 78 | } 79 | } 80 | return quit ? FTW_STOP : ret; 81 | } 82 | 83 | /* 84 | * For each entry in the archive, it checks "entry + len" pointer against searched string. 85 | * Len is always the offset of the current dir inside archive, eg: foo.tgz/bar/x, 86 | * while checking x, len will be strlen("bar/") 87 | */ 88 | static int search_inside_archive(const char *path) { 89 | int ret = FTW_CONTINUE, string_length; 90 | struct archive_entry *entry; 91 | struct archive *a = archive_read_new(); 92 | 93 | archive_read_support_filter_all(a); 94 | archive_read_support_format_all(a); 95 | string_length = strlen(sv.searched_string); 96 | if ((a) && (archive_read_open_filename(a, path, BUFF_SIZE) == ARCHIVE_OK)) { 97 | while ((!quit) && (ret == FTW_CONTINUE) && (archive_read_next_header(a, &entry) == ARCHIVE_OK)) { 98 | int len = 0, r = 0; 99 | 100 | if (!sv.search_lazy) { 101 | r = !strncmp(archive_entry_pathname(entry) + len, sv.searched_string, string_length); 102 | } else if (strcasestr(archive_entry_pathname(entry) + len, sv.searched_string)) { 103 | r = 1; 104 | } 105 | if (r) { 106 | snprintf(sv.found_searched[sv.found_cont], PATH_MAX, "%s/%s", path, archive_entry_pathname(entry)); 107 | sv.found_cont++; 108 | if (sv.found_cont == MAX_NUMBER_OF_FOUND) { 109 | ret = FTW_STOP; 110 | } 111 | } 112 | char *ptr = strrchr(archive_entry_pathname(entry), '/'); 113 | if ((ptr) && (strlen(ptr) == 1)) { 114 | len = strlen(archive_entry_pathname(entry)); 115 | } 116 | } 117 | } 118 | archive_read_free(a); 119 | return quit ? FTW_STOP : ret; 120 | } 121 | 122 | static void *search_thread(void *x) { 123 | INFO("starting recursive search..."); 124 | nftw(ps[active].my_cwd, recursive_search, 64, FTW_MOUNT | FTW_PHYS | FTW_ACTIONRETVAL); 125 | if (!quit) { 126 | char str[100]; 127 | 128 | INFO("ended recursive search"); 129 | if ((sv.found_cont == MAX_NUMBER_OF_FOUND) || (sv.found_cont == 0)) { 130 | sv.searching = NO_SEARCH; 131 | if (sv.found_cont == MAX_NUMBER_OF_FOUND) { 132 | print_info(_(too_many_found), INFO_LINE); 133 | strncpy(str, _(too_many_found), 100); 134 | } else { 135 | strncpy(str, _(no_found), 100); 136 | print_info(_(no_found), INFO_LINE); 137 | } 138 | } else { 139 | sv.searching = SEARCHED; 140 | snprintf(str, 100, "Search finished, %d files found.", sv.found_cont); 141 | } 142 | print_info("", SEARCH_LINE); 143 | #ifdef LIBNOTIFY_PRESENT 144 | send_notification(str); 145 | #endif 146 | } 147 | pthread_detach(pthread_self()); 148 | pthread_exit(NULL); 149 | } 150 | 151 | void list_found(void) { 152 | char str[100]; 153 | 154 | sprintf(str, _(search_mode_str), sv.found_cont, sv.searched_string); 155 | show_special_tab(sv.found_cont, sv.found_searched, str, search_); 156 | print_info("", SEARCH_LINE); 157 | } 158 | 159 | void leave_search_mode(const char *str) { 160 | if (ps[!active].mode != search_) { 161 | sv.searching = NO_SEARCH; 162 | print_info("", SEARCH_LINE); 163 | } 164 | leave_special_mode(str, active); 165 | } 166 | 167 | /* 168 | * While in search mode, enter will switch to current highlighted file's dir. 169 | * It checks if file is inside an archive; if so, it removes last part 170 | * of file's path while we're inside an archive. 171 | * Then returns the index where the real filename begins (to extract the directory path). 172 | */ 173 | int search_enter_press(const char *str) { 174 | char arch_str[PATH_MAX + 1] = {0}; 175 | const char *tmp; 176 | 177 | if (sv.search_archive) { 178 | int len; 179 | 180 | strncpy(arch_str, str, PATH_MAX); 181 | while ((len = strlen(arch_str))) { 182 | tmp = strrchr(arch_str, '/'); 183 | if (is_ext(tmp, arch_ext, NUM(arch_ext))) { 184 | break; 185 | } 186 | arch_str[len - strlen(tmp)] = '\0'; 187 | } 188 | } 189 | if (strlen(arch_str)) { 190 | tmp = arch_str; 191 | } else { 192 | tmp = str; 193 | } 194 | return (strlen(tmp) - strlen(strrchr(tmp, '/'))); 195 | } 196 | -------------------------------------------------------------------------------- /src/string_constants.c: -------------------------------------------------------------------------------- 1 | const char yes[] = "y"; 2 | const char no[] = "n"; 3 | 4 | const char editor_missing[] = "You have to specify a valid editor in config file."; 5 | 6 | const char generic_error[] = "A generic error occurred. Check log."; 7 | 8 | const char *info_win_str[] = {"?: ", "I: ", "E: "}; 9 | 10 | const char *arch_ext[] = {".tgz", ".tar.gz", ".zip", ".rar", ".xz", ".ar"}; 11 | 12 | const char *sorting_str[] = {"Files will be sorted alphabetically now.", 13 | "Files will be sorted by size now.", 14 | "Files will be sorted by last access now.", 15 | "Files will be sorted by type now."}; 16 | 17 | const char bookmarks_add_quest[] = "Adding current file to bookmarks. Proceed? Y/n:> "; 18 | const char bookmarks_rm_quest[] = "Removing current file from bookmarks. Proceed? Y/n:> "; 19 | const char bookmarks_xdg_err[] = "You cannot remove xdg defined user dirs."; 20 | const char bookmarks_file_err[] = "Could not open bookmarks file."; 21 | const char bookmark_added[] = "Added to bookmarks!"; 22 | const char bookmarks_rm[] = "Removed from bookmarks!"; 23 | const char no_bookmarks[] = "No bookmarks found."; 24 | const char inexistent_bookmark_quest[] = "It seems current bookmark no longer exists. Remove it? Y/n:> "; 25 | const char inexistent_bookmark[] = "Bookmark was deleted as it was no more existent."; 26 | const char bookmark_already_present[] = "Bookmark already present. Would you like to remove it? Y/n:> "; 27 | const char bookmarks_cleared[] = "User bookmarks have been cleared."; 28 | 29 | const char no_selected_files[] = "There are no selected files."; 30 | const char *file_sel[] = {"File selected.", 31 | "File unselected.", 32 | "File unselected. Selected list empty.", 33 | "Every file in this directory has been selected.", 34 | "Every file in this directory has been unselected.", 35 | "Every file in this directory has been unselected. Selected list empty."}; 36 | const char selected_cleared[] = "List of selected files has been cleared."; 37 | 38 | const char sure[] = "Are you serious? y/N:> "; 39 | 40 | const char already_searching[] = "There's already a search in progress. Wait for it."; 41 | const char search_insert_name[] = "Insert filename to be found, at least 5 chars, max 20 chars.:> "; 42 | const char search_archives[] = "Do you want to search in archives too? y/N:> "; 43 | const char lazy_search[] = "Do you want a lazy search (less precise but faster)? y/N:>"; 44 | const char searched_string_minimum[] = "At least 5 chars..."; 45 | const char too_many_found[] = "Too many files found; try with a larger string."; 46 | const char no_found[] = "No files found."; 47 | const char *searching_mess[] = {"Searching...", "Search finished. Press f anytime from normal mode to view the results."}; 48 | 49 | #ifdef LIBCUPS_PRESENT 50 | const char print_question[] = "Do you really want to print this file? Y/n:> "; 51 | const char print_ok[] = "Print job added."; 52 | const char print_fail[] = "No printers available."; 53 | #endif 54 | 55 | const char archiving_mesg[] = "Insert new file name (defaults to first entry name):> "; 56 | 57 | const char ask_name[] = "Insert new name:> "; 58 | 59 | const char extr_question[] = "Do you really want to extract this archive? Y/n:> "; 60 | 61 | const char pwd_archive[] = "Current archive is encrypted. Enter a pwd:> "; 62 | 63 | const char *thread_job_mesg[] = {"Cutting...", "Pasting...", "Removing...", "Archiving...", "Extracting..."}; 64 | const char *thread_str[] = {"Every file has been cut.", "Every file has been pasted.", "File/dir removed.", "Archive is ready.", "Succesfully extracted."}; 65 | const char *thread_fail_str[] = {"Could not cut", "Could not paste.", "Could not remove every file.", "Could not archive.", "Could not extract every file."}; 66 | const char *short_msg[] = {"File created.", "Dir created.", "File renamed."}; 67 | 68 | const char selected_mess[] = "There are selected files."; 69 | 70 | const char thread_running[] = "There's already an active job. This job will be queued."; 71 | const char quit_with_running_thread[] = "Queued jobs still running. Waiting..."; 72 | 73 | const char pkg_quest[] = "Do you really want to install this package? y/N:> "; 74 | const char install_th_wait[] = "Waiting for package installation to finish..."; 75 | const char install_success[] = "Installed."; 76 | const char install_failed[] = "Could not install."; 77 | const char package_warn[] = "Currently there is no check against wrong package arch: it will crash packagekit and ncursesfm."; 78 | const char device_mode_str[] = "Devices:"; 79 | const char no_devices[] = "No devices found."; 80 | const char dev_mounted[] = "%s mounted in: %s."; 81 | const char dev_unmounted[] = "%s unmounted."; 82 | const char ext_dev_mounted[] = "External tool has mounted %s."; 83 | const char ext_dev_unmounted[] = "External tool has unmounted %s."; 84 | const char device_added[] = "New device connected."; 85 | const char device_removed[] = "Device removed."; 86 | const char no_proc_mounts[] = "Could not find /proc/mounts."; 87 | const char polling[] = "Still polling for devices."; 88 | const char monitor_err[] = "Monitor is not active. An error occurred, check log file."; 89 | 90 | const char bookmarks_mode_str[] = "Bookmarks:"; 91 | 92 | const char search_mode_str[] = "%d files found searching %s:"; 93 | 94 | const char selected_mode_str[] = "Selected files:"; 95 | 96 | const char ac_online[] = "On AC"; 97 | const char power_fail[] = "No power supply info available."; 98 | 99 | const char win_too_small[] = "Window too small. Enlarge it."; 100 | 101 | const int HELPER_HEIGHT[] = {16, 10, 9, 9, 9, 9}; 102 | const char helper_title[] = "Press 'L' to trigger helper"; 103 | 104 | const char helper_string[][16][150] = 105 | { 106 | { 107 | {"Remember: every shortcut in ncursesFM is case insensitive."}, 108 | {"%ENTER%surf between folders or to open files."}, 109 | {"It will eventually (un)mount your ISO files or install your distro downloaded packages."}, 110 | {"%,%enable fast browse mode: it lets you jump between files by just typing their name."}, 111 | {"%PG_UP/DOWN%jump straight to first/last file.%I%check files fullname."}, 112 | {"%H%trigger the showing of hidden files.%S%see files stats."}, 113 | {"%TAB%change sorting function: alphabetically (default), by size, by last modified or by type."}, 114 | {"%SPACE%select files. Once more to remove the file from selected files."}, 115 | {"%O%rename current file/dir.%N/D%create new file/dir.%F%search for a file."}, 116 | #ifdef LIBCUPS_PRESENT 117 | {"%V/X%paste/cut.%B%compress.%R%remove.%Z%extract.%P%print."}, 118 | #else 119 | {"%V/X%paste/cut.%B%compress.%R%remove.%Z%extract."}, 120 | #endif 121 | {"%T%create second tab.%W%close second tab.%ARROW KEYS%switch between tabs."}, 122 | {"%G%switch to bookmarks mode.%E%add/remove current file to bookmarks."}, 123 | {"%M%switch to device mode.%K%switch to selected mode."}, 124 | {"%ESC%quit."} 125 | }, { 126 | {"Remember: every shortcut in ncursesFM is case insensitive."}, 127 | {"Just start typing your desired filename, to move right to its position."}, 128 | {"%ENTER%surf between folders or to open files.%ARROW KEYS%switch between tabs."}, 129 | {"It will eventually (un)mount your ISO files or install your distro downloaded packages."}, 130 | {"%SPACE%select files. Once more to remove the file from selected files."}, 131 | {"%PG_UP/DOWN%jump straight to first/last file."}, 132 | {"%TAB%change sorting function: alphabetically (default), by size, by last modified or by type."}, 133 | {"%ESC%leave fast browse mode."} 134 | }, { 135 | {"Remember: every shortcut in ncursesFM is case insensitive."}, 136 | {"%S%see files stats.%I%check files fullname."}, 137 | {"%PG_UP/DOWN%jump straight to first/last file."}, 138 | {"%T%create second tab.%W%close second tab.%ARROW KEYS%switch between tabs."}, 139 | {"%R%remove selected file from bookmarks.%DEL%remove all user bookmarks."}, 140 | {"%ENTER%move to the folder/file selected."}, 141 | {"%ESC%leave bookmarks mode."} 142 | }, { 143 | {"Remember: every shortcut in ncursesFM is case insensitive."}, 144 | {"%S%see files stats.%I%check files fullname."}, 145 | {"%PG_UP/DOWN%jump straight to first/last file."}, 146 | {"%T%create second tab.%W%close second tab.%ARROW KEYS%switch between tabs."}, 147 | {"%ENTER%move to the folder/file selected."}, 148 | {"%ESC%leave search mode."} 149 | }, { 150 | {"Remember: every shortcut in ncursesFM is case insensitive."}, 151 | {"%S%see files stats.%I%check files fullname."}, 152 | {"%PG_UP/DOWN%jump straight to first/last file."}, 153 | {"%T%create second tab.%W%close second tab.%ARROW KEYS%switch between tabs."}, 154 | {"%M%(un)mount current device."}, 155 | {"%ENTER%move to current device mountpoint, mounting it if necessary."}, 156 | {"%ESC%leave device mode."} 157 | },{ 158 | {"Remember: every shortcut in ncursesFM is case insensitive."}, 159 | {"%S%see files stats.%I%check files fullname."}, 160 | {"%PG_UP/DOWN%jump straight to first/last file."}, 161 | {"%T%create second tab.%W%close second tab.%ARROW KEYS%switch between tabs."}, 162 | {"%R%remove current file selection.%DEL%remove all selected files."}, 163 | {"%ENTER%move to the folder/file selected."}, 164 | {"%ESC%leave selected mode."} 165 | } 166 | }; 167 | -------------------------------------------------------------------------------- /src/sysinfo.c: -------------------------------------------------------------------------------- 1 | #include "../inc/sysinfo.h" 2 | 3 | static void update_battery(int where); 4 | static void poll_batteries(void); 5 | 6 | static int timerfd; 7 | static int num_of_batt; 8 | static char ac_path[PATH_MAX + 1], (*batt)[PATH_MAX + 1]; 9 | static struct udev *udev; 10 | 11 | /* 12 | * Create 30s timer and returns its fd to 13 | * the main struct pollfd 14 | */ 15 | int start_timer(void) { 16 | struct itimerspec timerValue = {{0}}; 17 | 18 | timerfd = timerfd_create(CLOCK_MONOTONIC, 0); 19 | if (timerfd == -1) { 20 | WARN("could not start timer."); 21 | } else { 22 | udev = udev_new(); 23 | poll_batteries(); 24 | timerValue.it_value.tv_sec = 30; 25 | timerValue.it_value.tv_nsec = 0; 26 | timerValue.it_interval.tv_sec = 30; 27 | timerValue.it_interval.tv_nsec = 0; 28 | timerfd_settime(timerfd, 0, &timerValue, NULL); 29 | INFO("started time/battery monitor."); 30 | } 31 | return timerfd; 32 | } 33 | 34 | /* 35 | * Called from main_poll(); 36 | * it checks power_supply online status, and if !online, 37 | * checks batteries perc level. 38 | * Then calls update_batt (ui_functions) 39 | */ 40 | void timer_func(void) { 41 | for (int i = 0; i < strlen(config.sysinfo_layout); i++) { 42 | switch (tolower(config.sysinfo_layout[i])) { 43 | case 'c': 44 | update_time(i); 45 | break; 46 | case 'p': 47 | update_sysinfo(i); 48 | break; 49 | case 'b': 50 | update_battery(i); 51 | break; 52 | default: 53 | break; 54 | } 55 | } 56 | } 57 | 58 | static void update_battery(int where) { 59 | struct udev_device *dev; 60 | int perc[num_of_batt], online; 61 | char name[num_of_batt][10]; 62 | 63 | dev = udev_device_new_from_syspath(udev, ac_path); 64 | if (dev) { 65 | online = strtol(udev_device_get_property_value(dev, "POWER_SUPPLY_ONLINE"), NULL, 10); 66 | udev_device_unref(dev); 67 | if (!online) { 68 | for (int i = 0; i < num_of_batt; i++) { 69 | dev = udev_device_new_from_syspath(udev, batt[i]); 70 | if (dev && udev_device_get_property_value(dev, "POWER_SUPPLY_CAPACITY")) { 71 | perc[i] = strtol(udev_device_get_property_value(dev, "POWER_SUPPLY_CAPACITY"), NULL, 10); 72 | } else { 73 | perc[i] = -1; 74 | } 75 | if (dev && udev_device_get_property_value(dev, "POWER_SUPPLY_NAME")) { 76 | strcpy(name[i], udev_device_get_property_value(dev, "POWER_SUPPLY_NAME")); 77 | } else { 78 | strcpy(name[i], "BAT"); 79 | } 80 | udev_device_unref(dev); 81 | } 82 | } 83 | } else { 84 | online = -1; 85 | } 86 | update_batt(online, perc, num_of_batt, name, where); 87 | } 88 | 89 | void free_timer(void) { 90 | close(timerfd); 91 | if (batt) { 92 | free(batt); 93 | } 94 | udev_unref(udev); 95 | } 96 | 97 | /* 98 | * Initial battery polling: for each system battery, 99 | * save its path in batt[]. 100 | * If property "POWER_SUPPLY_ONLINE" is present, then 101 | * current udev device is ac adapter. 102 | */ 103 | static void poll_batteries(void) { 104 | struct udev_enumerate *enumerate; 105 | struct udev_list_entry *devices, *dev_list_entry; 106 | 107 | enumerate = udev_enumerate_new(udev); 108 | udev_enumerate_add_match_subsystem(enumerate, "power_supply"); 109 | udev_enumerate_scan_devices(enumerate); 110 | devices = udev_enumerate_get_list_entry(enumerate); 111 | udev_list_entry_foreach(dev_list_entry, devices) { 112 | const char *path = udev_list_entry_get_name(dev_list_entry); 113 | struct udev_device * dev = udev_device_new_from_syspath(udev, path); 114 | if (udev_device_get_property_value(dev, "POWER_SUPPLY_ONLINE")) { 115 | strncpy(ac_path, path, PATH_MAX); 116 | } else { 117 | batt = realloc(batt, (PATH_MAX + 1) * (num_of_batt + 1)); 118 | if (batt) { 119 | strncpy(batt[num_of_batt], path, PATH_MAX); 120 | num_of_batt++; 121 | } 122 | } 123 | udev_device_unref(dev); 124 | } 125 | udev_enumerate_unref(enumerate); 126 | } 127 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | #include "../inc/utils.h" 2 | 3 | void *remove_from_list(int *num, char (*str)[PATH_MAX + 1], int i) { 4 | memmove(&str[i], &str[i + 1], ((*num) - 1 - i) * (PATH_MAX + 1)); 5 | return safe_realloc(--(*num), str); 6 | } 7 | 8 | void *safe_realloc(const size_t size, char (*str)[PATH_MAX + 1]) { 9 | if (!(str = realloc(str, (PATH_MAX + 1) * size)) && size) { 10 | quit = MEM_ERR_QUIT; 11 | ERROR("could not realloc. Leaving."); 12 | } 13 | return str; 14 | } 15 | 16 | /* 17 | * Check if filename has "." in it (otherwise surely it has not extension) 18 | * Then for each extension in *ext[], check if last strlen(ext[i]) chars of filename are 19 | * equals to ext[i]. 20 | */ 21 | int is_ext(const char *filename, const char *ext[], int size) { 22 | int len = strlen(filename); 23 | 24 | if (strrchr(filename, '.')) { 25 | int i = 0; 26 | 27 | while (i < size) { 28 | if (!strcmp(filename + len - strlen(ext[i]), ext[i])) { 29 | return 1; 30 | } 31 | i++; 32 | } 33 | } 34 | return 0; 35 | } 36 | 37 | int get_mimetype(const char *path, const char *test) { 38 | int ret = 0; 39 | const char *mimetype; 40 | magic_t magic; 41 | 42 | if ((magic = magic_open(MAGIC_MIME_TYPE)) == NULL) { 43 | ERROR("An error occurred while loading libmagic database."); 44 | return ret; 45 | } 46 | if (magic_load(magic, NULL) == -1) { 47 | ERROR("An error occurred while loading libmagic database."); 48 | goto end; 49 | } 50 | if ((mimetype = magic_file(magic, path)) == NULL) { 51 | ERROR("An error occurred while loading libmagic database."); 52 | goto end; 53 | } 54 | if (strstr(mimetype, test)) { 55 | ret = 1; 56 | } 57 | 58 | end: 59 | magic_close(magic); 60 | return ret; 61 | } 62 | 63 | int move_cursor_to_file(int start_idx, const char *filename, int win) { 64 | int len; 65 | char fullpath[PATH_MAX + 1] = {0}; 66 | 67 | snprintf(fullpath, PATH_MAX, "%s/%s", ps[win].my_cwd, filename); 68 | len = strlen(fullpath); 69 | int i = is_present(fullpath, ps[win].nl, ps[win].number_of_files, len, start_idx); 70 | if (i != -1) { 71 | if (i != ps[win].curr_pos) { 72 | void (*f)(int, int); 73 | int delta; 74 | 75 | if (i < ps[win].curr_pos) { 76 | f = scroll_up; 77 | delta = ps[win].curr_pos - i; 78 | } else { 79 | f = scroll_down; 80 | delta = i - ps[win].curr_pos; 81 | } 82 | f(win, delta); 83 | } 84 | return 1; 85 | } 86 | return 0; 87 | } 88 | 89 | void save_old_pos(int win) { 90 | char *str; 91 | 92 | str = strrchr(ps[win].nl[ps[win].curr_pos], '/') + 1; 93 | strncpy(ps[win].old_file, str, NAME_MAX); 94 | } 95 | 96 | int is_present(const char *name, char (*str)[PATH_MAX + 1], int num, int len, int start_idx) { 97 | int cmp; 98 | 99 | for (int i = start_idx; i < num; i++) { 100 | if (len != -1) { 101 | cmp = strncmp(str[i], name, len); 102 | } else { 103 | cmp = strcmp(str[i], name); 104 | } 105 | if (!cmp) { 106 | return i; 107 | } 108 | } 109 | return -1; 110 | } 111 | 112 | /* 113 | * Helper function used in show_stat: received a size, 114 | * it changes the unit from Kb to Mb to Gb if size > 1024(previous unit) 115 | */ 116 | void change_unit(float size, char *str) { 117 | char *unit[] = {"B", "KB", "MB", "GB", "TB"}; 118 | int i = 0; 119 | 120 | while ((size > 1024) && (i < NUM(unit) - 1)) { 121 | size /= 1024; 122 | i++; 123 | } 124 | sprintf(str, "%.2f%s", size, unit[i]); 125 | } 126 | 127 | void leave_mode_helper(struct stat s) { 128 | char str[PATH_MAX + 1] = {0}; 129 | 130 | strncpy(str, str_ptr[active][ps[active].curr_pos], PATH_MAX); 131 | if (!S_ISDIR(s.st_mode)) { 132 | strncpy(ps[active].old_file, strrchr(str_ptr[active][ps[active].curr_pos], '/') + 1, NAME_MAX); 133 | int len = strlen(str_ptr[active][ps[active].curr_pos]) - strlen(ps[active].old_file); 134 | str[len] = '\0'; 135 | } else { 136 | memset(ps[active].old_file, 0, strlen(ps[active].old_file)); 137 | } 138 | leave_special_mode(str, active); 139 | } 140 | -------------------------------------------------------------------------------- /src/worker_thread.c: -------------------------------------------------------------------------------- 1 | #include "../inc/worker_thread.h" 2 | 3 | static thread_job_list *add_job(thread_job_list *h, int type, int (*f)(void)); 4 | static void free_running_h(void); 5 | static int init_thread_helper(void); 6 | static void *execute_thread(void *x); 7 | 8 | static thread_job_list *current_th; // current_th: ptr to latest elem in thread_l list 9 | static struct thread_mesg thread_m; 10 | static pthread_mutex_t job_lck; 11 | static int inhibit_fd; 12 | 13 | /* 14 | * Initializes mutex 15 | */ 16 | void init_job_queue(void) { 17 | pthread_mutex_init(&job_lck, NULL); 18 | } 19 | 20 | /* 21 | * Destroys mutex 22 | */ 23 | void destroy_job_queue(void) { 24 | pthread_mutex_destroy(&job_lck); 25 | } 26 | 27 | /* 28 | * Creates a new job object for the worker_thread appending it to the end of job's queue. 29 | */ 30 | static thread_job_list *add_job(thread_job_list *h, int type, int (*f)(void)) { 31 | pthread_mutex_lock(&job_lck); 32 | if (h) { 33 | current_th->next = add_job(current_th->next, type, f); 34 | } else { 35 | if (!(h = malloc(sizeof(struct thread_list)))) { 36 | quit = MEM_ERR_QUIT; 37 | ERROR("could not malloc. Leaving."); 38 | return NULL; 39 | } 40 | num_of_jobs++; 41 | h->selected_files = NULL; 42 | h->next = NULL; 43 | h->f = f; 44 | strncpy(h->full_path, ps[active].my_cwd, PATH_MAX); 45 | h->num_selected = num_selected; 46 | h->type = type; 47 | h->num = num_of_jobs; 48 | current_th = h; 49 | } 50 | pthread_mutex_unlock(&job_lck); 51 | return h; 52 | } 53 | 54 | /* 55 | * Deletes current job object and updates job queue. 56 | */ 57 | static void free_running_h(void) { 58 | pthread_mutex_lock(&job_lck); 59 | thread_job_list *tmp = thread_h; 60 | 61 | thread_h = thread_h->next; 62 | if (tmp->selected_files) 63 | free(tmp->selected_files); 64 | free(tmp); 65 | tmp = NULL; 66 | pthread_mutex_unlock(&job_lck); 67 | } 68 | 69 | void init_thread(int type, int (* const f)(void)) { 70 | if (!(thread_h = add_job(thread_h, type, f))) { 71 | return; 72 | } 73 | if (init_thread_helper() == -1) { 74 | return; 75 | } 76 | if (num_of_jobs > 1) { 77 | print_info(_(thread_running), INFO_LINE); 78 | INFO("job added to job's queue."); 79 | } else { 80 | if (config.inhibit) { 81 | inhibit_fd = inhibit_suspend("Job in process..."); 82 | } 83 | // update info_line with newly added job 84 | print_info("", INFO_LINE); 85 | INFO("starting a job."); 86 | pthread_create(&worker_th, NULL, execute_thread, NULL); 87 | } 88 | } 89 | 90 | /* 91 | * Fixes some needed current_th variables. 92 | */ 93 | static int init_thread_helper(void) { 94 | if (current_th->type == ARCHIVER_TH) { 95 | char name[NAME_MAX + 1] = {0}; 96 | int num = 1, len;; 97 | 98 | ask_user(_(archiving_mesg), name, NAME_MAX); 99 | if (name[0] == 27) { 100 | free(current_th); 101 | current_th = NULL; 102 | return -1; 103 | } 104 | if (!strlen(name)) { 105 | strncpy(name, strrchr(selected[0], '/') + 1, NAME_MAX); 106 | } 107 | /* avoid overwriting a compressed file in path if it has the same name of the archive being created there */ 108 | len = strlen(name); 109 | strcat(name, ".tgz"); 110 | while (access(name, F_OK) == 0) { 111 | sprintf(name + len, "%d.tgz", num); 112 | num++; 113 | } 114 | len = strlen(current_th->full_path); 115 | snprintf(current_th->full_path + len, PATH_MAX - 1, "/%s", name); 116 | } 117 | current_th->selected_files = selected; 118 | selected = NULL; 119 | num_selected = 0; 120 | erase_selected_highlight(); 121 | return 0; 122 | } 123 | 124 | /* 125 | * While job's queue isn't empty, exec job's queue head function, frees its resources, updates UI and notifies user. 126 | * Finally, call itself recursively. 127 | * When job's queue is empty, reset some vars and returns. 128 | */ 129 | static void *execute_thread(void *x) { 130 | if (thread_h) { 131 | if (thread_h->f() == -1) { 132 | thread_m.str = thread_fail_str[thread_h->type]; 133 | ERROR(thread_fail_str[thread_h->type]); 134 | thread_m.line = ERR_LINE; 135 | print_info("", INFO_LINE); // remove previous INFO_LINE message 136 | } else { 137 | thread_m.str = thread_str[thread_h->type]; 138 | INFO(thread_str[thread_h->type]); 139 | thread_m.line = INFO_LINE; 140 | } 141 | free_running_h(); 142 | print_info(_(thread_m.str), thread_m.line); 143 | #ifdef LIBNOTIFY_PRESENT 144 | send_notification(_(thread_m.str)); 145 | #endif 146 | return execute_thread(NULL); 147 | } 148 | INFO("ended all queued jobs."); 149 | num_of_jobs = 0; 150 | current_th = NULL; 151 | if (config.inhibit) { 152 | stop_inhibition(inhibit_fd); 153 | } 154 | pthread_detach(pthread_self()); 155 | pthread_exit(NULL); 156 | } 157 | --------------------------------------------------------------------------------