3 Commits

Author SHA1 Message Date
Evgeniy A. Dushistov
21a0678cba implement support of gziped index files 2016-08-06 00:31:37 +03:00
Evgeniy A. Dushistov
3aaae57139 add cargo lock 2016-07-03 19:43:43 +03:00
Evgeniy A. Dushistov
5b26d06493 something working 2016-07-03 16:56:30 +03:00
77 changed files with 7064527 additions and 4262 deletions

View File

@@ -15,7 +15,7 @@ BreakBeforeBinaryOperators: true
BreakBeforeTernaryOperators: true BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: true BreakConstructorInitializersBeforeComma: true
BinPackParameters: true BinPackParameters: true
ColumnLimit: 120 ColumnLimit: 0
ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerAllOnOneLineOrOnePerLine: false
DerivePointerAlignment: false DerivePointerAlignment: false
ExperimentalAutoDetectBinPacking: false ExperimentalAutoDetectBinPacking: false

View File

@@ -1,50 +0,0 @@
name: CI
on:
push:
branches:
- master
pull_request:
branches:
- master
schedule:
- cron: '00 02 */4 * *'
env:
RUST_BACKTRACE: 1
jobs:
tests:
name: Run tests
runs-on: ${{ matrix.os }}
strategy:
fail-fast: true
matrix:
os: [ubuntu-22.04, ubuntu-latest]
steps:
- uses: actions/checkout@v4
- uses: jwlawson/actions-setup-cmake@v2
with:
cmake-version: '3.10'
github-api-token: ${{ secrets.GITHUB_TOKEN }}
- name: Check versions
run: |
set -e
cmake --version
gcc --version
echo "end of versions checking"
shell: bash
- uses: awalsh128/cache-apt-pkgs-action@v1
with:
packages: libglib2.0-dev
version: 1.0
- name: Run tests
run: |
set -e
cd $GITHUB_WORKSPACE
mkdir build
cd build
cmake -DBUILD_TESTS=True ..
make -k -j2 VERBOSE=1
ctest --output-on-failure
shell: bash

48
.travis.yml Normal file
View File

@@ -0,0 +1,48 @@
#
# Available repositories are listed here:
# https://github.com/travis-ci/apt-source-whitelist/blob/master/ubuntu.json
#
sudo: false
language: cpp
matrix:
include:
- env: COMPILER_VERSION=4.8
os: linux
compiler: g++
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- kalakris-cmake
- ubuntu-sdk-team
packages:
- g++-4.8
- cmake
- libglib2.0-dev
# - env: COMPILER_VERSION=3.5
# os: linux
# compiler: clang++
# addons:
# apt:
# sources:
# - ubuntu-toolchain-r-test
# - llvm-toolchain-precise-3.5
# packages:
# - clang-3.5
# - cmake
# - libglib2.0-dev
before_script:
- mkdir build
- cd build
- CC=$CC-${COMPILER_VERSION} CXX=$CXX-${COMPILER_VERSION} cmake -DBUILD_TESTS=True ..
- cd ..
script:
- cd build
- make -k -j2 VERBOSE=1
- ctest --output-on-failure

View File

@@ -1,18 +1,24 @@
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
cmake_policy(VERSION 3.10)
project(sdcv) project(sdcv)
set(CMAKE_CXX_STANDARD 11) # Older versions have a different signature for CMAKE_MINIMUM_REQUIRED,
set(CMAKE_CXX_STANDARD_REQUIRED True) # check it manually just to make sure
set(CMAKE_CXX_EXTENSIONS False) if (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} LESS 2.8)
message(FATAL_ERROR "${PROJECT_NAME} requires at least CMake v2.8."
" You are running v${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}."
" Please upgrade." )
endif(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} LESS 2.8)
# If we get this far, use the modern signature. This will also cause newer
# CMake versions to try to be backwards-compatible with the desired version
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
cmake_policy(VERSION 2.8)
include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/compiler.cmake") include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/compiler.cmake")
set(ZLIB_FIND_REQUIRED True) set(ZLIB_FIND_REQUIRED True)
include(FindZLIB) include(FindZLIB)
set(GLIB2_REQ "'glib-2.0 >= 2.36'") set(GLIB2_REQ "'glib-2.0 >= 2.6.1'")
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindGLIB2.cmake") include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindGLIB2.cmake")
@@ -21,16 +27,11 @@ if (NOT GLIB2_FOUND)
"make sure that you install it") "make sure that you install it")
endif() endif()
set(WITH_READLINE True CACHE BOOL "Use readline library") find_path(READLINE_INCLUDE_DIR readline/readline.h)
find_library(READLINE_LIBRARY NAMES readline)
if (WITH_READLINE) if (READLINE_INCLUDE_DIR AND READLINE_LIBRARY)
find_path(READLINE_INCLUDE_DIR readline/readline.h) set(WITH_READLINE True)
find_library(READLINE_LIBRARY NAMES readline) endif ()
if (NOT (READLINE_INCLUDE_DIR AND READLINE_LIBRARY))
message(STATUS "readline library not FOUND, disable it's usage")
set(WITH_READLINE False CACHE BOOL "Use readline library" FORCE)
endif ()
endif (WITH_READLINE)
option(ENABLE_NLS "Enable NLS support" True) option(ENABLE_NLS "Enable NLS support" True)
@@ -38,16 +39,16 @@ set(sdcv_SRCS
src/sdcv.cpp src/sdcv.cpp
src/readline.cpp src/readline.cpp
src/readline.hpp src/readline.hpp
src/libwrapper.cpp src/libwrapper.cpp
src/libwrapper.hpp src/libwrapper.hpp
src/utils.cpp src/utils.cpp
src/utils.hpp src/utils.hpp
src/stardict_lib.cpp src/stardict_lib.cpp
src/stardict_lib.hpp src/stardict_lib.hpp
src/dictziplib.cpp src/dictziplib.cpp
src/dictziplib.hpp src/dictziplib.hpp
src/distance.cpp src/distance.cpp
src/distance.hpp src/distance.hpp
src/mapfile.hpp src/mapfile.hpp
) )
@@ -85,11 +86,7 @@ include_directories(
${GLIB2_INCLUDE_DIRS} ${GLIB2_INCLUDE_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/src/lib ${CMAKE_CURRENT_SOURCE_DIR}/src/lib
${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}
) )
if (WITH_READLINE)
include_directories(${READLINE_INCLUDE_DIR})
endif()
# #
# Packing stuff # Packing stuff
@@ -99,7 +96,7 @@ set(CPACK_PACKAGE_VENDOR "Evgeniy Dushistov <dushistov@mail.ru>")
set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.org") set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.org")
set(CPACK_PACKAGE_VERSION_MAJOR "0") set(CPACK_PACKAGE_VERSION_MAJOR "0")
set(CPACK_PACKAGE_VERSION_MINOR "5") set(CPACK_PACKAGE_VERSION_MINOR "5")
set(CPACK_PACKAGE_VERSION_PATCH "5") set(CPACK_PACKAGE_VERSION_PATCH "0-beta4")
set(sdcv_VERSION set(sdcv_VERSION
"${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
@@ -111,10 +108,8 @@ add_executable(sdcv ${sdcv_SRCS})
target_link_libraries(sdcv target_link_libraries(sdcv
${GLIB2_LIBRARIES} ${GLIB2_LIBRARIES}
${ZLIB_LIBRARIES} ${ZLIB_LIBRARIES}
${READLINE_LIBRARY}
) )
if (WITH_READLINE)
target_link_libraries(sdcv ${READLINE_LIBRARY})
endif()
if (ENABLE_NLS) if (ENABLE_NLS)
set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "locale") set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "locale")
endif () endif ()
@@ -144,16 +139,9 @@ if (BUILD_TESTS)
add_sdcv_shell_test(t_list) add_sdcv_shell_test(t_list)
add_sdcv_shell_test(t_use) add_sdcv_shell_test(t_use)
add_sdcv_shell_test(t_only_data_dir)
add_sdcv_shell_test(t_synonyms)
add_sdcv_shell_test(t_json)
add_sdcv_shell_test(t_exact)
add_sdcv_shell_test(t_interactive) add_sdcv_shell_test(t_interactive)
add_sdcv_shell_test(t_utf8output) add_sdcv_shell_test(t_utf8output)
add_sdcv_shell_test(t_utf8input) add_sdcv_shell_test(t_utf8input)
add_sdcv_shell_test(t_datadir) add_sdcv_shell_test(t_datadir)
add_sdcv_shell_test(t_return_code)
add_sdcv_shell_test(t_multiple_results)
add_sdcv_shell_test(t_newlines_in_ifo)
endif (BUILD_TESTS) endif (BUILD_TESTS)

134
Cargo.lock generated Normal file
View File

@@ -0,0 +1,134 @@
[root]
name = "sdcv"
version = "0.1.0"
dependencies = [
"byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"flate2 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
"getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
"gettext 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rust-ini 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "byteorder"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "byteorder"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "encoding"
version = "0.2.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"encoding-index-japanese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)",
"encoding-index-korean 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)",
"encoding-index-simpchinese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)",
"encoding-index-singlebyte 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)",
"encoding-index-tradchinese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "encoding-index-japanese"
version = "1.20141219.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "encoding-index-korean"
version = "1.20141219.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "encoding-index-simpchinese"
version = "1.20141219.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "encoding-index-singlebyte"
version = "1.20141219.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "encoding-index-tradchinese"
version = "1.20141219.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "encoding_index_tests"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "flate2"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
"miniz-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "gcc"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "getopts"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "gettext"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"encoding 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "libc"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "log"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "miniz-sys"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rust-ini"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]

12
Cargo.toml Normal file
View File

@@ -0,0 +1,12 @@
[package]
name = "sdcv"
version = "0.1.0"
authors = ["Evgeniy A. Dushistov <dushistov@mail.ru>"]
[dependencies]
getopts = "0.2"
gettext = "0.2.0"
rust-ini = "0.9.5"
byteorder = "0.5"
libc = "0.2.14"
flate2 = "0.2"

11
LICENSE
View File

@@ -1,11 +1,10 @@
GNU GENERAL PUBLIC LICENSE GNU GENERAL PUBLIC LICENSE
Version 2, June 1991 Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc. Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
of this license document, but changing it is not allowed.
Preamble Preamble
@@ -306,7 +305,7 @@ the "copyright" line and a pointer to where the full notice is found.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail. Also add information on how to contact you by electronic and paper mail.

30
NEWS
View File

@@ -1,31 +1,3 @@
Version 0.5.5
- Avoid crashes when passing unknown dicts to the -u flag (by NiLuJe)
- Use off_t for stuff mainly assigned to a stat.st_size value
Version 0.5.4
- Use binary search for synonyms
- Various improvments in work with synonyms
- Added --json (same as --json-output) to match man
- Show all matched result
- More robust parsing of ifo file
- Prevent crash if file size of files not matched expecting one for .oft files
Version 0.5.3
- Use single quotes around JSON data to reduce need for escaping
- Store integer magic in cache file
- Added $SDCV_HISTFILE to set history file
- return exit code 2 if search term not found
- Comply with the XDG Base Directory Specification
Version 0.5.2
- Synonyms index support (.syn files) by Peter <craven@gmx.net>
- Add support of json output by Peter <craven@gmx.net> (--json-output)
- Add -e for exact searches (no fuzzy matches) by Peter <craven@gmx.net>
- Fix build with clang 3.4.1
- fix FSF address in license by Tomáš Čech <sleep_walker@suse.com>
Version 0.5.1
Fix usage of SDCV_PAGER by Anton Yuzhaninov
Fix build without readline<
specify dictionary order by -u switches or ~/.sdcv_ordering by Cong Gu
Version 0.5 Version 0.5
- add option to colorize output - add option to colorize output
- Roman Imankulov's patch to better use of readline - Roman Imankulov's patch to better use of readline
@@ -46,7 +18,7 @@ Version 0.4.2
* Russian translation update * Russian translation update
Version 0.4.1 Version 0.4.1
* Recreate cache if idx file was modified * Recreate cache if idx file was modified
* Abbility to use pager(SDCV_PAGER) * Abbility to use pager(SDCV_PAGER)
* Add Chinese (traditional) translation * Add Chinese (traditional) translation
* Add Ukrainian translation * Add Ukrainian translation

View File

@@ -1,9 +1,6 @@
#+OPTIONS: ^:nil #+OPTIONS: ^:nil
[[https://github.com/Dushistov/sdcv/actions?query=workflow%3ACI+branch%3Amaster][https://github.com/Dushistov/sdcv/workflows/CI/badge.svg]] [[https://travis-ci.org/Dushistov/sdcv][https://travis-ci.org/Dushistov/sdcv.svg?branch=master]]
[[https://github.com/Dushistov/sdcv/blob/master/LICENSE][https://img.shields.io/badge/license-GPL%202-brightgreen.svg]] [[https://github.com/Dushistov/sdcv/blob/master/LICENSE][https://img.shields.io/badge/license-GPL%202-brightgreen.svg]]
* sdcv
*sdcv* is a simple, cross-platform, text-based utility for working with dictionaries in [[http://stardict-4.sourceforge.net/][StarDict]] format.
* How to compile and install * How to compile and install
#+BEGIN_SRC sh #+BEGIN_SRC sh
mkdir /tmp/build-sdcv mkdir /tmp/build-sdcv
@@ -25,40 +22,12 @@ you can use "DESTDIR" variable to change installation path
See sdcv man page for usage description. See sdcv man page for usage description.
* Bugs * Bugs
To report bugs use https://github.com/Dushistov/sdcv/issues , If you find bug reports it via email to dushistov at mail dot ru.
if it is not possible you can report it via email to dushistov at mail dot ru.
Be sure to include the word "sdcv" somewhere in the "Subject:" field. Be sure to include the word "sdcv" somewhere in the "Subject:" field.
* Integration with [[https://github.com/junegunn/fzf][fzf]]
Useful when you have multiple dictionaries
#+BEGIN_SRC sh
fzf --prompt="Dict: " \
--phony \
--bind "enter:reload(sdcv {q} -n --json | jq '.[].dict' -r)" \
--preview "sdcv {q} -en --use-dict={}" \
--preview-window=right:70%:wrap \
< <(echo)
#+END_SRC
* Integration with readline
This lines can be added to inputrc file (~/.inputrc, /etc/inputrc),
to abort multiply usage with ESC:
#+begin_src
$if sdcv
"\e\e": "-1\n"
$endif
#+end_src
* Notes to developer * Notes to developer
** make source code release ** make source code release
#+BEGIN_SRC sh #+BEGIN_SRC sh
make package_source make package_source
#+END_SRC #+END_SRC
** update translation
#+BEGIN_SRC sh
cd po
xgettext -k_ ../src/*.cpp -o new.pot
msgmerge -U sdcv.pot new.pot
rm new.pot
for i in `ls *.po`; do msgmerge -U $i sdcv.pot; done
#+END_SRC

View File

@@ -16,6 +16,19 @@ if (NOT DEFINED SDCV_COMPILER_IS_GCC_COMPATIBLE)
endif() endif()
endif() endif()
if (MSVC AND (MSVC_VERSION LESS 1900))
message(FATAL_ERROR "MSVC version ${MSVC_VERSION} have no full c++11 support")
elseif (MSVC)
add_definitions(-DNOMINMAX)
elseif (NOT MSVC)
check_cxx_compiler_flag("-std=c++11" CXX_SUPPORTS_CXX11)
if (CXX_SUPPORTS_CXX11)
append("-std=c++11" CMAKE_CXX_FLAGS)
else ()
message(FATAL_ERROR "sdcv requires C++11 support but the '-std=c++11' flag isn't supported.")
endif()
endif ()
if (SDCV_COMPILER_IS_GCC_COMPATIBLE) if (SDCV_COMPILER_IS_GCC_COMPATIBLE)
append("-Wall" "-Wextra" "-Wformat-security" "-Wcast-align" "-Werror=format" "-Wcast-qual" CMAKE_C_FLAGS) append("-Wall" "-Wextra" "-Wformat-security" "-Wcast-align" "-Werror=format" "-Wcast-qual" CMAKE_C_FLAGS)
append("-Wall" "-pedantic" "-Wextra" "-Wformat-security" "-Wcast-align" "-Werror=format" "-Wcast-qual" CMAKE_CXX_FLAGS) append("-Wall" "-pedantic" "-Wextra" "-Wformat-security" "-Wcast-align" "-Werror=format" "-Wcast-qual" CMAKE_CXX_FLAGS)

View File

@@ -9,95 +9,78 @@ sdcv \- console version of StarDict program
[list of words] [list of words]
.SH DESCRIPTION .SH DESCRIPTION
.I sdcv .I sdcv
is a simple, cross-platform text-based utility is simple, cross-platform text-base utility
for working with dictionaries in StarDict format. for work with dictionaries in StarDict's format.
Each word from "list of words" may be a string The word from "list of words" may be string
with a leading '/' for using a Fuzzy search algorithm, with leading '/' for using Fuzzy search algorithm,
with a leading '|' for using full-text search, with leading '|' for using full-text search,
and the string may contain '?' and '*' for regexp search. string may contain '?' and '*' for using regexp search.
It works in interactive and non-interactive mode. It work in interactive and not interactive mode.
To exit from interactive mode press Ctrl+D. To exit from interactive mode press Ctrl+D.
In interactive mode, In interactive mode,
if sdcv was compiled with readline library support, if sdcv was compiled with readline library support,
you can use the UP and DOWN keys to cycle through history. you can use UP and DOWN keys to work through history.
.SH OPTIONS .SH OPTIONS
.TP 8 .TP 8
.B "\-h \-\-help" .B "\-h \-\-help"
Display help message and exit display help message and exit
.TP 8 .TP 8
.B "\-v \-\-version" .B "\-v \-\-verbose"
Display version and exit display version and exit
.TP 8 .TP 8
.B "\-l \-\-list\-dicts" .B "\-l \-\-list\-dicts"
Display list of available dictionaries and exit display list of available dictionaries and exit
.TP 8 .TP 8
.B "\-u \-\-use\-dict filename" .B "\-u \-\-use\-dict filename"
For search use only dictionary with this bookname for search use only dictionary with this bookname
.TP 8 .TP 8
.B "\-n \-\-non\-interactive" .B "\-n \-\-non\-interactive"
For use in scripts for use in scripts
.TP 8
.B "\-x \-\-only\-data\-dir"
For use in scripts: only use the dictionaries in data-dir, do not search in user and system directories
.TP 8
.B "\-e \-\-exact\-search"
Do not fuzzy-search for similar words, only return exact matches
.TP 8
.B "\-j \-\-json"
Print the results of list-dicts and searches as json, not as plain text.
For use in automatically processing the results of a dictionary lookup.
.TP 8 .TP 8
.B "\-\-utf8\-output" .B "\-\-utf8\-output"
Force sdcv to not convert to locale charset, output in utf8 Force sdcv not use conversation to locale charset, output in utf8
.TP 8 .TP 8
.B "\-\-utf8\-input" .B "\-\-utf8\-input"
Force sdcv to not convert from locale charset, assume that Force sdcv not use conversation from locale charset, suppose that
input is in utf8 input in utf8
.TP 8 .TP 8
.B "\-\-data\-dir path/to/directory" .B "\-\-data\-dir path/to/directory"
Use this directory as the path to the stardict data directory. This means that Use this directory as path to stardict data directory. This is mean that
sdcv searches for dictionaries in data-dir/dic directory. sdcv search dictionaries in data-dir/dic directory.
.TP 8 .TP 8
.B "\-\-color" .B "\-\-color"
Use ANSI escape codes for colorizing sdcv output (does not work with json output). Use ANSI escape code for colorize sdcv output
.SH FILES .SH FILES
.TP .TP
/usr/share/stardict/dic /usr/share/stardict/dic
.TP .TP
$(XDG_DATA_HOME)/stardict/dic $(HOME)/.stardict/dic
Place where sdcv expects to find dictionaries. Place, where sdcv expect to find dictionaries.
Instead of /usr/share/stardict/dic you can use any directory Instead of /usr/share/stardict/dic you can use everything
you want, just set the STARDICT_DATA_DIR environment variable. that you want, just set STARDICT_DATA_DIR environment variable.
For example, if you have dictionaries in /mnt/data/stardict-dicts/dic, For example, if you have dictionaries in /mnt/data/stardict-dicts/dic,
set STARDICT_DATA_DIR to /mnt/data/stardict-dicts. set STARDICT_DATA_DIR to /mnt/data/stardict-dicts.
.TP .TP
$(XDG_DATA_HOME)/sdcv_history $(HOME)/.sdcv_history
This file includes the last $(SDCV_HISTSIZE) words, which you sought with sdcv. This file include last $(SDCV_HISTSIZE) words, which you seek with sdcv.
SDCV uses this file only if it was compiled with readline library support. SDCV use this file only if it was compiled with readline library support.
.TP
$(XDG_CONFIG_HOME)/sdcv_ordering
This is a text file containing one dictionary bookname per line.
It specifies in which order the results of a search should be shown.
.SH ENVIRONMENT .SH ENVIRONMENT
Environment Variables Used By \fIsdcv\fR: Environment Variables Used By \fIsdcv\fR:
.TP 20 .TP 20
.B STARDICT_DATA_DIR .B STARDICT_DATA_DIR
If set, sdcv uses this variable as the data directory, this means that sdcv If set, sdcv use this variable as data directory, this is mean that sdcv
searches dictionaries in $\fBSTARDICT_DATA_DIR\fR/dic search dictionaries in $\fBSTARDICT_DATA_DIR\fR\\dic
.TP 20 .TP 20
.B SDCV_HISTSIZE .B SDCV_HISTSIZE
If set, sdcv writes in $(XDG_DATA_HOME)/sdcv_history (or $(SDCV_HISTFILE)) the last $(SDCV_HISTSIZE) words, If set, sdcv wrote in $(HOME)/.sdcv_history only last $(SDCV_HISTSIZE) words,
which you look up using sdcv. If it is not set, then the last 2000 words are saved in $(XDG_DATA_HOME)/sdcv_history. which you seek using sdcv. If it is not set, then last 2000 words saved in $(HOME)/.sdcv_history.
.TP 20
.B SDCV_HISTFILE
If set, sdcv writes it's history to $(SDCV_HISTFILE). If it is not set, then the default $(XDG_DATA_HOME)/sdcv_history path will be used.
.TP 20 .TP 20
.B SDCV_PAGER .B SDCV_PAGER
If SDCV_PAGER is set, its value is used as the name of the program If SDCV_PAGER is set, its value is used as the name of the program
to use to display the dictionary article. to use to display the dictionary's article.
.SH BUGS .SH BUGS
Email bug reports to dushistov at mail dot ru. Be sure to include the word Email bug reports to dushistov at mail dot ru. Be sure to include the word
"sdcv" somewhere in the "Subject:" field. "sdcv" somewhere in the "Subject:" field.

View File

@@ -50,7 +50,7 @@ sdcv проста, міжплатформена текстова утиліта
.TP .TP
/usr/share/stardict/dic /usr/share/stardict/dic
.TP .TP
$(XDG_DATA_HOME)/stardict/dic $(HOME)/.stardict/dic
Місце, де sdcv очікує знайти словники. Місце, де sdcv очікує знайти словники.
Замість шляху /usr/share/stardict/dic Ви можете використовувати все, Замість шляху /usr/share/stardict/dic Ви можете використовувати все,
@@ -58,7 +58,7 @@ $(XDG_DATA_HOME)/stardict/dic
Наприклад, якщо Ви маєте словники у теці /mnt/data/stardict-dicts/dic, Наприклад, якщо Ви маєте словники у теці /mnt/data/stardict-dicts/dic,
встановіть STARDICT_DATA_DIR у /mnt/data/stardict-dicts. встановіть STARDICT_DATA_DIR у /mnt/data/stardict-dicts.
.TP .TP
$(XDG_DATA_HOME)/sdcv_history $(HOME)/.sdcv_history
Цей файл містить останні $(SDCV_HISTSIZE) слова, які Ви шукали з sdcv. Цей файл містить останні $(SDCV_HISTSIZE) слова, які Ви шукали з sdcv.
SDCV використовує цей файл при умові, якщо sdcv був скомпільований SDCV використовує цей файл при умові, якщо sdcv був скомпільований
@@ -69,12 +69,12 @@ SDCV використовує цей файл при умові, якщо sdcv
.TP 20 .TP 20
.B STARDICT_DATA_DIR .B STARDICT_DATA_DIR
Якщо встановлена, sdcv використає цю змінну як теку даних, це означає, Якщо встановлена, sdcv використає цю змінну як теку даних, це означає,
що sdcv шукатиме словники у $\fBSTARDICT_DATA_DIR\fR/dic що sdcv шукатиме словники у $\fBSTARDICT_DATA_DIR\fR\dic
.TP 20 .TP 20
.B SDCV_HISTSIZE .B SDCV_HISTSIZE
Якщо встановлена, sdcv писатиме у $(XDG_DATA_HOME)/sdcv_history лише Якщо встановлена, sdcv писатиме у $(HOME)/.sdcv_history лише
останні $(SDCV_HISTSIZE) слова, які Ви шукали з sdcv. Якщо не встановлена, останні $(SDCV_HISTSIZE) слова, які Ви шукали з sdcv. Якщо не встановлена,
то збірігатиметься останніх 2000 слів у $(XDG_DATA_HOME)/sdcv_history. то збірігатиметься останніх 2000 слів у $(HOME)/.sdcv_history.
.SH BUGS .SH BUGS
Звіти про помилки висилайте на адресу dushistov на mail крапка ru. Звіти про помилки висилайте на адресу dushistov на mail крапка ru.
Не забувайте включати слово "sdcv" десь у полі "Тема:". Не забувайте включати слово "sdcv" десь у полі "Тема:".

188
po/cs.po
View File

@@ -6,7 +6,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: sdcv 0.4.2\n" "Project-Id-Version: sdcv 0.4.2\n"
"Report-Msgid-Bugs-To: dushistov@mail.ru\n" "Report-Msgid-Bugs-To: dushistov@mail.ru\n"
"POT-Creation-Date: 2020-08-14 12:58+0300\n" "POT-Creation-Date: 2008-09-24 10:54+0200\n"
"PO-Revision-Date: 2008-09-24 10:42+0200\n" "PO-Revision-Date: 2008-09-24 10:42+0200\n"
"Last-Translator: Michal Čihař <michal@cihar.com>\n" "Last-Translator: Michal Čihař <michal@cihar.com>\n"
"Language-Team: Czech <cs@li.org>\n" "Language-Team: Czech <cs@li.org>\n"
@@ -16,25 +16,102 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
#: ../src/libwrapper.cpp:300 #: src/sdcv.cpp:108
#, fuzzy
msgid "display version information and exit"
msgstr "-v, --version zobrazí informace o verzi a skončí\n"
#: src/sdcv.cpp:110
#, fuzzy
msgid "display list of available dictionaries and exit"
msgstr "-l, --list-dicts zobrazí seznam dostupných slovníků a skončí\n"
#: src/sdcv.cpp:112
#, fuzzy
msgid "for search use only dictionary with this bookname"
msgstr "-u, --use-dict jméno vyhledávat jen v zadaném slovníku\n"
#: src/sdcv.cpp:113
msgid "bookname"
msgstr ""
#: src/sdcv.cpp:115
msgid "for use in scripts"
msgstr ""
#: src/sdcv.cpp:117
#, fuzzy
msgid "output must be in utf8"
msgstr "--utf8-output výstup musí být v utf8\n"
#: src/sdcv.cpp:119
#, fuzzy
msgid "input of sdcv in utf8"
msgstr "--utf8-input vstup musí být v utf8\n"
#: src/sdcv.cpp:121
#, fuzzy
msgid "use this directory as path to stardict data directory"
msgstr ""
"--data-dir cesta/někam použít tento adresář jako cestu ke slovníkům "
"stardict\n"
#: src/sdcv.cpp:122
msgid "path/to/dir"
msgstr ""
#: src/sdcv.cpp:129
msgid " words"
msgstr ""
#: src/sdcv.cpp:135
#, c-format
msgid "Invalid command line arguments: %s\n"
msgstr ""
#: src/sdcv.cpp:141
#, c-format
msgid "Console version of Stardict, version %s\n"
msgstr "Konzolová verze Stardictu, verze %s\n"
#: src/sdcv.cpp:170
#, c-format
msgid "Dictionary's name Word count\n"
msgstr "Jméno slovníku Počet slov\n"
#: src/sdcv.cpp:192
#, c-format
msgid "g_mkdir failed: %s\n"
msgstr "g_mkdir selhalo: %s\n"
#: src/sdcv.cpp:208
msgid "Enter word or phrase: "
msgstr "Zadejte slovo nebo frázi: "
#: src/sdcv.cpp:216
#, c-format
msgid "There are no words/phrases to translate.\n"
msgstr "Nebyla zadáno nic k přeložení.\n"
#: src/libwrapper.cpp:248
msgid "popen failed" msgid "popen failed"
msgstr "popen selhalo" msgstr "popen selhalo"
#: ../src/libwrapper.cpp:341 #: src/libwrapper.cpp:285 src/utils.cpp:67
#, c-format #, c-format
msgid "Can not convert %s to utf8.\n" msgid "Can not convert %s to utf8.\n"
msgstr "Nepodařilo se převést %s do utf8.\n" msgstr "Nepodařilo se převést %s do utf8.\n"
#: ../src/libwrapper.cpp:399 ../src/libwrapper.cpp:433 #: src/libwrapper.cpp:342 src/libwrapper.cpp:370
#, fuzzy, c-format #, fuzzy, c-format
msgid "Found %zu items, similar to %s.\n" msgid "Found %zu items, similar to %s.\n"
msgstr "Nalezeno %d záznamů podobných %s.\n" msgstr "Nalezeno %d záznamů podobných %s.\n"
#: ../src/libwrapper.cpp:417 #: src/libwrapper.cpp:356
msgid "Your choice[-1 to abort]: " msgid "Your choice[-1 to abort]: "
msgstr "Vaše volba [-1 pro ukončení]: " msgstr "Vaše volba [-1 pro ukončení]: "
#: ../src/libwrapper.cpp:427 #: src/libwrapper.cpp:365
#, fuzzy, c-format #, fuzzy, c-format
msgid "" msgid ""
"Invalid choice.\n" "Invalid choice.\n"
@@ -43,107 +120,12 @@ msgstr ""
"Chybná volba.\n" "Chybná volba.\n"
"Musí být mezi 0 a %d nebo -1.\n" "Musí být mezi 0 a %d nebo -1.\n"
#: ../src/libwrapper.cpp:446 #: src/libwrapper.cpp:381
#, c-format #, c-format
msgid "Nothing similar to %s, sorry :(\n" msgid "Nothing similar to %s, sorry :(\n"
msgstr "Nic podobného %s nenalezeno, promiň :(\n" msgstr "Nic podobného %s nenalezeno, promiň :(\n"
#: ../src/sdcv.cpp:89 #: src/utils.cpp:45
#, fuzzy
msgid "display version information and exit"
msgstr "-v, --version zobrazí informace o verzi a skončí\n"
#: ../src/sdcv.cpp:91
#, fuzzy
msgid "display list of available dictionaries and exit"
msgstr "-l, --list-dicts zobrazí seznam dostupných slovníků a skončí\n"
#: ../src/sdcv.cpp:93
#, fuzzy
msgid "for search use only dictionary with this bookname"
msgstr "-u, --use-dict jméno vyhledávat jen v zadaném slovníku\n"
#: ../src/sdcv.cpp:94
msgid "bookname"
msgstr ""
#: ../src/sdcv.cpp:96
msgid "for use in scripts"
msgstr ""
#: ../src/sdcv.cpp:98
msgid "print the result formatted as JSON"
msgstr ""
#: ../src/sdcv.cpp:100
msgid "do not fuzzy-search for similar words, only return exact matches"
msgstr ""
#: ../src/sdcv.cpp:102
#, fuzzy
msgid "output must be in utf8"
msgstr "--utf8-output výstup musí být v utf8\n"
#: ../src/sdcv.cpp:104
#, fuzzy
msgid "input of sdcv in utf8"
msgstr "--utf8-input vstup musí být v utf8\n"
#: ../src/sdcv.cpp:106
#, fuzzy
msgid "use this directory as path to stardict data directory"
msgstr ""
"--data-dir cesta/někam použít tento adresář jako cestu ke slovníkům "
"stardict\n"
#: ../src/sdcv.cpp:107
msgid "path/to/dir"
msgstr ""
#: ../src/sdcv.cpp:109
msgid ""
"only use the dictionaries in data-dir, do not search in user and system "
"directories"
msgstr ""
#: ../src/sdcv.cpp:111
msgid "colorize the output"
msgstr ""
#: ../src/sdcv.cpp:116
msgid " words"
msgstr ""
#: ../src/sdcv.cpp:122
#, c-format
msgid "Invalid command line arguments: %s\n"
msgstr ""
#: ../src/sdcv.cpp:128
#, c-format
msgid "Console version of Stardict, version %s\n"
msgstr "Konzolová verze Stardictu, verze %s\n"
#: ../src/sdcv.cpp:206
#, c-format
msgid "g_mkdir failed: %s\n"
msgstr "g_mkdir selhalo: %s\n"
#: ../src/sdcv.cpp:222
msgid "Enter word or phrase: "
msgstr "Zadejte slovo nebo frázi: "
#: ../src/sdcv.cpp:230
#, c-format
msgid "There are no words/phrases to translate.\n"
msgstr "Nebyla zadáno nic k přeložení.\n"
#: ../src/sdcv.cpp:242
#, c-format
msgid "Dictionary's name Word count\n"
msgstr "Jméno slovníku Počet slov\n"
#: ../src/utils.cpp:48
#, fuzzy, c-format #, fuzzy, c-format
msgid "Can not convert %s to current locale.\n" msgid "Can not convert %s to current locale.\n"
msgstr "Nepodařilo se převést %s do utf8.\n" msgstr "Nepodařilo se převést %s do utf8.\n"

198
po/fr.po
View File

@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: sdcv 0.4.2\n" "Project-Id-Version: sdcv 0.4.2\n"
"Report-Msgid-Bugs-To: dushistov@mail.ru\n" "Report-Msgid-Bugs-To: dushistov@mail.ru\n"
"POT-Creation-Date: 2020-08-14 12:58+0300\n" "POT-Creation-Date: 2008-09-24 10:54+0200\n"
"PO-Revision-Date: 2009-06-15 23:20+0800\n" "PO-Revision-Date: 2009-06-15 23:20+0800\n"
"Language-Team: Vincent Petry <PVince81@yahoo.fr>\n" "Language-Team: Vincent Petry <PVince81@yahoo.fr>\n"
"Language: \n" "Language: \n"
@@ -18,25 +18,107 @@ msgstr ""
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
#: ../src/libwrapper.cpp:300 #: src/sdcv.cpp:108
#, fuzzy
msgid "display version information and exit"
msgstr ""
"-v, --version afficher les informations de version et sortir\n"
#: src/sdcv.cpp:110
#, fuzzy
msgid "display list of available dictionaries and exit"
msgstr ""
"-l, --list-dicts afficher la liste des dictionnaires disponibles et "
"sortir\n"
#: src/sdcv.cpp:112
#, fuzzy
msgid "for search use only dictionary with this bookname"
msgstr ""
"-u, --use-dict nom_dict pour chercher seulement en utilisant le "
"dictionnaire spécifié\n"
#: src/sdcv.cpp:113
msgid "bookname"
msgstr ""
#: src/sdcv.cpp:115
msgid "for use in scripts"
msgstr ""
#: src/sdcv.cpp:117
#, fuzzy
msgid "output must be in utf8"
msgstr "--utf8-output force la sortie au format utf8\n"
#: src/sdcv.cpp:119
#, fuzzy
msgid "input of sdcv in utf8"
msgstr "--utf8-input force l'entrée de sdcv au format utf8\n"
#: src/sdcv.cpp:121
#, fuzzy
msgid "use this directory as path to stardict data directory"
msgstr ""
"--data-dir chemin utiliser ce chemin pour trouver les données de "
"stardict\n"
#: src/sdcv.cpp:122
msgid "path/to/dir"
msgstr ""
#: src/sdcv.cpp:129
msgid " words"
msgstr ""
#: src/sdcv.cpp:135
#, c-format
msgid "Invalid command line arguments: %s\n"
msgstr ""
#: src/sdcv.cpp:141
#, c-format
msgid "Console version of Stardict, version %s\n"
msgstr "Version console de Stardict, version %s\n"
#: src/sdcv.cpp:170
#, c-format
msgid "Dictionary's name Word count\n"
msgstr "Nom dictionnaire Nombre de mots\n"
#: src/sdcv.cpp:192
#, c-format
msgid "g_mkdir failed: %s\n"
msgstr "Échec de g_mkdir : %s\n"
#: src/sdcv.cpp:208
msgid "Enter word or phrase: "
msgstr "Entrez un mot ou une phrase: "
#: src/sdcv.cpp:216
#, c-format
msgid "There are no words/phrases to translate.\n"
msgstr "Il n'y a pas de mots/phrases à traduire.\n"
#: src/libwrapper.cpp:248
msgid "popen failed" msgid "popen failed"
msgstr "Échec de popen" msgstr "Échec de popen"
#: ../src/libwrapper.cpp:341 #: src/libwrapper.cpp:285 src/utils.cpp:67
#, c-format #, c-format
msgid "Can not convert %s to utf8.\n" msgid "Can not convert %s to utf8.\n"
msgstr "Ne peut convertir %s au format utf8.\n" msgstr "Ne peut convertir %s au format utf8.\n"
#: ../src/libwrapper.cpp:399 ../src/libwrapper.cpp:433 #: src/libwrapper.cpp:342 src/libwrapper.cpp:370
#, fuzzy, c-format #, fuzzy, c-format
msgid "Found %zu items, similar to %s.\n" msgid "Found %zu items, similar to %s.\n"
msgstr "Trouvé %d éléments similaires à %s.\n" msgstr "Trouvé %d éléments similaires à %s.\n"
#: ../src/libwrapper.cpp:417 #: src/libwrapper.cpp:356
msgid "Your choice[-1 to abort]: " msgid "Your choice[-1 to abort]: "
msgstr "Votre choix[-1 pour abandonner] : " msgstr "Votre choix[-1 pour abandonner] : "
#: ../src/libwrapper.cpp:427 #: src/libwrapper.cpp:365
#, fuzzy, c-format #, fuzzy, c-format
msgid "" msgid ""
"Invalid choice.\n" "Invalid choice.\n"
@@ -45,112 +127,12 @@ msgstr ""
"Selection invalide.\n" "Selection invalide.\n"
"Veuillez choisir un nombre entre 0 et %d, ou -1.\n" "Veuillez choisir un nombre entre 0 et %d, ou -1.\n"
#: ../src/libwrapper.cpp:446 #: src/libwrapper.cpp:381
#, c-format #, c-format
msgid "Nothing similar to %s, sorry :(\n" msgid "Nothing similar to %s, sorry :(\n"
msgstr "Aucun mot/phrase similaire à %s, désolé :(\n" msgstr "Aucun mot/phrase similaire à %s, désolé :(\n"
#: ../src/sdcv.cpp:89 #: src/utils.cpp:45
#, fuzzy
msgid "display version information and exit"
msgstr ""
"-v, --version afficher les informations de version et sortir\n"
#: ../src/sdcv.cpp:91
#, fuzzy
msgid "display list of available dictionaries and exit"
msgstr ""
"-l, --list-dicts afficher la liste des dictionnaires disponibles et "
"sortir\n"
#: ../src/sdcv.cpp:93
#, fuzzy
msgid "for search use only dictionary with this bookname"
msgstr ""
"-u, --use-dict nom_dict pour chercher seulement en utilisant le "
"dictionnaire spécifié\n"
#: ../src/sdcv.cpp:94
msgid "bookname"
msgstr ""
#: ../src/sdcv.cpp:96
msgid "for use in scripts"
msgstr ""
#: ../src/sdcv.cpp:98
msgid "print the result formatted as JSON"
msgstr ""
#: ../src/sdcv.cpp:100
msgid "do not fuzzy-search for similar words, only return exact matches"
msgstr ""
#: ../src/sdcv.cpp:102
#, fuzzy
msgid "output must be in utf8"
msgstr "--utf8-output force la sortie au format utf8\n"
#: ../src/sdcv.cpp:104
#, fuzzy
msgid "input of sdcv in utf8"
msgstr "--utf8-input force l'entrée de sdcv au format utf8\n"
#: ../src/sdcv.cpp:106
#, fuzzy
msgid "use this directory as path to stardict data directory"
msgstr ""
"--data-dir chemin utiliser ce chemin pour trouver les données de "
"stardict\n"
#: ../src/sdcv.cpp:107
msgid "path/to/dir"
msgstr ""
#: ../src/sdcv.cpp:109
msgid ""
"only use the dictionaries in data-dir, do not search in user and system "
"directories"
msgstr ""
#: ../src/sdcv.cpp:111
msgid "colorize the output"
msgstr ""
#: ../src/sdcv.cpp:116
msgid " words"
msgstr ""
#: ../src/sdcv.cpp:122
#, c-format
msgid "Invalid command line arguments: %s\n"
msgstr ""
#: ../src/sdcv.cpp:128
#, c-format
msgid "Console version of Stardict, version %s\n"
msgstr "Version console de Stardict, version %s\n"
#: ../src/sdcv.cpp:206
#, c-format
msgid "g_mkdir failed: %s\n"
msgstr "Échec de g_mkdir : %s\n"
#: ../src/sdcv.cpp:222
msgid "Enter word or phrase: "
msgstr "Entrez un mot ou une phrase: "
#: ../src/sdcv.cpp:230
#, c-format
msgid "There are no words/phrases to translate.\n"
msgstr "Il n'y a pas de mots/phrases à traduire.\n"
#: ../src/sdcv.cpp:242
#, c-format
msgid "Dictionary's name Word count\n"
msgstr "Nom dictionnaire Nombre de mots\n"
#: ../src/utils.cpp:48
#, c-format #, c-format
msgid "Can not convert %s to current locale.\n" msgid "Can not convert %s to current locale.\n"
msgstr "Ne peut pas convertir %s dans la locale courante.\n" msgstr "Ne peut pas convertir %s dans la locale courante.\n"

147
po/ka.po
View File

@@ -1,147 +0,0 @@
# Georgian translation for sdcv.
# Copyright (C) 2025 sdcv authors
# This file is distributed under the same license as the sdcv project.
# Temuri Doghonadze <temuri.doghonadze@gmail.com>, 2025.
#
msgid ""
msgstr ""
"Project-Id-Version: sdcv 0.5\n"
"Report-Msgid-Bugs-To: dushistov@mail.ru\n"
"POT-Creation-Date: 2020-08-14 12:58+0300\n"
"PO-Revision-Date: 2025-01-26 03:17+0100\n"
"Last-Translator: Temuri Doghonadze <temuri.doghonadze@gmail.com>\n"
"Language-Team: Georgian <(nothing)>\n"
"Language: ka\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 3.5\n"
#: ../src/libwrapper.cpp:300
msgid "popen failed"
msgstr "popen ჩავარდა"
#: ../src/libwrapper.cpp:341
#, c-format
msgid "Can not convert %s to utf8.\n"
msgstr "%s-ის utf8-ში გადაყვანა შეუძლებელია.\n"
#: ../src/libwrapper.cpp:399 ../src/libwrapper.cpp:433
#, c-format
msgid "Found %zu items, similar to %s.\n"
msgstr "აღმოჩენილია %zu ელემენტი, რომელიც %s-ს ჰგავს.\n"
#: ../src/libwrapper.cpp:417
msgid "Your choice[-1 to abort]: "
msgstr "თქვენი არჩევანი[-1 გასაუქმებლად]: "
#: ../src/libwrapper.cpp:427
#, c-format
msgid ""
"Invalid choice.\n"
"It must be from 0 to %zu or -1.\n"
msgstr ""
"არასწორი არჩევანი.\n"
"უნდა იყოს 0-დან %zu-მდე, ან -1.\n"
#: ../src/libwrapper.cpp:446
#, c-format
msgid "Nothing similar to %s, sorry :(\n"
msgstr "%s-ს არაფერი ჰგავს :(\n"
#: ../src/sdcv.cpp:89
msgid "display version information and exit"
msgstr "ვერსიის ჩვენება და გასვლა"
#: ../src/sdcv.cpp:91
msgid "display list of available dictionaries and exit"
msgstr "ხელმისაწვდომი ლექსიკონების ჩვენება და გასვლა"
#: ../src/sdcv.cpp:93
msgid "for search use only dictionary with this bookname"
msgstr "ძებნისთვის, მხოლოდ, ამ სახელის მქონე ლექსიკონის გამოყენება"
#: ../src/sdcv.cpp:94
msgid "bookname"
msgstr "ლექსიკონის_სახელი"
#: ../src/sdcv.cpp:96
msgid "for use in scripts"
msgstr "სკრიპტებში გამოსაყენებლად"
#: ../src/sdcv.cpp:98
msgid "print the result formatted as JSON"
msgstr "შედეგების JSON ფორმატში გამოტანა"
#: ../src/sdcv.cpp:100
msgid "do not fuzzy-search for similar words, only return exact matches"
msgstr ""
"მსგავსი სიტყვებისთვის არაზუსტი ძებნა გამოყენებული არ იქნება. "
"დაბრუნდება, მხოლოდ, ზუსტი დამთხვევები"
#: ../src/sdcv.cpp:102
msgid "output must be in utf8"
msgstr "გამოტანა utf8-ში უნდა იყოს"
#: ../src/sdcv.cpp:104
msgid "input of sdcv in utf8"
msgstr "პროგრამაში შეყვანა utf8 -ში"
#: ../src/sdcv.cpp:106
msgid "use this directory as path to stardict data directory"
msgstr ""
"ამ საქაღალდის გამოყენება stardict-ის მონაცემების საქაღალდის ბილიკად"
#: ../src/sdcv.cpp:107
msgid "path/to/dir"
msgstr "ბილიკი/სასურველ/საქაღალდემდე"
#: ../src/sdcv.cpp:109
msgid ""
"only use the dictionaries in data-dir, do not search in user and system "
"directories"
msgstr ""
"ლექსიკონების, მხოლოდ, data-dir-დან გამოყენება. მომხმარებლის და სისტემურ "
"საქაღალდეებში ძებნა არ მოხდება"
#: ../src/sdcv.cpp:111
msgid "colorize the output"
msgstr "ფერებში გამოტანა"
#: ../src/sdcv.cpp:116
msgid " words"
msgstr " სიტყვა"
#: ../src/sdcv.cpp:122
#, c-format
msgid "Invalid command line arguments: %s\n"
msgstr "არასწორი ბრძანების სტრიქონის არგუმენტები: %s\n"
#: ../src/sdcv.cpp:128
#, c-format
msgid "Console version of Stardict, version %s\n"
msgstr "Startdict-ის კონსოლის ვერსია. ვერსია %s\n"
#: ../src/sdcv.cpp:206
#, c-format
msgid "g_mkdir failed: %s\n"
msgstr "g_mkdir ჩავარდა: %s\n"
#: ../src/sdcv.cpp:222
msgid "Enter word or phrase: "
msgstr "შეიყვანეთ სიტყვა ან ფრაზა: "
#: ../src/sdcv.cpp:230
#, c-format
msgid "There are no words/phrases to translate.\n"
msgstr "სათარგმნი სიტყვების/ფრაზების გარეშე.\n"
#: ../src/sdcv.cpp:242
#, c-format
msgid "Dictionary's name Word count\n"
msgstr "ლექსიკონის სახელი სიტყვების რაოდენობა\n"
#: ../src/utils.cpp:48
#, c-format
msgid "Can not convert %s to current locale.\n"
msgstr "ვერ გადავიყვანე %s მიმდინარე ლოკალში.\n"

183
po/ru.po
View File

@@ -8,8 +8,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: sdcv 0.5\n" "Project-Id-Version: sdcv 0.5\n"
"Report-Msgid-Bugs-To: dushistov@mail.ru\n" "Report-Msgid-Bugs-To: dushistov@mail.ru\n"
"POT-Creation-Date: 2020-08-14 12:58+0300\n" "POT-Creation-Date: 2008-09-24 10:54+0200\n"
"PO-Revision-Date: 2017-08-16 10:05+0300\n" "PO-Revision-Date: 2013-07-07 18:49+0400\n"
"Last-Translator: Evgeniy Dushistov <dushistov@mail.ru>\n" "Last-Translator: Evgeniy Dushistov <dushistov@mail.ru>\n"
"Language-Team: Russian <ru@li.org>\n" "Language-Team: Russian <ru@li.org>\n"
"Language: ru\n" "Language: ru\n"
@@ -19,25 +19,99 @@ msgstr ""
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
#: ../src/libwrapper.cpp:300 #: src/sdcv.cpp:80
msgid "display version information and exit"
msgstr "показать номер версии и завершить работу"
#: src/sdcv.cpp:82
msgid "display list of available dictionaries and exit"
msgstr "показать список доступных словарей и завершить работу"
#: src/sdcv.cpp:84
msgid "for search use only dictionary with this bookname"
msgstr "для поиска использовать только этот словарь с таким именем"
#: src/sdcv.cpp:85
msgid "bookname"
msgstr "имя_словаря"
#: src/sdcv.cpp:87
msgid "for use in scripts"
msgstr "для использования в скриптах"
#: src/sdcv.cpp:89
msgid "output must be in utf8"
msgstr "вывод программы должен быть в utf8"
#: src/sdcv.cpp:91
msgid "input of sdcv in utf8"
msgstr "ввод программы в utf8"
#: src/sdcv.cpp:93
msgid "use this directory as path to stardict data directory"
msgstr ""
"использовать эту директорию в качестве пути к \"stardict data\" директории"
#: src/sdcv.cpp:94
msgid "path/to/dir"
msgstr "путь/до/директории"
#: src/sdcv.cpp:96
msgid "colorize the output"
msgstr "раскрашивать вывод в разные цвета"
#: src/sdcv.cpp:101
msgid " words"
msgstr "слова"
#: src/sdcv.cpp:107
#, c-format
msgid "Invalid command line arguments: %s\n"
msgstr "Неправильный аргумент командой строки: %s\n"
#: src/sdcv.cpp:113
#, c-format
msgid "Console version of Stardict, version %s\n"
msgstr "Консольная версия StarDict, версия %s\n"
#: src/sdcv.cpp:139
#, c-format
msgid "Dictionary's name Word count\n"
msgstr "Название словаря Количество слов\n"
#: src/sdcv.cpp:174
#, c-format
msgid "g_mkdir failed: %s\n"
msgstr "g_mkdir завершился с ошибкой: %s\n"
#: src/sdcv.cpp:189
msgid "Enter word or phrase: "
msgstr "Введите слово или фразу: "
#: src/sdcv.cpp:197
#, c-format
msgid "There are no words/phrases to translate.\n"
msgstr "Не задано слова/фразы для перевода.\n"
#: src/libwrapper.cpp:280
msgid "popen failed" msgid "popen failed"
msgstr "функция popen завершилась с ошибкой" msgstr "функция popen завершилась с ошибкой"
#: ../src/libwrapper.cpp:341 #: src/libwrapper.cpp:317 src/utils.cpp:67
#, c-format #, c-format
msgid "Can not convert %s to utf8.\n" msgid "Can not convert %s to utf8.\n"
msgstr "Не могу преобразовать %s в utf8.\n" msgstr "Не могу преобразовать %s в utf8.\n"
#: ../src/libwrapper.cpp:399 ../src/libwrapper.cpp:433 #: src/libwrapper.cpp:371 src/libwrapper.cpp:402
#, c-format #, c-format
msgid "Found %zu items, similar to %s.\n" msgid "Found %zu items, similar to %s.\n"
msgstr "Найдено %zu слов, похожих на %s.\n" msgstr "Найдено %zu слов, похожих на %s.\n"
#: ../src/libwrapper.cpp:417 #: src/libwrapper.cpp:388
msgid "Your choice[-1 to abort]: " msgid "Your choice[-1 to abort]: "
msgstr "Ваш выбор[-1 - отмена]: " msgstr "Ваш выбор[-1 - отмена]: "
#: ../src/libwrapper.cpp:427 #: src/libwrapper.cpp:397
#, c-format #, c-format
msgid "" msgid ""
"Invalid choice.\n" "Invalid choice.\n"
@@ -46,103 +120,12 @@ msgstr ""
"Неправильный выбор.\n" "Неправильный выбор.\n"
"Должно быть от 0 до %zu или -1.\n" "Должно быть от 0 до %zu или -1.\n"
#: ../src/libwrapper.cpp:446 #: src/libwrapper.cpp:413
#, c-format #, c-format
msgid "Nothing similar to %s, sorry :(\n" msgid "Nothing similar to %s, sorry :(\n"
msgstr "Ничего похожего на %s, извините :(\n" msgstr "Ничего похожего на %s, извините :(\n"
#: ../src/sdcv.cpp:89 #: src/utils.cpp:45
msgid "display version information and exit"
msgstr "показать номер версии и завершить работу"
#: ../src/sdcv.cpp:91
msgid "display list of available dictionaries and exit"
msgstr "показать список доступных словарей и завершить работу"
#: ../src/sdcv.cpp:93
msgid "for search use only dictionary with this bookname"
msgstr "для поиска использовать только этот словарь с таким именем"
#: ../src/sdcv.cpp:94
msgid "bookname"
msgstr "имя_словаря"
#: ../src/sdcv.cpp:96
msgid "for use in scripts"
msgstr "для использования в скриптах"
#: ../src/sdcv.cpp:98
msgid "print the result formatted as JSON"
msgstr "выдать результат в JSON формате"
#: ../src/sdcv.cpp:100
msgid "do not fuzzy-search for similar words, only return exact matches"
msgstr ""
"не использовать нечеткий поиск похожих слов, вернуть только точные совпадения"
#: ../src/sdcv.cpp:102
msgid "output must be in utf8"
msgstr "вывод программы должен быть в utf8"
#: ../src/sdcv.cpp:104
msgid "input of sdcv in utf8"
msgstr "ввод программы в utf8"
#: ../src/sdcv.cpp:106
msgid "use this directory as path to stardict data directory"
msgstr ""
"использовать эту директорию в качестве пути к \"stardict data\" директории"
#: ../src/sdcv.cpp:107
msgid "path/to/dir"
msgstr "путь/до/директории"
#: ../src/sdcv.cpp:109
msgid ""
"only use the dictionaries in data-dir, do not search in user and system "
"directories"
msgstr ""
"использовать словари только из data-dir, не искать в пользовательских и "
"системных каталогах"
#: ../src/sdcv.cpp:111
msgid "colorize the output"
msgstr "раскрашивать вывод в разные цвета"
#: ../src/sdcv.cpp:116
msgid " words"
msgstr "слова"
#: ../src/sdcv.cpp:122
#, c-format
msgid "Invalid command line arguments: %s\n"
msgstr "Неправильный аргумент командой строки: %s\n"
#: ../src/sdcv.cpp:128
#, c-format
msgid "Console version of Stardict, version %s\n"
msgstr "Консольная версия StarDict, версия %s\n"
#: ../src/sdcv.cpp:206
#, c-format
msgid "g_mkdir failed: %s\n"
msgstr "g_mkdir завершился с ошибкой: %s\n"
#: ../src/sdcv.cpp:222
msgid "Enter word or phrase: "
msgstr "Введите слово или фразу: "
#: ../src/sdcv.cpp:230
#, c-format
msgid "There are no words/phrases to translate.\n"
msgstr "Не задано слова/фразы для перевода.\n"
#: ../src/sdcv.cpp:242
#, c-format
msgid "Dictionary's name Word count\n"
msgstr "Название словаря Количество слов\n"
#: ../src/utils.cpp:48
#, c-format #, c-format
msgid "Can not convert %s to current locale.\n" msgid "Can not convert %s to current locale.\n"
msgstr "Не могу преобразовать %s в локальную кодировку.\n" msgstr "Не могу преобразовать %s в локальную кодировку.\n"

View File

@@ -7,133 +7,118 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: sdcv 0.5\n" "Project-Id-Version: sdcv 0.5\n"
"Report-Msgid-Bugs-To: dushistov@mail.ru\n" "Report-Msgid-Bugs-To: dushistov@mail.ru\n"
"POT-Creation-Date: 2020-08-14 12:58+0300\n" "POT-Creation-Date: 2008-09-24 10:54+0200\n"
"PO-Revision-Date: 2017-08-16 10:01+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n" "Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: ../src/libwrapper.cpp:300 #: src/sdcv.cpp:76
msgid "display version information and exit"
msgstr ""
#: src/sdcv.cpp:78
msgid "display list of available dictionaries and exit"
msgstr ""
#: src/sdcv.cpp:84
msgid "for search use only dictionary with this bookname"
msgstr ""
#: src/sdcv.cpp:85
msgid "bookname"
msgstr ""
#: src/sdcv.cpp:87
msgid "for use in scripts"
msgstr ""
#: src/sdcv.cpp:89
msgid "output must be in utf8"
msgstr ""
#: src/sdcv.cpp:91
msgid "input of sdcv in utf8"
msgstr ""
#: src/sdcv.cpp:93
msgid "use this directory as path to stardict data directory"
msgstr ""
#: src/sdcv.cpp:94
msgid "path/to/dir"
msgstr ""
#: src/sdcv.cpp:96
msgid "colorize the output"
msgstr ""
#: src/sdcv.cpp:101
msgid " words"
msgstr ""
#: src/sdcv.cpp:107
#, c-format
msgid "Invalid command line arguments: %s\n"
msgstr ""
#: src/sdcv.cpp:113
#, c-format
msgid "Console version of Stardict, version %s\n"
msgstr ""
#: src/sdcv.cpp:139
#, c-format
msgid "Dictionary's name Word count\n"
msgstr ""
#: src/sdcv.cpp:174
#, c-format
msgid "g_mkdir failed: %s\n"
msgstr ""
#: src/sdcv.cpp:189
msgid "Enter word or phrase: "
msgstr ""
#: src/sdcv.cpp:197
#, c-format
msgid "There are no words/phrases to translate.\n"
msgstr ""
#: src/libwrapper.cpp:280
msgid "popen failed" msgid "popen failed"
msgstr "" msgstr ""
#: ../src/libwrapper.cpp:341 #: src/libwrapper.cpp:317 src/utils.cpp:67
#, c-format #, c-format
msgid "Can not convert %s to utf8.\n" msgid "Can not convert %s to utf8.\n"
msgstr "" msgstr ""
#: ../src/libwrapper.cpp:399 ../src/libwrapper.cpp:433 #: src/libwrapper.cpp:371 src/libwrapper.cpp:402
#, c-format #, c-format
msgid "Found %zu items, similar to %s.\n" msgid "Found %zu items, similar to %s.\n"
msgstr "" msgstr ""
#: ../src/libwrapper.cpp:417 #: src/libwrapper.cpp:388
msgid "Your choice[-1 to abort]: " msgid "Your choice[-1 to abort]: "
msgstr "" msgstr ""
#: ../src/libwrapper.cpp:427 #: src/libwrapper.cpp:397
#, c-format #, c-format
msgid "" msgid ""
"Invalid choice.\n" "Invalid choice.\n"
"It must be from 0 to %zu or -1.\n" "It must be from 0 to %zu or -1.\n"
msgstr "" msgstr ""
#: ../src/libwrapper.cpp:446 #: src/libwrapper.cpp:413
#, c-format #, c-format
msgid "Nothing similar to %s, sorry :(\n" msgid "Nothing similar to %s, sorry :(\n"
msgstr "" msgstr ""
#: ../src/sdcv.cpp:89 #: src/utils.cpp:45
msgid "display version information and exit"
msgstr ""
#: ../src/sdcv.cpp:91
msgid "display list of available dictionaries and exit"
msgstr ""
#: ../src/sdcv.cpp:93
msgid "for search use only dictionary with this bookname"
msgstr ""
#: ../src/sdcv.cpp:94
msgid "bookname"
msgstr ""
#: ../src/sdcv.cpp:96
msgid "for use in scripts"
msgstr ""
#: ../src/sdcv.cpp:98
msgid "print the result formatted as JSON"
msgstr ""
#: ../src/sdcv.cpp:100
msgid "do not fuzzy-search for similar words, only return exact matches"
msgstr ""
#: ../src/sdcv.cpp:102
msgid "output must be in utf8"
msgstr ""
#: ../src/sdcv.cpp:104
msgid "input of sdcv in utf8"
msgstr ""
#: ../src/sdcv.cpp:106
msgid "use this directory as path to stardict data directory"
msgstr ""
#: ../src/sdcv.cpp:107
msgid "path/to/dir"
msgstr ""
#: ../src/sdcv.cpp:109
msgid ""
"only use the dictionaries in data-dir, do not search in user and system "
"directories"
msgstr ""
#: ../src/sdcv.cpp:111
msgid "colorize the output"
msgstr ""
#: ../src/sdcv.cpp:116
msgid " words"
msgstr ""
#: ../src/sdcv.cpp:122
#, c-format
msgid "Invalid command line arguments: %s\n"
msgstr ""
#: ../src/sdcv.cpp:128
#, c-format
msgid "Console version of Stardict, version %s\n"
msgstr ""
#: ../src/sdcv.cpp:206
#, c-format
msgid "g_mkdir failed: %s\n"
msgstr ""
#: ../src/sdcv.cpp:222
msgid "Enter word or phrase: "
msgstr ""
#: ../src/sdcv.cpp:230
#, c-format
msgid "There are no words/phrases to translate.\n"
msgstr ""
#: ../src/sdcv.cpp:242
#, c-format
msgid "Dictionary's name Word count\n"
msgstr ""
#: ../src/utils.cpp:48
#, c-format #, c-format
msgid "Can not convert %s to current locale.\n" msgid "Can not convert %s to current locale.\n"
msgstr "" msgstr ""

190
po/sk.po
View File

@@ -6,7 +6,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: sdcv 0.3.2\n" "Project-Id-Version: sdcv 0.3.2\n"
"Report-Msgid-Bugs-To: dushistov@mail.ru\n" "Report-Msgid-Bugs-To: dushistov@mail.ru\n"
"POT-Creation-Date: 2020-08-14 12:58+0300\n" "POT-Creation-Date: 2008-09-24 10:54+0200\n"
"PO-Revision-Date: 2007-09-11 00:22+0100\n" "PO-Revision-Date: 2007-09-11 00:22+0100\n"
"Last-Translator: Ivan Masár <helix84@centrum.sk>\n" "Last-Translator: Ivan Masár <helix84@centrum.sk>\n"
"Language-Team: Slovak <sk-i18n@lists.linux.sk>\n" "Language-Team: Slovak <sk-i18n@lists.linux.sk>\n"
@@ -18,26 +18,104 @@ msgstr ""
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
#: ../src/libwrapper.cpp:300 #: src/sdcv.cpp:108
#, fuzzy
msgid "display version information and exit"
msgstr "-v, --version zobrazí informácie o verzii a skončí\n"
#: src/sdcv.cpp:110
#, fuzzy
msgid "display list of available dictionaries and exit"
msgstr ""
"-l, --list-dicts zobrazí zoznam dostupných slovníkov a skončí\n"
#: src/sdcv.cpp:112
#, fuzzy
msgid "for search use only dictionary with this bookname"
msgstr "-u, --use-dict názov použiť pre hľadanie iba zvolený slovník\n"
#: src/sdcv.cpp:113
msgid "bookname"
msgstr ""
#: src/sdcv.cpp:115
msgid "for use in scripts"
msgstr ""
#: src/sdcv.cpp:117
#, fuzzy
msgid "output must be in utf8"
msgstr "--utf8-output výstup musí byť v utf8\n"
#: src/sdcv.cpp:119
#, fuzzy
msgid "input of sdcv in utf8"
msgstr "--utf8-input vstup pre sdcv je v utf8\n"
#: src/sdcv.cpp:121
#, fuzzy
msgid "use this directory as path to stardict data directory"
msgstr ""
"--data-dir path/to/dir použiť tento priečinok ako cestu pre stardict "
"dátový priečinok\n"
#: src/sdcv.cpp:122
msgid "path/to/dir"
msgstr ""
#: src/sdcv.cpp:129
msgid " words"
msgstr ""
#: src/sdcv.cpp:135
#, c-format
msgid "Invalid command line arguments: %s\n"
msgstr ""
#: src/sdcv.cpp:141
#, c-format
msgid "Console version of Stardict, version %s\n"
msgstr "Konzolová verzia StarDict, verzia %s\n"
#: src/sdcv.cpp:170
#, c-format
msgid "Dictionary's name Word count\n"
msgstr "názov slovníka počet slov\n"
#: src/sdcv.cpp:192
#, c-format
msgid "g_mkdir failed: %s\n"
msgstr "g_mkdir zlyhalo: %s\n"
#: src/sdcv.cpp:208
msgid "Enter word or phrase: "
msgstr "Vložte slovo alebo frázu: "
#: src/sdcv.cpp:216
#, c-format
msgid "There are no words/phrases to translate.\n"
msgstr "Nie je čo preložiť.\n"
#: src/libwrapper.cpp:248
msgid "popen failed" msgid "popen failed"
msgstr "popen zlyhalo" msgstr "popen zlyhalo"
#: ../src/libwrapper.cpp:341 #: src/libwrapper.cpp:285 src/utils.cpp:67
#, c-format #, c-format
msgid "Can not convert %s to utf8.\n" msgid "Can not convert %s to utf8.\n"
msgstr "nie je možné konvertovať %s na utf8.\n" msgstr "nie je možné konvertovať %s na utf8.\n"
#: ../src/libwrapper.cpp:399 ../src/libwrapper.cpp:433 #: src/libwrapper.cpp:342 src/libwrapper.cpp:370
#, fuzzy, c-format #, fuzzy, c-format
msgid "Found %zu items, similar to %s.\n" msgid "Found %zu items, similar to %s.\n"
msgstr "Nájdené %d položiek, podobných %s,\n" msgstr "Nájdené %d položiek, podobných %s,\n"
#: ../src/libwrapper.cpp:417 #: src/libwrapper.cpp:356
#, fuzzy #, fuzzy
msgid "Your choice[-1 to abort]: " msgid "Your choice[-1 to abort]: "
msgstr "Vaša voľba[-1 zruší]: " msgstr "Vaša voľba[-1 zruší]: "
#: ../src/libwrapper.cpp:427 #: src/libwrapper.cpp:365
#, fuzzy, c-format #, fuzzy, c-format
msgid "" msgid ""
"Invalid choice.\n" "Invalid choice.\n"
@@ -46,108 +124,12 @@ msgstr ""
"Neplatná voľba.\n" "Neplatná voľba.\n"
"Musí byť od 0 do %d alebo -1.\n" "Musí byť od 0 do %d alebo -1.\n"
#: ../src/libwrapper.cpp:446 #: src/libwrapper.cpp:381
#, c-format #, c-format
msgid "Nothing similar to %s, sorry :(\n" msgid "Nothing similar to %s, sorry :(\n"
msgstr "Ľutujem, nič sa nepodobá na %s :(\n" msgstr "Ľutujem, nič sa nepodobá na %s :(\n"
#: ../src/sdcv.cpp:89 #: src/utils.cpp:45
#, fuzzy
msgid "display version information and exit"
msgstr "-v, --version zobrazí informácie o verzii a skončí\n"
#: ../src/sdcv.cpp:91
#, fuzzy
msgid "display list of available dictionaries and exit"
msgstr ""
"-l, --list-dicts zobrazí zoznam dostupných slovníkov a skončí\n"
#: ../src/sdcv.cpp:93
#, fuzzy
msgid "for search use only dictionary with this bookname"
msgstr "-u, --use-dict názov použiť pre hľadanie iba zvolený slovník\n"
#: ../src/sdcv.cpp:94
msgid "bookname"
msgstr ""
#: ../src/sdcv.cpp:96
msgid "for use in scripts"
msgstr ""
#: ../src/sdcv.cpp:98
msgid "print the result formatted as JSON"
msgstr ""
#: ../src/sdcv.cpp:100
msgid "do not fuzzy-search for similar words, only return exact matches"
msgstr ""
#: ../src/sdcv.cpp:102
#, fuzzy
msgid "output must be in utf8"
msgstr "--utf8-output výstup musí byť v utf8\n"
#: ../src/sdcv.cpp:104
#, fuzzy
msgid "input of sdcv in utf8"
msgstr "--utf8-input vstup pre sdcv je v utf8\n"
#: ../src/sdcv.cpp:106
#, fuzzy
msgid "use this directory as path to stardict data directory"
msgstr ""
"--data-dir path/to/dir použiť tento priečinok ako cestu pre stardict "
"dátový priečinok\n"
#: ../src/sdcv.cpp:107
msgid "path/to/dir"
msgstr ""
#: ../src/sdcv.cpp:109
msgid ""
"only use the dictionaries in data-dir, do not search in user and system "
"directories"
msgstr ""
#: ../src/sdcv.cpp:111
msgid "colorize the output"
msgstr ""
#: ../src/sdcv.cpp:116
msgid " words"
msgstr ""
#: ../src/sdcv.cpp:122
#, c-format
msgid "Invalid command line arguments: %s\n"
msgstr ""
#: ../src/sdcv.cpp:128
#, c-format
msgid "Console version of Stardict, version %s\n"
msgstr "Konzolová verzia StarDict, verzia %s\n"
#: ../src/sdcv.cpp:206
#, c-format
msgid "g_mkdir failed: %s\n"
msgstr "g_mkdir zlyhalo: %s\n"
#: ../src/sdcv.cpp:222
msgid "Enter word or phrase: "
msgstr "Vložte slovo alebo frázu: "
#: ../src/sdcv.cpp:230
#, c-format
msgid "There are no words/phrases to translate.\n"
msgstr "Nie je čo preložiť.\n"
#: ../src/sdcv.cpp:242
#, c-format
msgid "Dictionary's name Word count\n"
msgstr "názov slovníka počet slov\n"
#: ../src/utils.cpp:48
#, c-format #, c-format
msgid "Can not convert %s to current locale.\n" msgid "Can not convert %s to current locale.\n"
msgstr "Nie je možné konvertovať %s na aktuálne locale.\n" msgstr "Nie je možné konvertovať %s na aktuálne locale.\n"

194
po/uk.po
View File

@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: sdcv 0.3\n" "Project-Id-Version: sdcv 0.3\n"
"Report-Msgid-Bugs-To: dushistov@mail.ru\n" "Report-Msgid-Bugs-To: dushistov@mail.ru\n"
"POT-Creation-Date: 2020-08-14 12:58+0300\n" "POT-Creation-Date: 2008-09-24 10:54+0200\n"
"PO-Revision-Date: 2004-12-14 17:54+0300\n" "PO-Revision-Date: 2004-12-14 17:54+0300\n"
"Last-Translator: <dubyk@lsl.lviv.ua>\n" "Last-Translator: <dubyk@lsl.lviv.ua>\n"
"Language-Team: Ukrainian <dubyk@lsl.lviv.ua>\n" "Language-Team: Ukrainian <dubyk@lsl.lviv.ua>\n"
@@ -19,26 +19,106 @@ msgstr ""
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
#: ../src/libwrapper.cpp:300 #: src/sdcv.cpp:108
#, fuzzy
msgid "display version information and exit"
msgstr "-v, --version показати номер версії і завершити роботу\n"
#: src/sdcv.cpp:110
#, fuzzy
msgid "display list of available dictionaries and exit"
msgstr ""
"-l, --list-dicts показати список доступних словників і завершити "
"роботу\n"
#: src/sdcv.cpp:112
#, fuzzy
msgid "for search use only dictionary with this bookname"
msgstr ""
"-u, --use-dict ім`я словника для пошуку використовувати лише цей словник\n"
#: src/sdcv.cpp:113
msgid "bookname"
msgstr ""
#: src/sdcv.cpp:115
msgid "for use in scripts"
msgstr ""
#: src/sdcv.cpp:117
#, fuzzy
msgid "output must be in utf8"
msgstr "--utf8-output вивід програми повинен бути в utf8\n"
#: src/sdcv.cpp:119
#, fuzzy
msgid "input of sdcv in utf8"
msgstr "--utf8-input ввід програми в utf8\n"
#: src/sdcv.cpp:121
#, fuzzy
msgid "use this directory as path to stardict data directory"
msgstr ""
"--data-dir тека використовувати цю теку як шлях до stardict data "
"directory\n"
#: src/sdcv.cpp:122
msgid "path/to/dir"
msgstr ""
#: src/sdcv.cpp:129
msgid " words"
msgstr ""
#: src/sdcv.cpp:135
#, c-format
msgid "Invalid command line arguments: %s\n"
msgstr ""
#: src/sdcv.cpp:141
#, c-format
msgid "Console version of Stardict, version %s\n"
msgstr "Консольна версія Зоряного словника [Stardict], номер версії %s\n"
#: src/sdcv.cpp:170
#, fuzzy, c-format
msgid "Dictionary's name Word count\n"
msgstr "назва словника кількість слів\n"
#: src/sdcv.cpp:192
#, c-format
msgid "g_mkdir failed: %s\n"
msgstr ""
#: src/sdcv.cpp:208
msgid "Enter word or phrase: "
msgstr "ВведЁть слово або фразу: "
#: src/sdcv.cpp:216
#, c-format
msgid "There are no words/phrases to translate.\n"
msgstr "Не задано слова/фрази для перекладу.\n"
#: src/libwrapper.cpp:248
msgid "popen failed" msgid "popen failed"
msgstr "" msgstr ""
#: ../src/libwrapper.cpp:341 #: src/libwrapper.cpp:285 src/utils.cpp:67
#, c-format #, c-format
msgid "Can not convert %s to utf8.\n" msgid "Can not convert %s to utf8.\n"
msgstr "Не можу перетворити %s у utf8.\n" msgstr "Не можу перетворити %s у utf8.\n"
#: ../src/libwrapper.cpp:399 ../src/libwrapper.cpp:433 #: src/libwrapper.cpp:342 src/libwrapper.cpp:370
#, fuzzy, c-format #, fuzzy, c-format
msgid "Found %zu items, similar to %s.\n" msgid "Found %zu items, similar to %s.\n"
msgstr "Знайдено %d слів, схожих на %s.\n" msgstr "Знайдено %d слів, схожих на %s.\n"
#: ../src/libwrapper.cpp:417 #: src/libwrapper.cpp:356
#, fuzzy #, fuzzy
msgid "Your choice[-1 to abort]: " msgid "Your choice[-1 to abort]: "
msgstr "Ваш вибір: " msgstr "Ваш вибір: "
#: ../src/libwrapper.cpp:427 #: src/libwrapper.cpp:365
#, fuzzy, c-format #, fuzzy, c-format
msgid "" msgid ""
"Invalid choice.\n" "Invalid choice.\n"
@@ -47,110 +127,12 @@ msgstr ""
"Неправильний вибір.\n" "Неправильний вибір.\n"
"Повинно бути від 0 до %d.\n" "Повинно бути від 0 до %d.\n"
#: ../src/libwrapper.cpp:446 #: src/libwrapper.cpp:381
#, c-format #, c-format
msgid "Nothing similar to %s, sorry :(\n" msgid "Nothing similar to %s, sorry :(\n"
msgstr "Нічого схожого на %s, даруйте :(\n" msgstr "Нічого схожого на %s, даруйте :(\n"
#: ../src/sdcv.cpp:89 #: src/utils.cpp:45
#, fuzzy
msgid "display version information and exit"
msgstr "-v, --version показати номер версії і завершити роботу\n"
#: ../src/sdcv.cpp:91
#, fuzzy
msgid "display list of available dictionaries and exit"
msgstr ""
"-l, --list-dicts показати список доступних словників і завершити "
"роботу\n"
#: ../src/sdcv.cpp:93
#, fuzzy
msgid "for search use only dictionary with this bookname"
msgstr ""
"-u, --use-dict ім`я словника для пошуку використовувати лише цей словник\n"
#: ../src/sdcv.cpp:94
msgid "bookname"
msgstr ""
#: ../src/sdcv.cpp:96
msgid "for use in scripts"
msgstr ""
#: ../src/sdcv.cpp:98
msgid "print the result formatted as JSON"
msgstr ""
#: ../src/sdcv.cpp:100
msgid "do not fuzzy-search for similar words, only return exact matches"
msgstr ""
#: ../src/sdcv.cpp:102
#, fuzzy
msgid "output must be in utf8"
msgstr "--utf8-output вивід програми повинен бути в utf8\n"
#: ../src/sdcv.cpp:104
#, fuzzy
msgid "input of sdcv in utf8"
msgstr "--utf8-input ввід програми в utf8\n"
#: ../src/sdcv.cpp:106
#, fuzzy
msgid "use this directory as path to stardict data directory"
msgstr ""
"--data-dir тека використовувати цю теку як шлях до stardict data "
"directory\n"
#: ../src/sdcv.cpp:107
msgid "path/to/dir"
msgstr ""
#: ../src/sdcv.cpp:109
msgid ""
"only use the dictionaries in data-dir, do not search in user and system "
"directories"
msgstr ""
#: ../src/sdcv.cpp:111
msgid "colorize the output"
msgstr ""
#: ../src/sdcv.cpp:116
msgid " words"
msgstr ""
#: ../src/sdcv.cpp:122
#, c-format
msgid "Invalid command line arguments: %s\n"
msgstr ""
#: ../src/sdcv.cpp:128
#, c-format
msgid "Console version of Stardict, version %s\n"
msgstr "Консольна версія Зоряного словника [Stardict], номер версії %s\n"
#: ../src/sdcv.cpp:206
#, c-format
msgid "g_mkdir failed: %s\n"
msgstr ""
#: ../src/sdcv.cpp:222
msgid "Enter word or phrase: "
msgstr "ВведЁть слово або фразу: "
#: ../src/sdcv.cpp:230
#, c-format
msgid "There are no words/phrases to translate.\n"
msgstr "Не задано слова/фрази для перекладу.\n"
#: ../src/sdcv.cpp:242
#, fuzzy, c-format
msgid "Dictionary's name Word count\n"
msgstr "назва словника кількість слів\n"
#: ../src/utils.cpp:48
#, c-format #, c-format
msgid "Can not convert %s to current locale.\n" msgid "Can not convert %s to current locale.\n"
msgstr "Не можу перетворити %s у локальне кодування.\n" msgstr "Не можу перетворити %s у локальне кодування.\n"

View File

@@ -6,7 +6,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: sdcv 0.3\n" "Project-Id-Version: sdcv 0.3\n"
"Report-Msgid-Bugs-To: dushistov@mail.ru\n" "Report-Msgid-Bugs-To: dushistov@mail.ru\n"
"POT-Creation-Date: 2020-08-14 12:58+0300\n" "POT-Creation-Date: 2008-09-24 10:54+0200\n"
"PO-Revision-Date: 2005-1-17 00:58+0800\n" "PO-Revision-Date: 2005-1-17 00:58+0800\n"
"Last-Translator: Cai Qian <caiqian@gnome.org>\n" "Last-Translator: Cai Qian <caiqian@gnome.org>\n"
"Language-Team: Simplified Chinese\n" "Language-Team: Simplified Chinese\n"
@@ -15,26 +15,101 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: ../src/libwrapper.cpp:300 #: src/sdcv.cpp:108
#, fuzzy
msgid "display version information and exit"
msgstr "-v, --version 显示版本信息并退出\n"
#: src/sdcv.cpp:110
#, fuzzy
msgid "display list of available dictionaries and exit"
msgstr "-l, --list-dicts 显示可用的字典列表并退出\n"
#: src/sdcv.cpp:112
#, fuzzy
msgid "for search use only dictionary with this bookname"
msgstr "-u, --use-dict 字典名 只使用指定的字典进行单词搜索\n"
#: src/sdcv.cpp:113
msgid "bookname"
msgstr ""
#: src/sdcv.cpp:115
msgid "for use in scripts"
msgstr ""
#: src/sdcv.cpp:117
#, fuzzy
msgid "output must be in utf8"
msgstr "--utf8-output 输出必须是 UTF-8\n"
#: src/sdcv.cpp:119
#, fuzzy
msgid "input of sdcv in utf8"
msgstr "--utf8-input sdcv 的输入为 UTF-8\n"
#: src/sdcv.cpp:121
#, fuzzy
msgid "use this directory as path to stardict data directory"
msgstr "--data-dir 目录路径 指定 Stardict 数据所在目录的路径\n"
#: src/sdcv.cpp:122
msgid "path/to/dir"
msgstr ""
#: src/sdcv.cpp:129
msgid " words"
msgstr ""
#: src/sdcv.cpp:135
#, c-format
msgid "Invalid command line arguments: %s\n"
msgstr ""
#: src/sdcv.cpp:141
#, c-format
msgid "Console version of Stardict, version %s\n"
msgstr "Stardict 的控制台版本,版本为 %s\n"
#: src/sdcv.cpp:170
#, fuzzy, c-format
msgid "Dictionary's name Word count\n"
msgstr "字典名 单词量\n"
#: src/sdcv.cpp:192
#, c-format
msgid "g_mkdir failed: %s\n"
msgstr ""
#: src/sdcv.cpp:208
msgid "Enter word or phrase: "
msgstr "请输入单词或短语:"
#: src/sdcv.cpp:216
#, c-format
msgid "There are no words/phrases to translate.\n"
msgstr "没有供翻译的单词或短语。\n"
#: src/libwrapper.cpp:248
msgid "popen failed" msgid "popen failed"
msgstr "" msgstr ""
#: ../src/libwrapper.cpp:341 #: src/libwrapper.cpp:285 src/utils.cpp:67
#, c-format #, c-format
msgid "Can not convert %s to utf8.\n" msgid "Can not convert %s to utf8.\n"
msgstr "无法将 %s 转换为 UTF-8。\n" msgstr "无法将 %s 转换为 UTF-8。\n"
#: ../src/libwrapper.cpp:399 ../src/libwrapper.cpp:433 #: src/libwrapper.cpp:342 src/libwrapper.cpp:370
#, fuzzy, c-format #, fuzzy, c-format
msgid "Found %zu items, similar to %s.\n" msgid "Found %zu items, similar to %s.\n"
msgstr "发现 %d 条记录和 %s 相似。\n" msgstr "发现 %d 条记录和 %s 相似。\n"
#: ../src/libwrapper.cpp:417 #: src/libwrapper.cpp:356
#, fuzzy #, fuzzy
msgid "Your choice[-1 to abort]: " msgid "Your choice[-1 to abort]: "
msgstr "您的选择为:" msgstr "您的选择为:"
#: ../src/libwrapper.cpp:427 #: src/libwrapper.cpp:365
#, fuzzy, c-format #, fuzzy, c-format
msgid "" msgid ""
"Invalid choice.\n" "Invalid choice.\n"
@@ -43,105 +118,12 @@ msgstr ""
"无效的选择。\n" "无效的选择。\n"
"必须是 0 到 %d。\n" "必须是 0 到 %d。\n"
#: ../src/libwrapper.cpp:446 #: src/libwrapper.cpp:381
#, c-format #, c-format
msgid "Nothing similar to %s, sorry :(\n" msgid "Nothing similar to %s, sorry :(\n"
msgstr "对不起,没有发现和 %s 相似的 :(\n" msgstr "对不起,没有发现和 %s 相似的 :(\n"
#: ../src/sdcv.cpp:89 #: src/utils.cpp:45
#, fuzzy
msgid "display version information and exit"
msgstr "-v, --version 显示版本信息并退出\n"
#: ../src/sdcv.cpp:91
#, fuzzy
msgid "display list of available dictionaries and exit"
msgstr "-l, --list-dicts 显示可用的字典列表并退出\n"
#: ../src/sdcv.cpp:93
#, fuzzy
msgid "for search use only dictionary with this bookname"
msgstr "-u, --use-dict 字典名 只使用指定的字典进行单词搜索\n"
#: ../src/sdcv.cpp:94
msgid "bookname"
msgstr ""
#: ../src/sdcv.cpp:96
msgid "for use in scripts"
msgstr ""
#: ../src/sdcv.cpp:98
msgid "print the result formatted as JSON"
msgstr ""
#: ../src/sdcv.cpp:100
msgid "do not fuzzy-search for similar words, only return exact matches"
msgstr ""
#: ../src/sdcv.cpp:102
#, fuzzy
msgid "output must be in utf8"
msgstr "--utf8-output 输出必须是 UTF-8\n"
#: ../src/sdcv.cpp:104
#, fuzzy
msgid "input of sdcv in utf8"
msgstr "--utf8-input sdcv 的输入为 UTF-8\n"
#: ../src/sdcv.cpp:106
#, fuzzy
msgid "use this directory as path to stardict data directory"
msgstr "--data-dir 目录路径 指定 Stardict 数据所在目录的路径\n"
#: ../src/sdcv.cpp:107
msgid "path/to/dir"
msgstr ""
#: ../src/sdcv.cpp:109
msgid ""
"only use the dictionaries in data-dir, do not search in user and system "
"directories"
msgstr ""
#: ../src/sdcv.cpp:111
msgid "colorize the output"
msgstr ""
#: ../src/sdcv.cpp:116
msgid " words"
msgstr ""
#: ../src/sdcv.cpp:122
#, c-format
msgid "Invalid command line arguments: %s\n"
msgstr ""
#: ../src/sdcv.cpp:128
#, c-format
msgid "Console version of Stardict, version %s\n"
msgstr "Stardict 的控制台版本,版本为 %s\n"
#: ../src/sdcv.cpp:206
#, c-format
msgid "g_mkdir failed: %s\n"
msgstr ""
#: ../src/sdcv.cpp:222
msgid "Enter word or phrase: "
msgstr "请输入单词或短语:"
#: ../src/sdcv.cpp:230
#, c-format
msgid "There are no words/phrases to translate.\n"
msgstr "没有供翻译的单词或短语。\n"
#: ../src/sdcv.cpp:242
#, fuzzy, c-format
msgid "Dictionary's name Word count\n"
msgstr "字典名 单词量\n"
#: ../src/utils.cpp:48
#, c-format #, c-format
msgid "Can not convert %s to current locale.\n" msgid "Can not convert %s to current locale.\n"
msgstr "无法将 %s 转换为当前 Locale。\n" msgstr "无法将 %s 转换为当前 Locale。\n"

View File

@@ -8,158 +8,122 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: sdcv 0.4.2\n" "Project-Id-Version: sdcv 0.4.2\n"
"Report-Msgid-Bugs-To: dushistov@mail.ru\n" "Report-Msgid-Bugs-To: dushistov@mail.ru\n"
"POT-Creation-Date: 2020-08-14 12:58+0300\n" "POT-Creation-Date: 2006-04-24 15:18+0400\n"
"PO-Revision-Date: 2013-06-12 14:11+0800\n" "PO-Revision-Date: 2013-06-12 14:11+0800\n"
"Last-Translator: Wei-Lun Chao <bluebat@member.fsf.org>\n" "Last-Translator: Wei-Lun Chao <bluebat@member.fsf.org>\n"
"Language-Team: Chinese (traditional) <zh-l10n@linux.org.tw>\n" "Language-Team: Chinese (traditional) <zh-l10n@linux.org.tw>\n"
"Language: zh_TW\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
#: ../src/libwrapper.cpp:300 #: src/sdcv.cpp:127
msgid "popen failed"
msgstr "popen 失敗"
#: ../src/libwrapper.cpp:341
#, c-format #, c-format
msgid "Can not convert %s to utf8.\n"
msgstr "無法將 %s 轉換為 UTF-8。\n"
#: ../src/libwrapper.cpp:399 ../src/libwrapper.cpp:433
#, fuzzy, c-format
msgid "Found %zu items, similar to %s.\n"
msgstr "找到 %d 項紀錄和 %s 相似。\n"
#: ../src/libwrapper.cpp:417
msgid "Your choice[-1 to abort]: "
msgstr "您的選擇是[-1 表示放棄]"
#: ../src/libwrapper.cpp:427
#, fuzzy, c-format
msgid "" msgid ""
"Invalid choice.\n" "Unknown option.\n"
"It must be from 0 to %zu or -1.\n" "Try '%s --help' for more information.\n"
msgstr "" msgstr ""
"無效的選擇。\n" "不明選項。\n"
"必須是 0 到 %d 之間或 -1。\n" "更多資訊請看 '%s --help'。\n"
#: ../src/libwrapper.cpp:446 #: src/sdcv.cpp:134
#, c-format #, c-format
msgid "Nothing similar to %s, sorry :(\n" msgid "Usage: %s [OPTIONS] words\n"
msgstr "抱歉,沒有和 %s 相似者 :(\n" msgstr "用法:%s [選項] 單字…\n"
#: ../src/sdcv.cpp:89 #: src/sdcv.cpp:135
#, fuzzy #, c-format
msgid "display version information and exit" msgid "-h, --help display this help and exit\n"
msgstr "-h, --help 顯示本輔助並離開\n"
#: src/sdcv.cpp:136
#, c-format
msgid "-v, --version display version information and exit\n"
msgstr "-v, --version 顯示版本資訊並離開\n" msgstr "-v, --version 顯示版本資訊並離開\n"
#: ../src/sdcv.cpp:91 #: src/sdcv.cpp:137
#, fuzzy #, c-format
msgid "display list of available dictionaries and exit" msgid "-l, --list-dicts display list of available dictionaries and exit\n"
msgstr "-l, --list-dicts 顯示可用的字典清單並離開\n" msgstr "-l, --list-dicts 顯示可用的字典清單並離開\n"
#: ../src/sdcv.cpp:93 #: src/sdcv.cpp:138
#, fuzzy #, c-format
msgid "for search use only dictionary with this bookname" msgid "-u, --use-dict bookname for search use only dictionary with this bookname\n"
msgstr "-u, --use-dict 字典名 只使用指定的字典進行單字搜尋\n" msgstr "-u, --use-dict 字典名 只使用指定的字典進行單字搜尋\n"
#: ../src/sdcv.cpp:94 #: src/sdcv.cpp:139
msgid "bookname" #, c-format
msgstr "" msgid "-n, --non-interactive for use in scripts\n"
msgstr "-n, --non-interactive 在指令稿中使用\n"
#: ../src/sdcv.cpp:96 #: src/sdcv.cpp:140
msgid "for use in scripts" #, c-format
msgstr "" msgid "--utf8-output output must be in utf8\n"
#: ../src/sdcv.cpp:98
msgid "print the result formatted as JSON"
msgstr ""
#: ../src/sdcv.cpp:100
msgid "do not fuzzy-search for similar words, only return exact matches"
msgstr ""
#: ../src/sdcv.cpp:102
#, fuzzy
msgid "output must be in utf8"
msgstr "--utf8-output 輸出必須是 UTF-8\n" msgstr "--utf8-output 輸出必須是 UTF-8\n"
#: ../src/sdcv.cpp:104 #: src/sdcv.cpp:141
#, fuzzy #, c-format
msgid "input of sdcv in utf8" msgid "--utf8-input input of sdcv in utf8\n"
msgstr "--utf8-input sdcv 的輸入為 UTF-8\n" msgstr "--utf8-input sdcv 的輸入為 UTF-8\n"
#: ../src/sdcv.cpp:106 #: src/sdcv.cpp:142
#, fuzzy #, c-format
msgid "use this directory as path to stardict data directory" msgid "--data-dir path/to/dir use this directory as path to stardict data directory\n"
msgstr "--data-dir 目錄路徑 指定 Stardict 資料所在目錄的路徑\n" msgstr "--data-dir 目錄路徑 指定 Stardict 資料所在目錄的路徑\n"
#: ../src/sdcv.cpp:107 #: src/sdcv.cpp:148
msgid "path/to/dir"
msgstr ""
#: ../src/sdcv.cpp:109
msgid ""
"only use the dictionaries in data-dir, do not search in user and system "
"directories"
msgstr ""
#: ../src/sdcv.cpp:111
msgid "colorize the output"
msgstr ""
#: ../src/sdcv.cpp:116
msgid " words"
msgstr ""
#: ../src/sdcv.cpp:122
#, c-format
msgid "Invalid command line arguments: %s\n"
msgstr ""
#: ../src/sdcv.cpp:128
#, c-format #, c-format
msgid "Console version of Stardict, version %s\n" msgid "Console version of Stardict, version %s\n"
msgstr "Stardict 的主控臺版本,版本為 %s\n" msgstr "Stardict 的主控臺版本,版本為 %s\n"
#: ../src/sdcv.cpp:206 #: src/sdcv.cpp:169
#, c-format
msgid "g_mkdir failed: %s\n"
msgstr "g_mkdir 失敗:%s\n"
#: ../src/sdcv.cpp:222
msgid "Enter word or phrase: "
msgstr "請輸入單字或片語:"
#: ../src/sdcv.cpp:230
#, c-format
msgid "There are no words/phrases to translate.\n"
msgstr "沒有可供翻譯的單字或片語。\n"
#: ../src/sdcv.cpp:242
#, c-format #, c-format
msgid "Dictionary's name Word count\n" msgid "Dictionary's name Word count\n"
msgstr "字典名稱 單字數量\n" msgstr "字典名稱 單字數量\n"
#: ../src/utils.cpp:48 #: src/sdcv.cpp:191
#, fuzzy, c-format #, c-format
msgid "Can not convert %s to current locale.\n" msgid "g_mkdir failed: %s\n"
msgstr "g_mkdir 失敗:%s\n"
#: src/sdcv.cpp:207
msgid "Enter word or phrase: "
msgstr "請輸入單字或片語:"
#: src/sdcv.cpp:215
#, c-format
msgid "There are no words/phrases to translate.\n"
msgstr "沒有可供翻譯的單字或片語。\n"
#: src/libwrapper.cpp:245
msgid "popen failed"
msgstr "popen 失敗"
#: src/libwrapper.cpp:282
#, c-format
msgid "Can not convert %s to utf8.\n"
msgstr "無法將 %s 轉換為 UTF-8。\n" msgstr "無法將 %s 轉換為 UTF-8。\n"
#~ msgid "" #: src/libwrapper.cpp:339 src/libwrapper.cpp:371
#~ "Unknown option.\n" #, c-format
#~ "Try '%s --help' for more information.\n" msgid "Found %d items, similar to %s.\n"
#~ msgstr "" msgstr "找到 %d 項紀錄和 %s 相似。\n"
#~ "不明選項。\n"
#~ "更多資訊請看 '%s --help'。\n"
#~ msgid "Usage: %s [OPTIONS] words\n" #: src/libwrapper.cpp:352
#~ msgstr "用法:%s [選項] 單字…\n" #, c-format
msgid "Your choice[-1 to abort]: "
msgstr "您的選擇是[-1 表示放棄]"
#~ msgid "-h, --help display this help and exit\n" #: src/libwrapper.cpp:366
#~ msgstr "-h, --help 顯示本輔助並離開\n" #, c-format
msgid ""
"Invalid choise.\n"
"It must be from 0 to %d or -1.\n"
msgstr ""
"無效的選擇。\n"
"必須是 0 到 %d 之間或 -1。\n"
#~ msgid "-n, --non-interactive for use in scripts\n" #: src/libwrapper.cpp:382
#~ msgstr "-n, --non-interactive 在指令稿中使用\n" #, c-format
msgid "Nothing similar to %s, sorry :(\n"
msgstr "抱歉,沒有和 %s 相似者 :(\n"

116
src/core.rs Normal file
View File

@@ -0,0 +1,116 @@
use ini::Ini;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::path::PathBuf;
use std::error::Error;
use index::DictionaryIndex;
use index::MemoryDictionaryIndex;
use index::DiskDictionaryIndex;
use data::DictionaryData;
use data::SimpleDictionaryData;
pub struct Dictionary {
ifo_file_name: PathBuf,
wordcount : usize,
bookname: String,
sametypesequence: String,
index: Box<DictionaryIndex>,
data: Box<DictionaryData>
}
impl Dictionary {
pub fn new(ifo_file_name: &Path) -> Result<Dictionary, String> {
let basename = try!(try!(ifo_file_name.file_stem().ok_or("bad ifo file name")).to_str().ok_or("can not convert file name to unicode"));
let dict_dir = try!(ifo_file_name.parent().ok_or("bad ifo directory name"));
let mut file = try!(File::open(ifo_file_name).map_err(|err| err.to_string()));
let mut ifo_content = String::new();
match file.read_to_string(&mut ifo_content) {
Ok(read_bytes) => { if read_bytes < 3 { return Result::Err("ifo file too small".to_string()) } },
Result::Err(err) => return Result::Err(err.to_string())
};
let content =
if ifo_content.starts_with("\u{feff}") {
&ifo_content[1..]
} else {
&ifo_content[0..]
};
static DICT_MAGIC_DATA: &'static str = "StarDict's dict ifo file";
if !content.starts_with(DICT_MAGIC_DATA) {
return Result::Err("ifo magic not match".to_string());
}
let ifo_cfg = try!(Ini::load_from_str(content).map_err(|err| err.msg));
let section = try!(ifo_cfg.section(None::<String>).ok_or("ifo: parse none section error".to_string()));
let wordcount = try!(section.get("wordcount").ok_or("no wordcount".to_string()));
let wordcount = try!(wordcount.parse::<usize>().map_err(|err| err.description().to_string()));
let idxfilesize = try!(section.get("idxfilesize").ok_or("no idxfilesize".to_string()));
let bookname = try!(section.get("bookname").ok_or("no bookname".to_string()));
let sametypesequence = try!(section.get("sametypesequence").ok_or("no sametypesequence".to_string()));
let idx_path = dict_dir.join(basename.to_string() + ".idx");
let idx_path_gz = dict_dir.join(basename.to_string() + ".idx.gz");
let mut index: Box<DictionaryIndex>;
if idx_path.exists() && idx_path.is_file() {
let disk_dict = try!(DiskDictionaryIndex::new(wordcount, &idx_path));
index = Box::new(disk_dict);
} else if idx_path_gz.exists() && idx_path_gz.is_file() {
let mem_dict = try!(MemoryDictionaryIndex::new_from_gzip(wordcount, &idx_path_gz));
index = Box::new(mem_dict);
} else {
return Result::Err(format!("no index file for {}", ifo_file_name.to_str().unwrap()));
}
let data_path = dict_dir.join(basename.to_string() + ".dict");
let data_path_dz = dict_dir.join(basename.to_string() + ".dict.dz");
let mut data: Box<DictionaryData>;
if data_path.exists() && data_path.is_file() {
let simple_data = try!(SimpleDictionaryData::new(data_path.as_path()));
data = Box::new(simple_data);
} else if data_path_dz.exists() && data_path_dz.is_file() {
return Result::Err("reading dict.dz not implemented".to_string());
} else {
return Result::Err(format!("no data file for {}", ifo_file_name.to_str().unwrap()));
}
Result::Ok(Dictionary{ifo_file_name : ifo_file_name.to_path_buf(), wordcount : wordcount,
bookname : bookname.clone(), sametypesequence: sametypesequence.clone(),
index : index, data: data})
}
pub fn find(&mut self, key: &str) -> Option<String> {
match self.index.find(key) {
Err(idx) => None,
Ok(idx) => {
let key_pos = self.index.get_key(idx);
Some(self.data.get_data(key_pos.1, key_pos.2).unwrap())
}
}
}
}
#[cfg(test)]
mod test {
use super::*;
use std::path::Path;
#[test]
fn open_dict() {
let dic = Dictionary::new(Path::new("tests/stardict-test_dict-2.4.2/test_dict.ifo")).unwrap();
assert_eq!(dic.wordcount, 1_usize);
assert_eq!(dic.bookname, "test_dict");
assert_eq!(dic.sametypesequence, "x");
}
#[test]
fn find_in_dict() {
let mut dic = Dictionary::new(Path::new("tests/stardict-test_dict-2.4.2/test_dict.ifo")).unwrap();
assert_eq!(dic.find("test").unwrap(), "<k>test</k>\ntest passed");
}
}

33
src/data.rs Normal file
View File

@@ -0,0 +1,33 @@
use std::fs::File;
use std::io::Read;
use std::io::Seek;
use std::path::Path;
use std::io::SeekFrom;
use std::str::from_utf8;
pub trait DictionaryData {
fn get_data(& mut self, offset: u64, length: usize) -> Result<String, String>;
}
pub struct SimpleDictionaryData {
data_file: File,
}
impl DictionaryData for SimpleDictionaryData {
fn get_data(& mut self, offset: u64, length: usize) -> Result<String, String> {
try!(self.data_file.seek(SeekFrom::Start(offset)).map_err(|err| err.to_string()));
let mut buffer = Vec::<u8>::with_capacity(length);
buffer.resize(length, 0_u8);
try!(self.data_file.read_exact(& mut buffer).map_err(|err| err.to_string()));
let utf8_s = try!(String::from_utf8(buffer).map_err(|err| "invalid utf-8 data in data".to_string()));
Result::Ok(utf8_s)
}
}
impl SimpleDictionaryData {
pub fn new(data_file_name: &Path) ->Result<SimpleDictionaryData, String> {
let file = try!(File::open(data_file_name).map_err(|err| err.to_string()));
Result::Ok(SimpleDictionaryData{data_file : file})
}
}

View File

@@ -21,24 +21,25 @@
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/ */
//#define HAVE_MMAP //it will defined in config.h. this can be done by configure.in with a AC_FUNC_MMAP. //#define HAVE_MMAP //it will defined in config.h. this can be done by configure.in with a AC_FUNC_MMAP.
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "config.h" # include "config.h"
#endif #endif
#include <cassert> #include <cassert>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <fcntl.h>
#include <limits.h>
#include <unistd.h> #include <unistd.h>
#include <limits.h>
#include <fcntl.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "dictziplib.hpp" #include "dictziplib.hpp"
#define USE_CACHE 1 #define USE_CACHE 1
@@ -56,424 +57,426 @@
/* For gzip-compatible header, as defined in RFC 1952 */ /* For gzip-compatible header, as defined in RFC 1952 */
/* Magic for GZIP (rfc1952) */ /* Magic for GZIP (rfc1952) */
#define GZ_MAGIC1 0x1f /* First magic byte */ #define GZ_MAGIC1 0x1f /* First magic byte */
#define GZ_MAGIC2 0x8b /* Second magic byte */ #define GZ_MAGIC2 0x8b /* Second magic byte */
/* FLaGs (bitmapped), from rfc1952 */ /* FLaGs (bitmapped), from rfc1952 */
#define GZ_FTEXT 0x01 /* Set for ASCII text */ #define GZ_FTEXT 0x01 /* Set for ASCII text */
#define GZ_FHCRC 0x02 /* Header CRC16 */ #define GZ_FHCRC 0x02 /* Header CRC16 */
#define GZ_FEXTRA 0x04 /* Optional field (random access index) */ #define GZ_FEXTRA 0x04 /* Optional field (random access index) */
#define GZ_FNAME 0x08 /* Original name */ #define GZ_FNAME 0x08 /* Original name */
#define GZ_COMMENT 0x10 /* Zero-terminated, human-readable comment */ #define GZ_COMMENT 0x10 /* Zero-terminated, human-readable comment */
#define GZ_MAX 2 /* Maximum compression */ #define GZ_MAX 2 /* Maximum compression */
#define GZ_FAST 4 /* Fasted compression */ #define GZ_FAST 4 /* Fasted compression */
/* These are from rfc1952 */ /* These are from rfc1952 */
#define GZ_OS_FAT 0 /* FAT filesystem (MS-DOS, OS/2, NT/Win32) */ #define GZ_OS_FAT 0 /* FAT filesystem (MS-DOS, OS/2, NT/Win32) */
#define GZ_OS_AMIGA 1 /* Amiga */ #define GZ_OS_AMIGA 1 /* Amiga */
#define GZ_OS_VMS 2 /* VMS (or OpenVMS) */ #define GZ_OS_VMS 2 /* VMS (or OpenVMS) */
#define GZ_OS_UNIX 3 /* Unix */ #define GZ_OS_UNIX 3 /* Unix */
#define GZ_OS_VMCMS 4 /* VM/CMS */ #define GZ_OS_VMCMS 4 /* VM/CMS */
#define GZ_OS_ATARI 5 /* Atari TOS */ #define GZ_OS_ATARI 5 /* Atari TOS */
#define GZ_OS_HPFS 6 /* HPFS filesystem (OS/2, NT) */ #define GZ_OS_HPFS 6 /* HPFS filesystem (OS/2, NT) */
#define GZ_OS_MAC 7 /* Macintosh */ #define GZ_OS_MAC 7 /* Macintosh */
#define GZ_OS_Z 8 /* Z-System */ #define GZ_OS_Z 8 /* Z-System */
#define GZ_OS_CPM 9 /* CP/M */ #define GZ_OS_CPM 9 /* CP/M */
#define GZ_OS_TOPS20 10 /* TOPS-20 */ #define GZ_OS_TOPS20 10 /* TOPS-20 */
#define GZ_OS_NTFS 11 /* NTFS filesystem (NT) */ #define GZ_OS_NTFS 11 /* NTFS filesystem (NT) */
#define GZ_OS_QDOS 12 /* QDOS */ #define GZ_OS_QDOS 12 /* QDOS */
#define GZ_OS_ACORN 13 /* Acorn RISCOS */ #define GZ_OS_ACORN 13 /* Acorn RISCOS */
#define GZ_OS_UNKNOWN 255 /* unknown */ #define GZ_OS_UNKNOWN 255 /* unknown */
#define GZ_RND_S1 'R' /* First magic for random access format */ #define GZ_RND_S1 'R' /* First magic for random access format */
#define GZ_RND_S2 'A' /* Second magic for random access format */ #define GZ_RND_S2 'A' /* Second magic for random access format */
#define GZ_ID1 0 /* GZ_MAGIC1 */ #define GZ_ID1 0 /* GZ_MAGIC1 */
#define GZ_ID2 1 /* GZ_MAGIC2 */ #define GZ_ID2 1 /* GZ_MAGIC2 */
#define GZ_CM 2 /* Compression Method (Z_DEFALTED) */ #define GZ_CM 2 /* Compression Method (Z_DEFALTED) */
#define GZ_FLG 3 /* FLaGs (see above) */ #define GZ_FLG 3 /* FLaGs (see above) */
#define GZ_MTIME 4 /* Modification TIME */ #define GZ_MTIME 4 /* Modification TIME */
#define GZ_XFL 8 /* eXtra FLags (GZ_MAX or GZ_FAST) */ #define GZ_XFL 8 /* eXtra FLags (GZ_MAX or GZ_FAST) */
#define GZ_OS 9 /* Operating System */ #define GZ_OS 9 /* Operating System */
#define GZ_XLEN 10 /* eXtra LENgth (16bit) */ #define GZ_XLEN 10 /* eXtra LENgth (16bit) */
#define GZ_FEXTRA_START 12 /* Start of extra fields */ #define GZ_FEXTRA_START 12 /* Start of extra fields */
#define GZ_SI1 12 /* Subfield ID1 */ #define GZ_SI1 12 /* Subfield ID1 */
#define GZ_SI2 13 /* Subfield ID2 */ #define GZ_SI2 13 /* Subfield ID2 */
#define GZ_SUBLEN 14 /* Subfield length (16bit) */ #define GZ_SUBLEN 14 /* Subfield length (16bit) */
#define GZ_VERSION 16 /* Version for subfield format */ #define GZ_VERSION 16 /* Version for subfield format */
#define GZ_CHUNKLEN 18 /* Chunk length (16bit) */ #define GZ_CHUNKLEN 18 /* Chunk length (16bit) */
#define GZ_CHUNKCNT 20 /* Number of chunks (16bit) */ #define GZ_CHUNKCNT 20 /* Number of chunks (16bit) */
#define GZ_RNDDATA 22 /* Random access data (16bit) */ #define GZ_RNDDATA 22 /* Random access data (16bit) */
#define DICT_UNKNOWN 0
#define DICT_TEXT 1
#define DICT_GZIP 2
#define DICT_DZIP 3
#define DICT_UNKNOWN 0
#define DICT_TEXT 1
#define DICT_GZIP 2
#define DICT_DZIP 3
int DictData::read_header(const std::string &fname, int computeCRC) int DictData::read_header(const std::string &fname, int computeCRC)
{ {
FILE *str; FILE *str;
int id1, id2, si1, si2; int id1, id2, si1, si2;
char buffer[BUFFERSIZE]; char buffer[BUFFERSIZE];
int extraLength, subLength; int extraLength, subLength;
int i; int i;
char *pt; char *pt;
int c; int c;
struct stat sb; struct stat sb;
unsigned long crc = crc32(0L, Z_NULL, 0); unsigned long crc = crc32( 0L, Z_NULL, 0 );
int count; int count;
unsigned long offset; unsigned long offset;
if (!(str = fopen(fname.c_str(), "rb"))) { if (!(str = fopen(fname.c_str(), "rb"))) {
//err_fatal_errno( __FUNCTION__, //err_fatal_errno( __FUNCTION__,
// "Cannot open data file \"%s\" for read\n", filename ); // "Cannot open data file \"%s\" for read\n", filename );
return -1; return -1;
} }
this->headerLength = GZ_XLEN - 1; this->headerLength = GZ_XLEN - 1;
this->type = DICT_UNKNOWN; this->type = DICT_UNKNOWN;
id1 = getc(str); id1 = getc( str );
id2 = getc(str); id2 = getc( str );
if (id1 != GZ_MAGIC1 || id2 != GZ_MAGIC2) { if (id1 != GZ_MAGIC1 || id2 != GZ_MAGIC2) {
this->type = DICT_TEXT; this->type = DICT_TEXT;
fstat(fileno(str), &sb); fstat( fileno( str ), &sb );
this->compressedLength = this->length = sb.st_size; this->compressedLength = this->length = sb.st_size;
this->origFilename = fname; this->origFilename = fname;
this->mtime = sb.st_mtime; this->mtime = sb.st_mtime;
if (computeCRC) { if (computeCRC) {
rewind(str); rewind( str );
while (!feof(str)) { while (!feof( str )) {
if ((count = fread(buffer, 1, BUFFERSIZE, str))) { if ((count = fread( buffer, 1, BUFFERSIZE, str ))) {
crc = crc32(crc, (Bytef *)buffer, count); crc = crc32(crc, (Bytef *)buffer, count);
} }
} }
} }
this->crc = crc; this->crc = crc;
fclose(str); fclose( str );
return 0; return 0;
} }
this->type = DICT_GZIP; this->type = DICT_GZIP;
this->method = getc(str); this->method = getc( str );
this->flags = getc(str); this->flags = getc( str );
this->mtime = getc(str) << 0; this->mtime = getc( str ) << 0;
this->mtime |= getc(str) << 8; this->mtime |= getc( str ) << 8;
this->mtime |= getc(str) << 16; this->mtime |= getc( str ) << 16;
this->mtime |= getc(str) << 24; this->mtime |= getc( str ) << 24;
this->extraFlags = getc(str); this->extraFlags = getc( str );
this->os = getc(str); this->os = getc( str );
if (this->flags & GZ_FEXTRA) { if (this->flags & GZ_FEXTRA) {
extraLength = getc(str) << 0; extraLength = getc( str ) << 0;
extraLength |= getc(str) << 8; extraLength |= getc( str ) << 8;
this->headerLength += extraLength + 2; this->headerLength += extraLength + 2;
si1 = getc(str); si1 = getc( str );
si2 = getc(str); si2 = getc( str );
if (si1 == GZ_RND_S1 || si2 == GZ_RND_S2) { if (si1 == GZ_RND_S1 || si2 == GZ_RND_S2) {
subLength = getc(str) << 0; subLength = getc( str ) << 0;
subLength |= getc(str) << 8; subLength |= getc( str ) << 8;
this->version = getc(str) << 0; this->version = getc( str ) << 0;
this->version |= getc(str) << 8; this->version |= getc( str ) << 8;
if (this->version != 1) { if (this->version != 1) {
//err_internal( __FUNCTION__, //err_internal( __FUNCTION__,
// "dzip header version %d not supported\n", // "dzip header version %d not supported\n",
// this->version ); // this->version );
} }
this->chunkLength = getc(str) << 0; this->chunkLength = getc( str ) << 0;
this->chunkLength |= getc(str) << 8; this->chunkLength |= getc( str ) << 8;
this->chunkCount = getc(str) << 0; this->chunkCount = getc( str ) << 0;
this->chunkCount |= getc(str) << 8; this->chunkCount |= getc( str ) << 8;
if (this->chunkCount <= 0) { if (this->chunkCount <= 0) {
fclose(str); fclose( str );
return 5; return 5;
} }
this->chunks = (int *)malloc(sizeof(this->chunks[0]) this->chunks = (int *)malloc(sizeof( this->chunks[0] )
* this->chunkCount); * this->chunkCount );
for (i = 0; i < this->chunkCount; i++) { for (i = 0; i < this->chunkCount; i++) {
this->chunks[i] = getc(str) << 0; this->chunks[i] = getc( str ) << 0;
this->chunks[i] |= getc(str) << 8; this->chunks[i] |= getc( str ) << 8;
} }
this->type = DICT_DZIP; this->type = DICT_DZIP;
} else { } else {
fseek(str, this->headerLength, SEEK_SET); fseek( str, this->headerLength, SEEK_SET );
} }
} }
if (this->flags & GZ_FNAME) { /* FIXME! Add checking against header len */ if (this->flags & GZ_FNAME) { /* FIXME! Add checking against header len */
pt = buffer; pt = buffer;
while ((c = getc(str)) && c != EOF) while ((c = getc( str )) && c != EOF)
*pt++ = c; *pt++ = c;
*pt = '\0'; *pt = '\0';
this->origFilename = buffer; this->origFilename = buffer;
this->headerLength += this->origFilename.length() + 1; this->headerLength += this->origFilename.length() + 1;
} else { } else {
this->origFilename = ""; this->origFilename = "";
} }
if (this->flags & GZ_COMMENT) { /* FIXME! Add checking for header len */ if (this->flags & GZ_COMMENT) { /* FIXME! Add checking for header len */
pt = buffer; pt = buffer;
while ((c = getc(str)) && c != EOF) while ((c = getc( str )) && c != EOF)
*pt++ = c; *pt++ = c;
*pt = '\0'; *pt = '\0';
comment = buffer; comment = buffer;
headerLength += comment.length() + 1; headerLength += comment.length()+1;
} else { } else {
comment = ""; comment = "";
} }
if (this->flags & GZ_FHCRC) { if (this->flags & GZ_FHCRC) {
getc(str); getc( str );
getc(str); getc( str );
this->headerLength += 2; this->headerLength += 2;
} }
if (ftell(str) != this->headerLength + 1) { if (ftell( str ) != this->headerLength + 1) {
//err_internal( __FUNCTION__, //err_internal( __FUNCTION__,
// "File position (%lu) != header length + 1 (%d)\n", // "File position (%lu) != header length + 1 (%d)\n",
// ftell( str ), this->headerLength + 1 ); // ftell( str ), this->headerLength + 1 );
} }
fseek(str, -8, SEEK_END); fseek( str, -8, SEEK_END );
this->crc = getc(str) << 0; this->crc = getc( str ) << 0;
this->crc |= getc(str) << 8; this->crc |= getc( str ) << 8;
this->crc |= getc(str) << 16; this->crc |= getc( str ) << 16;
this->crc |= getc(str) << 24; this->crc |= getc( str ) << 24;
this->length = getc(str) << 0; this->length = getc( str ) << 0;
this->length |= getc(str) << 8; this->length |= getc( str ) << 8;
this->length |= getc(str) << 16; this->length |= getc( str ) << 16;
this->length |= getc(str) << 24; this->length |= getc( str ) << 24;
this->compressedLength = ftell(str); this->compressedLength = ftell( str );
/* Compute offsets */ /* Compute offsets */
this->offsets = (unsigned long *)malloc(sizeof(this->offsets[0]) this->offsets = (unsigned long *)malloc( sizeof( this->offsets[0] )
* this->chunkCount); * this->chunkCount );
for (offset = this->headerLength + 1, i = 0; for (offset = this->headerLength + 1, i = 0;
i < this->chunkCount; i < this->chunkCount;
i++) { i++) {
this->offsets[i] = offset; this->offsets[i] = offset;
offset += this->chunks[i]; offset += this->chunks[i];
} }
fclose(str); fclose( str );
return 0; return 0;
} }
bool DictData::open(const std::string &fname, int computeCRC) bool DictData::open(const std::string& fname, int computeCRC)
{ {
struct stat sb; struct stat sb;
int fd; int fd;
this->initialized = 0; this->initialized = 0;
if (stat(fname.c_str(), &sb) || !S_ISREG(sb.st_mode)) { if (stat(fname.c_str(), &sb) || !S_ISREG(sb.st_mode)) {
//err_warning( __FUNCTION__, //err_warning( __FUNCTION__,
// "%s is not a regular file -- ignoring\n", fname ); // "%s is not a regular file -- ignoring\n", fname );
return false; return false;
} }
if (read_header(fname, computeCRC)) { if (read_header(fname, computeCRC)) {
//err_fatal( __FUNCTION__, //err_fatal( __FUNCTION__,
// "\"%s\" not in text or dzip format\n", fname ); // "\"%s\" not in text or dzip format\n", fname );
return false; return false;
} }
if ((fd = ::open(fname.c_str(), O_RDONLY)) < 0) { if ((fd = ::open(fname.c_str(), O_RDONLY )) < 0) {
//err_fatal_errno( __FUNCTION__, //err_fatal_errno( __FUNCTION__,
// "Cannot open data file \"%s\"\n", fname ); // "Cannot open data file \"%s\"\n", fname );
return false; return false;
} }
if (fstat(fd, &sb)) { if (fstat(fd, &sb)) {
//err_fatal_errno( __FUNCTION__, //err_fatal_errno( __FUNCTION__,
// "Cannot stat data file \"%s\"\n", fname ); // "Cannot stat data file \"%s\"\n", fname );
return false; return false;
} }
this->size = sb.st_size; this->size = sb.st_size;
::close(fd); ::close(fd);
if (!mapfile.open(fname.c_str(), size)) if (!mapfile.open(fname.c_str(), size))
return false; return false;
this->start = mapfile.begin(); this->start=mapfile.begin();
this->end = this->start + this->size; this->end = this->start + this->size;
for (size_t j = 0; j < DICT_CACHE_SIZE; j++) { for (size_t j = 0; j < DICT_CACHE_SIZE; j++) {
cache[j].chunk = -1; cache[j].chunk = -1;
cache[j].stamp = -1; cache[j].stamp = -1;
cache[j].inBuffer = nullptr; cache[j].inBuffer = nullptr;
cache[j].count = 0; cache[j].count = 0;
} }
return true; return true;
} }
void DictData::close() void DictData::close()
{ {
if (this->chunks) if (this->chunks)
free(this->chunks); free(this->chunks);
if (this->offsets) if (this->offsets)
free(this->offsets); free(this->offsets);
if (this->initialized) { if (this->initialized) {
if (inflateEnd(&this->zStream)) { if (inflateEnd( &this->zStream )) {
//err_internal( __FUNCTION__, //err_internal( __FUNCTION__,
// "Cannot shut down inflation engine: %s\n", // "Cannot shut down inflation engine: %s\n",
// this->zStream.msg ); // this->zStream.msg );
} }
} }
for (size_t i = 0; i < DICT_CACHE_SIZE; ++i) { for (size_t i = 0; i < DICT_CACHE_SIZE; ++i){
if (this->cache[i].inBuffer) if (this -> cache [i].inBuffer)
free(this->cache[i].inBuffer); free (this -> cache [i].inBuffer);
} }
} }
void DictData::read(char *buffer, unsigned long start, unsigned long size) void DictData::read(char *buffer, unsigned long start, unsigned long size)
{ {
char *pt; char *pt;
unsigned long end; unsigned long end;
int count; int count;
char *inBuffer; char *inBuffer;
char outBuffer[OUT_BUFFER_SIZE]; char outBuffer[OUT_BUFFER_SIZE];
int firstChunk, lastChunk; int firstChunk, lastChunk;
int firstOffset, lastOffset; int firstOffset, lastOffset;
int i; int i;
int found, target, lastStamp; int found, target, lastStamp;
static int stamp = 0; static int stamp = 0;
end = start + size; end = start + size;
//buffer = malloc( size + 1 ); //buffer = malloc( size + 1 );
//PRINTF(DBG_UNZIP, //PRINTF(DBG_UNZIP,
// ("dict_data_read( %p, %lu, %lu )\n", // ("dict_data_read( %p, %lu, %lu )\n",
//h, start, size )); //h, start, size ));
switch (this->type) {
case DICT_GZIP:
//err_fatal( __FUNCTION__,
// "Cannot seek on pure gzip format files.\n"
// "Use plain text (for performance)"
// " or dzip format (for space savings).\n" );
break;
case DICT_TEXT:
memcpy(buffer, this->start + start, size);
//buffer[size] = '\0';
break;
case DICT_DZIP:
if (!this->initialized) {
++this->initialized;
this->zStream.zalloc = nullptr;
this->zStream.zfree = nullptr;
this->zStream.opaque = nullptr;
this->zStream.next_in = 0;
this->zStream.avail_in = 0;
this->zStream.next_out = nullptr;
this->zStream.avail_out = 0;
if (inflateInit2(&this->zStream, -15) != Z_OK) {
//err_internal( __FUNCTION__,
// "Cannot initialize inflation engine: %s\n",
//this->zStream.msg );
}
}
firstChunk = start / this->chunkLength;
firstOffset = start - firstChunk * this->chunkLength;
lastChunk = end / this->chunkLength;
lastOffset = end - lastChunk * this->chunkLength;
//PRINTF(DBG_UNZIP,
// (" start = %lu, end = %lu\n"
//"firstChunk = %d, firstOffset = %d,"
//" lastChunk = %d, lastOffset = %d\n",
//start, end, firstChunk, firstOffset, lastChunk, lastOffset ));
for (pt = buffer, i = firstChunk; i <= lastChunk; i++) {
/* Access cache */ switch (this->type) {
found = 0; case DICT_GZIP:
target = 0; //err_fatal( __FUNCTION__,
lastStamp = INT_MAX; // "Cannot seek on pure gzip format files.\n"
for (size_t j = 0; j < DICT_CACHE_SIZE; j++) { // "Use plain text (for performance)"
// " or dzip format (for space savings).\n" );
break;
case DICT_TEXT:
memcpy( buffer, this->start + start, size );
//buffer[size] = '\0';
break;
case DICT_DZIP:
if (!this->initialized) {
++this->initialized;
this->zStream.zalloc = nullptr;
this->zStream.zfree = nullptr;
this->zStream.opaque = nullptr;
this->zStream.next_in = 0;
this->zStream.avail_in = 0;
this->zStream.next_out = nullptr;
this->zStream.avail_out = 0;
if (inflateInit2( &this->zStream, -15 ) != Z_OK) {
//err_internal( __FUNCTION__,
// "Cannot initialize inflation engine: %s\n",
//this->zStream.msg );
}
}
firstChunk = start / this->chunkLength;
firstOffset = start - firstChunk * this->chunkLength;
lastChunk = end / this->chunkLength;
lastOffset = end - lastChunk * this->chunkLength;
//PRINTF(DBG_UNZIP,
// (" start = %lu, end = %lu\n"
//"firstChunk = %d, firstOffset = %d,"
//" lastChunk = %d, lastOffset = %d\n",
//start, end, firstChunk, firstOffset, lastChunk, lastOffset ));
for (pt = buffer, i = firstChunk; i <= lastChunk; i++) {
/* Access cache */
found = 0;
target = 0;
lastStamp = INT_MAX;
for (size_t j = 0; j < DICT_CACHE_SIZE; j++) {
#if USE_CACHE #if USE_CACHE
if (this->cache[j].chunk == i) { if (this->cache[j].chunk == i) {
found = 1; found = 1;
target = j; target = j;
break; break;
} }
#endif #endif
if (this->cache[j].stamp < lastStamp) { if (this->cache[j].stamp < lastStamp) {
lastStamp = this->cache[j].stamp; lastStamp = this->cache[j].stamp;
target = j; target = j;
} }
} }
this->cache[target].stamp = ++stamp; this->cache[target].stamp = ++stamp;
if (found) { if (found) {
count = this->cache[target].count; count = this->cache[target].count;
inBuffer = this->cache[target].inBuffer; inBuffer = this->cache[target].inBuffer;
} else { } else {
this->cache[target].chunk = i; this->cache[target].chunk = i;
if (!this->cache[target].inBuffer) if (!this->cache[target].inBuffer)
this->cache[target].inBuffer = (char *)malloc(IN_BUFFER_SIZE); this->cache[target].inBuffer = (char *)malloc( IN_BUFFER_SIZE );
inBuffer = this->cache[target].inBuffer; inBuffer = this->cache[target].inBuffer;
if (this->chunks[i] >= OUT_BUFFER_SIZE) { if (this->chunks[i] >= OUT_BUFFER_SIZE ) {
//err_internal( __FUNCTION__, //err_internal( __FUNCTION__,
// "this->chunks[%d] = %d >= %ld (OUT_BUFFER_SIZE)\n", // "this->chunks[%d] = %d >= %ld (OUT_BUFFER_SIZE)\n",
// i, this->chunks[i], OUT_BUFFER_SIZE ); // i, this->chunks[i], OUT_BUFFER_SIZE );
} }
memcpy(outBuffer, this->start + this->offsets[i], this->chunks[i]); memcpy( outBuffer, this->start + this->offsets[i], this->chunks[i] );
this->zStream.next_in = (Bytef *)outBuffer; this->zStream.next_in = (Bytef *)outBuffer;
this->zStream.avail_in = this->chunks[i]; this->zStream.avail_in = this->chunks[i];
this->zStream.next_out = (Bytef *)inBuffer; this->zStream.next_out = (Bytef *)inBuffer;
this->zStream.avail_out = IN_BUFFER_SIZE; this->zStream.avail_out = IN_BUFFER_SIZE;
if (inflate(&this->zStream, Z_PARTIAL_FLUSH) != Z_OK) { if (inflate( &this->zStream, Z_PARTIAL_FLUSH ) != Z_OK) {
//err_fatal( __FUNCTION__, "inflate: %s\n", this->zStream.msg ); //err_fatal( __FUNCTION__, "inflate: %s\n", this->zStream.msg );
} }
if (this->zStream.avail_in) { if (this->zStream.avail_in) {
//err_internal( __FUNCTION__, //err_internal( __FUNCTION__,
// "inflate did not flush (%d pending, %d avail)\n", // "inflate did not flush (%d pending, %d avail)\n",
// this->zStream.avail_in, this->zStream.avail_out ); // this->zStream.avail_in, this->zStream.avail_out );
} }
count = IN_BUFFER_SIZE - this->zStream.avail_out; count = IN_BUFFER_SIZE - this->zStream.avail_out;
this->cache[target].count = count; this->cache[target].count = count;
} }
if (i == firstChunk) { if (i == firstChunk) {
if (i == lastChunk) { if (i == lastChunk) {
memcpy(pt, inBuffer + firstOffset, lastOffset - firstOffset); memcpy( pt, inBuffer + firstOffset, lastOffset-firstOffset);
pt += lastOffset - firstOffset; pt += lastOffset - firstOffset;
} else { } else {
if (count != this->chunkLength) { if (count != this->chunkLength ) {
//err_internal( __FUNCTION__, //err_internal( __FUNCTION__,
// "Length = %d instead of %d\n", // "Length = %d instead of %d\n",
//count, this->chunkLength ); //count, this->chunkLength );
} }
memcpy(pt, inBuffer + firstOffset, memcpy( pt, inBuffer + firstOffset,
this->chunkLength - firstOffset); this->chunkLength - firstOffset );
pt += this->chunkLength - firstOffset; pt += this->chunkLength - firstOffset;
} }
} else if (i == lastChunk) { } else if (i == lastChunk) {
memcpy(pt, inBuffer, lastOffset); memcpy( pt, inBuffer, lastOffset );
pt += lastOffset; pt += lastOffset;
} else { } else {
assert(count == this->chunkLength); assert( count == this->chunkLength );
memcpy(pt, inBuffer, this->chunkLength); memcpy( pt, inBuffer, this->chunkLength );
pt += this->chunkLength; pt += this->chunkLength;
} }
} }
//*pt = '\0'; //*pt = '\0';
break; break;
case DICT_UNKNOWN: case DICT_UNKNOWN:
//err_fatal( __FUNCTION__, "Cannot read unknown file type\n" ); //err_fatal( __FUNCTION__, "Cannot read unknown file type\n" );
break; break;
} }
} }

View File

@@ -7,50 +7,49 @@
#include "mapfile.hpp" #include "mapfile.hpp"
struct DictCache { struct DictCache {
int chunk; int chunk;
char *inBuffer; char *inBuffer;
int stamp; int stamp;
int count; int count;
}; };
class DictData class DictData {
{
public: public:
static const size_t DICT_CACHE_SIZE = 5; static const size_t DICT_CACHE_SIZE = 5;
DictData() {}
~DictData() { close(); }
bool open(const std::string &filename, int computeCRC);
void close();
void read(char *buffer, unsigned long start, unsigned long size);
DictData() {}
~DictData() { close(); }
bool open(const std::string& filename, int computeCRC);
void close();
void read(char *buffer, unsigned long start, unsigned long size);
private: private:
const char *start; /* start of mmap'd area */ const char *start; /* start of mmap'd area */
const char *end; /* end of mmap'd area */ const char *end; /* end of mmap'd area */
off_t size; /* size of mmap */ unsigned long size; /* size of mmap */
int type;
z_stream zStream;
int initialized;
int headerLength;
int method;
int flags;
time_t mtime;
int extraFlags;
int os;
int version;
int chunkLength;
int chunkCount;
int *chunks;
unsigned long *offsets; /* Sum-scan of chunks. */
std::string origFilename;
std::string comment;
unsigned long crc;
unsigned long length;
unsigned long compressedLength;
DictCache cache[DICT_CACHE_SIZE];
MapFile mapfile;
int type; int read_header(const std::string &filename, int computeCRC);
z_stream zStream;
int initialized;
int headerLength;
int method;
int flags;
time_t mtime;
int extraFlags;
int os;
int version;
int chunkLength;
int chunkCount;
int *chunks;
unsigned long *offsets; /* Sum-scan of chunks. */
std::string origFilename;
std::string comment;
unsigned long crc;
off_t length;
unsigned long compressedLength;
DictCache cache[DICT_CACHE_SIZE];
MapFile mapfile;
int read_header(const std::string &filename, int computeCRC);
}; };

View File

@@ -33,6 +33,7 @@ The Levenshtein distance algorithm has been used in:
* Plagiarism detection * Plagiarism detection
*/ */
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
@@ -55,39 +56,43 @@ Enhanced Dynamic Programming ASM Algorithm"
static inline int minimum(const int a, const int b, const int c) static inline int minimum(const int a, const int b, const int c)
{ {
int min = a; int min = a;
if (b < min) if ( b < min )
min = b; min = b;
if (c < min) if ( c < min )
min = c; min = c;
return min; return min;
} }
int EditDistance::CalEditDistance(const gunichar *s, const gunichar *t, const int limit) int EditDistance::CalEditDistance(const gunichar *s,const gunichar *t,const int limit)
/*Compute levenshtein distance between s and t, this is using QUICK algorithm*/ /*Compute levenshtein distance between s and t, this is using QUICK algorithm*/
{ {
int n = 0, m = 0, iLenDif, k, i, j, cost; int n=0,m=0,iLenDif,k,i,j,cost;
// Remove leftmost matching portion of strings // Remove leftmost matching portion of strings
while (*s && (*s == *t)) { while ( *s && (*s==*t) )
{
s++; s++;
t++; t++;
}
while (s[n]) {
n++;
}
while (t[m]) {
m++;
} }
while (s[n])
{
n++;
}
while (t[m])
{
m++;
}
// Remove rightmost matching portion of strings by decrement n and m. // Remove rightmost matching portion of strings by decrement n and m.
while (n && m && (*(s + n - 1) == *(t + m - 1))) { while ( n && m && (*(s+n-1)==*(t+m-1)) )
n--; {
m--; n--;m--;
} }
if (m == 0 || n == 0 || d == nullptr) if ( m==0 || n==0 || d==nullptr )
return (m + n); return (m+n);
if (m < n) { if ( m < n )
const gunichar *temp = s; {
const gunichar * temp = s;
int itemp = n; int itemp = n;
s = t; s = t;
t = temp; t = temp;
@@ -95,51 +100,55 @@ int EditDistance::CalEditDistance(const gunichar *s, const gunichar *t, const in
m = itemp; m = itemp;
} }
iLenDif = m - n; iLenDif = m - n;
if (iLenDif >= limit) if ( iLenDif >= limit )
return iLenDif; return iLenDif;
// step 1 // step 1
n++; n++;m++;
m++; // d=(int*)malloc(sizeof(int)*m*n);
// d=(int*)malloc(sizeof(int)*m*n); if ( m*n > currentelements )
if (m * n > currentelements) { {
currentelements = m * n * 2; // double the request currentelements = m*n*2; // double the request
d = static_cast<int *>(realloc(d, sizeof(int) * currentelements)); d = static_cast<int*>(realloc(d, sizeof(int) * currentelements));
if (nullptr == d) if ( nullptr == d )
return (m + n); return (m+n);
} }
// step 2, init matrix // step 2, init matrix
for (k = 0; k < n; k++) for (k=0;k<n;k++)
d[k] = k; d[k] = k;
for (k = 1; k < m; k++) for (k=1;k<m;k++)
d[k * n] = k; d[k*n] = k;
// step 3 // step 3
for (i = 1; i < n; i++) { for (i=1;i<n;i++)
{
// first calculate column, d(i,j) // first calculate column, d(i,j)
for (j = 1; j < iLenDif + i; j++) { for ( j=1;j<iLenDif+i;j++ )
cost = s[i - 1] == t[j - 1] ? 0 : 1; {
d[j * n + i] = minimum(d[(j - 1) * n + i] + 1, d[j * n + i - 1] + 1, d[(j - 1) * n + i - 1] + cost); cost = s[i-1]==t[j-1]?0:1;
d[j*n+i] = minimum(d[(j-1)*n+i]+1,d[j*n+i-1]+1,d[(j-1)*n+i-1]+cost);
#ifdef COVER_TRANSPOSITION #ifdef COVER_TRANSPOSITION
if (i >= 2 && j >= 2 && (d[j * n + i] - d[(j - 2) * n + i - 2] == 2) if ( i>=2 && j>=2 && (d[j*n+i]-d[(j-2)*n+i-2]==2)
&& (s[i - 2] == t[j - 1]) && (s[i - 1] == t[j - 2])) && (s[i-2]==t[j-1]) && (s[i-1]==t[j-2]) )
d[j * n + i]--; d[j*n+i]--;
#endif #endif
} }
// second calculate row, d(k,j) // second calculate row, d(k,j)
// now j==iLenDif+i; // now j==iLenDif+i;
for (k = 1; k <= i; k++) { for ( k=1;k<=i;k++ )
cost = s[k - 1] == t[j - 1] ? 0 : 1; {
d[j * n + k] = minimum(d[(j - 1) * n + k] + 1, d[j * n + k - 1] + 1, d[(j - 1) * n + k - 1] + cost); cost = s[k-1]==t[j-1]?0:1;
d[j*n+k] = minimum(d[(j-1)*n+k]+1,d[j*n+k-1]+1,d[(j-1)*n+k-1]+cost);
#ifdef COVER_TRANSPOSITION #ifdef COVER_TRANSPOSITION
if (k >= 2 && j >= 2 && (d[j * n + k] - d[(j - 2) * n + k - 2] == 2) if ( k>=2 && j>=2 && (d[j*n+k]-d[(j-2)*n+k-2]==2)
&& (s[k - 2] == t[j - 1]) && (s[k - 1] == t[j - 2])) && (s[k-2]==t[j-1]) && (s[k-1]==t[j-2]) )
d[j * n + k]--; d[j*n+k]--;
#endif #endif
} }
// test if d(i,j) limit gets equal or exceed // test if d(i,j) limit gets equal or exceed
if (d[j * n + i] >= limit) { if ( d[j*n+i] >= limit )
return d[j * n + i]; {
return d[j*n+i];
} }
} }
// d(n-1,m-1) // d(n-1,m-1)
return d[n * m - 1]; return d[n*m-1];
} }

View File

@@ -3,24 +3,21 @@
#include <cstdlib> #include <cstdlib>
#include <glib.h> #include <glib.h>
class EditDistance class EditDistance {
{
public: public:
EditDistance() EditDistance() {
{
currentelements = 2500; // It's enough for most conditions :-) currentelements = 2500; // It's enough for most conditions :-)
d = static_cast<int *>(malloc(sizeof(int) * currentelements)); d = static_cast<int *>(malloc(sizeof(int)*currentelements));
} }
~EditDistance() ~EditDistance() {
{
if (d != nullptr) if (d != nullptr)
free(d); free(d);
} }
EditDistance(const EditDistance &) = delete; EditDistance(const EditDistance&) = delete;
EditDistance &operator=(const EditDistance &) = delete; EditDistance& operator=(const EditDistance&) = delete;
int CalEditDistance(const gunichar *s, const gunichar *t, const int limit); int CalEditDistance( const gunichar *s, const gunichar *t, const int limit );
private: private:
int *d; int *d;
int currentelements; int currentelements;
}; };

308
src/index.rs Normal file
View File

@@ -0,0 +1,308 @@
use std::str::from_utf8;
use std::cmp::Ordering;
use std::io::Cursor;
use byteorder::{NetworkEndian, ReadBytesExt};
use mem_mapped_file::MemMappedFile;
use std::path::Path;
use std::fs::File;
use std::io::Read;
use flate2::read::GzDecoder;
use std::error::Error;
pub trait DictionaryIndex {
fn get_key(&self, idx: usize) -> (&str, u64, usize);
fn count(&self) -> usize;
fn find(&self, key: &str) -> Result<usize, usize>;
}
pub struct MemoryDictionaryIndex {
idx_content: Vec<u8>,
wordlist: Vec<usize>,
}
fn g_ascii_strcasecmp(s1: &str, s2: &str) -> isize {
fn is_upper(c: u8) -> bool { c >= b'A' && c <= b'Z' }
fn to_lower(c: u8) -> u8 { if is_upper(c) { c - b'A' + b'a' } else { c } }
let mut it1 = s1.bytes();
let mut it2 = s2.bytes();
loop {
match (it1.next(), it2.next()) {
(None, None) => return 0,
(Some(b1), None) => return b1 as isize,
(None, Some(b2)) => return -(b2 as isize),
(Some(b1), Some(b2)) => {
let c1 = to_lower(b1) as isize;
let c2 = to_lower(b2) as isize;
if c1 != c2 {
return c1 - c2;
}
},
}
}
0_isize
}
fn stardict_strcmp(s1: &str, s2: &str) -> isize {
/*
#define ISUPPER(c) ((c) >= 'A' && (c) <= 'Z')
#define TOLOWER(c) (ISUPPER (c) ? (c) - 'A' + 'a' : (c))
gint
g_ascii_strcasecmp (const gchar *s1,
const gchar *s2)
{
gint c1, c2;
g_return_val_if_fail (s1 != NULL, 0);
g_return_val_if_fail (s2 != NULL, 0);
while (*s1 && *s2)
{
c1 = (gint)(guchar) TOLOWER (*s1);
c2 = (gint)(guchar) TOLOWER (*s2);
if (c1 != c2)
return (c1 - c2);
s1++; s2++;
}
return (((gint)(guchar) *s1) - ((gint)(guchar) *s2));
}
const gint a = g_ascii_strcasecmp(s1, s2);
if (a == 0)
return strcmp(s1, s2);
else
return a;
*/
let res = g_ascii_strcasecmp(s1, s2);
if res == 0 {
assert_eq!(s1.len(), s2.len());
for it in s1.bytes().zip(s2.bytes()) {
let (b1, b2) = it;
if b1 != b2 {
return (b1 as isize) - (b2 as isize);
}
}
0_isize
} else {
res
}
}
fn stardict_str_order(s1: &str, s2: &str) -> Ordering {
let cmp_res = stardict_strcmp(s1, s2);
if cmp_res < 0 {
Ordering::Less
} else if cmp_res > 0 {
Ordering::Greater
} else {
Ordering::Equal
}
}
fn get_key_by_offset(content: &[u8], key_pos: usize) -> (&str, u64, usize) {
let key = &content[key_pos..];
let end_of_key = key.iter().position(|&x| x == b'\0').unwrap() + key_pos;
let mut reader = Cursor::new(&content[end_of_key + 1..]);
let offset = reader.read_u32::<NetworkEndian>().unwrap() as u64;
let length = reader.read_u32::<NetworkEndian>().unwrap() as usize;
(from_utf8(&content[key_pos..end_of_key]).unwrap(), offset, length)
}
impl DictionaryIndex for MemoryDictionaryIndex {
fn get_key(&self, idx: usize) -> (&str, u64, usize) {
let key_pos = self.wordlist[idx];
get_key_by_offset(&self.idx_content, key_pos)
/*
let key = &self.idx_content[key_pos..];
let end_of_key = key.iter().position(|&x| x == b'\0').unwrap();
let mut reader = Cursor::new(&self.idx_content[end_of_key + 1..]);
let offset = reader.read_u32::<NetworkEndian>().unwrap() as u64;
let length = reader.read_u32::<NetworkEndian>().unwrap() as usize;
(from_utf8(&self.idx_content[key_pos..(key_pos + end_of_key)]).unwrap(), offset, length)*/
}
fn count(&self) -> usize {
self.wordlist.len()
}
fn find(&self, key: &str) -> Result<usize, usize> {
self.wordlist.binary_search_by(
|probe| stardict_str_order(get_key_by_offset(&self.idx_content, *probe).0, key)
)
}
}
fn get_offsets_of_phrases_in_index(expected_wordcount: usize, idx_content: &[u8]) -> Result<Vec<usize>, String> {
const PADDING_LENGTH: usize = 8;
let mut wordlist = vec![];
wordlist.reserve(expected_wordcount);
{
let mut slice = idx_content;
let mut pos : usize = 0;
while let Some(idx) = slice.iter().position(|&x| x == b'\0') {
let (head, tail) = slice.split_at(idx);
try!(from_utf8(head).map_err(|err| "invalid utf-8 in key".to_string()));
wordlist.push(pos);
pos += head.len() + PADDING_LENGTH + 1;
// +1 to skip over the NUL
if tail.len() > PADDING_LENGTH + 1 {
slice = &tail[PADDING_LENGTH + 1..];
} else {
break;
}
}
}
if wordlist.len() != expected_wordcount {
Result::Err(format!("Expect words in index {}, got {} words", expected_wordcount, wordlist.len()))
} else {
Result::Ok(wordlist)
}
}
impl MemoryDictionaryIndex {
pub fn new_from_vec(expected_wordcount: usize, idx_content: Vec<u8>) -> Result<MemoryDictionaryIndex, String> {
let wordlist = try!(get_offsets_of_phrases_in_index(expected_wordcount, idx_content.as_slice()));
Result::Ok(MemoryDictionaryIndex {idx_content: idx_content, wordlist: wordlist})
}
pub fn new_from_gzip(expected_wordcount: usize, idx_gz_file_path: &Path) -> Result<MemoryDictionaryIndex, String> {
let mut idx_file = try!(File::open(idx_gz_file_path).map_err(|err| err.description().to_string()));
let mut gzip_decoder = try!(GzDecoder::new(idx_file).map_err(|err| err.description().to_string()));
let mut idx_content = Vec::<u8>::new();
if let Result::Err(err) = gzip_decoder.read_to_end(&mut idx_content) {
return Result::Err(format!("Can not read index file: {}", err.description()));
}
let wordlist = try!(get_offsets_of_phrases_in_index(expected_wordcount, idx_content.as_slice()));
Result::Ok(MemoryDictionaryIndex {idx_content: idx_content, wordlist: wordlist})
}
}
pub struct DiskDictionaryIndex {
file_map: MemMappedFile,
wordlist: Vec<usize>,
}
impl DictionaryIndex for DiskDictionaryIndex {
fn get_key(&self, idx: usize) -> (&str, u64, usize) {
let key_pos = self.wordlist[idx];
let idx_content = self.file_map.get_chunk(0, self.file_map.len()).unwrap();
get_key_by_offset(idx_content, key_pos)
}
fn count(&self) -> usize { self.wordlist.len() }
fn find(&self, key: &str) -> Result<usize, usize> {
let idx_content = self.file_map.get_chunk(0, self.file_map.len()).unwrap();
self.wordlist.binary_search_by(
|probe| stardict_str_order(get_key_by_offset(idx_content, *probe).0, key)
)
}
}
impl DiskDictionaryIndex {
pub fn new(expected_wordcount: usize, idx_file_path: &Path) -> Result<DiskDictionaryIndex, String> {
let file_map = try!(MemMappedFile::new(idx_file_path));
let whole_map = try!(file_map.get_chunk(0, file_map.len()));
let wordlist = try!(get_offsets_of_phrases_in_index(expected_wordcount, whole_map));
Ok(DiskDictionaryIndex{file_map: file_map, wordlist: wordlist})
}
}
#[cfg(test)]
mod test {
use super::*;
use super::stardict_str_order;
use super::stardict_strcmp;
use std::path::Path;
use std::fs::File;
use std::io::Read;
use std::io::BufReader;
use std::io::BufRead;
use std::collections::HashSet;
#[test]
fn index_memory_open() {
let idx_file_name = Path::new("tests/stardict-test_dict-2.4.2/test_dict.idx");
let mut file = File::open(idx_file_name).unwrap();
let mut idx_content = Vec::<u8>::new();
file.read_to_end(&mut idx_content).unwrap();
let mut index = MemoryDictionaryIndex::new_from_vec(1, idx_content).unwrap();
assert_eq!(1_usize, index.count());
assert_eq!("test", index.get_key(0).0);
let idx_file_name = Path::new("tests/words_dic/stardict-words-2.4.2/words.idx");
let mut file = File::open(idx_file_name).unwrap();
let mut idx_content = Vec::<u8>::new();
file.read_to_end(&mut idx_content).unwrap();
let index_size = 1671704_usize;
let mut mem_index = MemoryDictionaryIndex::new_from_vec(index_size, idx_content).unwrap();
assert_eq!(index_size, mem_index.count());
let mut disk_index = DiskDictionaryIndex::new(index_size, idx_file_name).unwrap();
assert_eq!(index_size, disk_index.count());
let mut gz_mem_index = MemoryDictionaryIndex::new_from_gzip(index_size,
Path::new("tests/words_dic/stardict-words-2.4.2/words.idx.gz")).unwrap();
assert_eq!(index_size, gz_mem_index.count());
{
let mut counter: usize = 0;
let f = File::open("tests/words_dic/words.dummy").unwrap();
let mut file = BufReader::new(&f);
let mut dic_words = HashSet::new();
for line in file.lines() {
let l = line.unwrap();
dic_words.insert(l);
}
for i in 0..index.count() {
for index in [&mem_index as &DictionaryIndex, &disk_index as &DictionaryIndex, &gz_mem_index as &DictionaryIndex].iter() {
let (key, _, _) = index.get_key(i);
if !dic_words.contains(key) {
panic!("no '{}'(len {}) idx {} in dic_words", key, key.len(), i);
}
match index.find(key) {
Err(idx) => panic!("we search '{}', and not found err({})", key, idx),
Ok(idx) => assert_eq!(idx, i),
}
}
}
}
}
#[test]
fn index_stardict_strcmp_small() {
let arr_s = ["a", "b", "c", "d", "z"];
let seek = "c";
assert_eq!(arr_s.binary_search_by(|probe| stardict_str_order(probe, seek)), Ok(2));
let seek = "e";
assert_eq!(arr_s.binary_search_by(|probe| stardict_str_order(probe, seek)), Err(4));
}
#[test]
fn index_stardict_strcmp_big() {
let mut exp_res_list = vec![];
{
let f = File::open("tests/stardict_strcmp_test_data_cmp_exp.txt").unwrap();
let mut file = BufReader::new(&f);
for line in file.lines() {
let line = line.unwrap();
let exp_res = line.parse::<isize>().unwrap();
exp_res_list.push(exp_res);
}
}
let f = File::open("tests/stardict_strcmp_test_data.txt").unwrap();
let mut file = BufReader::new(&f);
let mut prev_line : String = "".to_string();
let mut counter : u8 = 0;
let mut exp_it = exp_res_list.iter();
for line in file.lines() {
if counter == 1 {
let cur_line = line.unwrap();
let res = stardict_strcmp(&prev_line, &cur_line);
let exp_res = exp_it.next().unwrap();
if res != *exp_res {
panic!("we expect {}, got {} for '{}' vs '{}'", exp_res, res, prev_line, cur_line);
}
} else {
prev_line = line.unwrap();
}
counter = (counter + 1) % 2;
}
}
}

View File

@@ -15,15 +15,14 @@
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "config.h" # include "config.h"
#endif #endif
#include <cstring> #include <cstring>
#include <cstdio> //for popen
#include <map> #include <map>
#include <memory> #include <memory>
@@ -49,410 +48,373 @@ static const char *ABR_VISFMT = ESC_GREEN;
static std::string xdxf2text(const char *p, bool colorize_output) static std::string xdxf2text(const char *p, bool colorize_output)
{ {
std::string res; std::string res;
for (; *p; ++p) { for (; *p; ++p) {
if (*p != '<') { if (*p != '<') {
if (g_str_has_prefix(p, "&gt;")) { if (g_str_has_prefix(p, "&gt;")) {
res += ">"; res += ">";
p += 3; p += 3;
} else if (g_str_has_prefix(p, "&lt;")) { } else if (g_str_has_prefix(p, "&lt;")) {
res += "<"; res += "<";
p += 3; p += 3;
} else if (g_str_has_prefix(p, "&amp;")) { } else if (g_str_has_prefix(p, "&amp;")) {
res += "&"; res += "&";
p += 4; p += 4;
} else if (g_str_has_prefix(p, "&quot;")) { } else if (g_str_has_prefix(p, "&quot;")) {
res += "\""; res += "\"";
p += 5; p += 5;
} else if (g_str_has_prefix(p, "&apos;")) { } else if (g_str_has_prefix(p, "&apos;")) {
res += "\'"; res += "\'";
p += 5; p += 5;
} else } else
res += *p; res += *p;
continue; continue;
} }
const char *next = strchr(p, '>'); const char *next = strchr(p, '>');
if (!next) if (!next)
continue; continue;
const std::string name(p + 1, next - p - 1); const std::string name(p+1, next-p-1);
if (name == "abr") if (name == "abr")
res += colorize_output ? ABR_VISFMT : ""; res += colorize_output ? ABR_VISFMT : "";
else if (name == "/abr") else if (name=="/abr")
res += colorize_output ? ESC_END : ""; res += colorize_output ? ESC_END : "";
else if (name == "k") { else if (name == "k") {
const char *begin = next; const char *begin = next;
if ((next = strstr(begin, "</k>")) != nullptr) if ((next = strstr(begin, "</k>")) != nullptr)
next += sizeof("</k>") - 1 - 1; next += sizeof("</k>") - 1 - 1;
else else
next = begin; next = begin;
} else if (name == "kref") { } else if (name == "kref") {
res += colorize_output ? KREF_VISFMT : ""; res += colorize_output ? KREF_VISFMT : "";
} else if (name == "/kref") { } else if (name == "/kref") {
res += colorize_output ? ESC_END : ""; res += colorize_output ? ESC_END : "";
} else if (name == "b") } else if (name == "b")
res += colorize_output ? ESC_BOLD : ""; res += colorize_output ? ESC_BOLD : "";
else if (name == "/b") else if (name=="/b")
res += colorize_output ? ESC_END : ""; res += colorize_output ? ESC_END : "";
else if (name == "i") else if (name == "i")
res += colorize_output ? ESC_ITALIC : ""; res += colorize_output ? ESC_ITALIC : "";
else if (name == "/i") else if (name == "/i")
res += colorize_output ? ESC_END : ""; res += colorize_output ? ESC_END : "";
else if (name == "tr") { else if (name == "tr") {
if (colorize_output) if (colorize_output)
res += TRANSCRIPTION_VISFMT; res += TRANSCRIPTION_VISFMT;
res += "["; res += "[";
} else if (name == "/tr") { } else if (name == "/tr") {
res += "]"; res += "]";
if (colorize_output) if (colorize_output)
res += ESC_END; res += ESC_END;
} else if (name == "ex") } else if (name == "ex")
res += colorize_output ? EXAMPLE_VISFMT : ""; res += colorize_output ? EXAMPLE_VISFMT : "";
else if (name == "/ex") else if (name == "/ex")
res += colorize_output ? ESC_END : ""; res += colorize_output ? ESC_END : "";
else if (!name.empty() && name[0] == 'c' && name != "co") { else if (!name.empty() && name[0] == 'c' && name != "co") {
std::string::size_type pos = name.find("code"); std::string::size_type pos = name.find("code");
if (pos != std::string::npos) { if (pos != std::string::npos) {
pos += sizeof("code=\"") - 1; pos += sizeof("code=\"") - 1;
std::string::size_type end_pos = name.find("\""); std::string::size_type end_pos = name.find("\"");
const std::string color(name, pos, end_pos - pos); const std::string color(name, pos, end_pos - pos);
res += ""; res += "";
} else { } else {
res += ""; res += "";
} }
} else if (name == "/c") } else if (name == "/c")
res += ""; res += "";
p = next; p = next;
} }
return res; return res;
} }
static std::string parse_data(const gchar *data, bool colorize_output) static std::string parse_data(const gchar *data, bool colorize_output)
{ {
if (!data) if (!data)
return ""; return "";
std::string res; std::string res;
guint32 data_size, sec_size = 0; guint32 data_size, sec_size = 0;
gchar *m_str; gchar *m_str;
const gchar *p = data; const gchar *p = data;
data_size = get_uint32(p); data_size = get_uint32(p);
p += sizeof(guint32); p += sizeof(guint32);
while (guint32(p - data) < data_size) { while (guint32(p - data)<data_size) {
switch (*p++) { switch (*p++) {
case 'h': // HTML data case 'm':
case 'w': // WikiMedia markup data case 'l': //need more work...
case 'm': // plain text, utf-8 sec_size = strlen(p);
case 'l': // not utf-8, some other locale encoding, discouraged, need more work... if (sec_size) {
sec_size = strlen(p); res+="\n";
if (sec_size) { m_str = g_strndup(p, sec_size);
res += "\n"; res += m_str;
m_str = g_strndup(p, sec_size); g_free(m_str);
res += m_str; }
g_free(m_str); sec_size++;
} break;
sec_size++; case 'g':
break; case 'x':
case 'g': // pango markup data sec_size = strlen(p);
case 'x': // xdxf if (sec_size) {
sec_size = strlen(p); res+="\n";
if (sec_size) { m_str = g_strndup(p, sec_size);
res += "\n"; res += xdxf2text(m_str, colorize_output);
m_str = g_strndup(p, sec_size); g_free(m_str);
res += xdxf2text(m_str, colorize_output); }
g_free(m_str); sec_size++;
} break;
sec_size++; case 't':
break; sec_size = strlen(p);
case 't': // english phonetic string if(sec_size){
sec_size = strlen(p); res += "\n";
if (sec_size) {
res += "\n";
if (colorize_output) if (colorize_output)
res += TRANSCRIPTION_VISFMT; res += TRANSCRIPTION_VISFMT;
res += "[" + std::string(p, sec_size) + "]"; res += "[" + std::string(p, sec_size) + "]";
if (colorize_output) if (colorize_output)
res += ESC_END; res += ESC_END;
} }
sec_size++; sec_size++;
break; break;
case 'k': // KingSoft PowerWord data case 'k':
case 'y': // chinese YinBiao or japanese kana, utf-8 case 'y':
sec_size = strlen(p); sec_size = strlen(p);
if (sec_size) if (sec_size)
res += std::string(p, sec_size); res += std::string(p, sec_size);
sec_size++; sec_size++;
break; break;
case 'W': // wav file case 'W':
case 'P': // picture data case 'P':
sec_size = get_uint32(p); sec_size = get_uint32(p);
sec_size += sizeof(guint32); sec_size += sizeof(guint32);
break; break;
} }
p += sec_size; p += sec_size;
} }
return res;
return res;
} }
void Library::SimpleLookup(const std::string &str, TSearchResultList &res_list) void Library::SimpleLookup(const std::string &str, TSearchResultList& res_list)
{ {
std::set<glong> wordIdxs; glong ind;
res_list.reserve(ndicts()); res_list.reserve(ndicts());
for (gint idict = 0; idict < ndicts(); ++idict) { for (gint idict = 0; idict < ndicts(); ++idict)
wordIdxs.clear(); if (SimpleLookupWord(str.c_str(), ind, idict))
if (SimpleLookupWord(str.c_str(), wordIdxs, idict)) res_list.push_back(
for (auto &wordIdx : wordIdxs) TSearchResult(dict_name(idict),
res_list.push_back( poGetWord(ind, idict),
TSearchResult(dict_name(idict), parse_data(poGetWordData(ind, idict), colorize_output_)));
poGetWord(wordIdx, idict),
parse_data(poGetWordData(wordIdx, idict),
colorize_output_)));
}
} }
void Library::LookupWithFuzzy(const std::string &str, TSearchResultList &res_list) void Library::LookupWithFuzzy(const std::string &str, TSearchResultList& res_list)
{ {
static const int MAXFUZZY = 10; static const int MAXFUZZY=10;
gchar *fuzzy_res[MAXFUZZY]; gchar *fuzzy_res[MAXFUZZY];
if (!Libs::LookupWithFuzzy(str.c_str(), fuzzy_res, MAXFUZZY)) if (!Libs::LookupWithFuzzy(str.c_str(), fuzzy_res, MAXFUZZY))
return; return;
for (gchar **p = fuzzy_res, **end = (fuzzy_res + MAXFUZZY); p != end && *p; ++p) { for (gchar **p = fuzzy_res, **end = (fuzzy_res + MAXFUZZY); p != end && *p; ++p) {
SimpleLookup(*p, res_list); SimpleLookup(*p, res_list);
g_free(*p); g_free(*p);
} }
} }
void Library::LookupWithRule(const std::string &str, TSearchResultList &res_list) void Library::LookupWithRule(const std::string &str, TSearchResultList& res_list)
{ {
std::vector<gchar *> match_res((MAX_MATCH_ITEM_PER_LIB)*ndicts()); std::vector<gchar *> match_res((MAX_MATCH_ITEM_PER_LIB) * ndicts());
const gint nfound = Libs::LookupWithRule(str.c_str(), &match_res[0]); const gint nfound = Libs::LookupWithRule(str.c_str(), &match_res[0]);
if (nfound == 0) if (nfound == 0)
return; return;
for (gint i = 0; i < nfound; ++i) { for (gint i = 0; i < nfound; ++i) {
SimpleLookup(match_res[i], res_list); SimpleLookup(match_res[i], res_list);
g_free(match_res[i]); g_free(match_res[i]);
} }
} }
void Library::LookupData(const std::string &str, TSearchResultList &res_list) void Library::LookupData(const std::string &str, TSearchResultList& res_list)
{ {
std::vector<std::vector<gchar *>> drl(ndicts()); std::vector<std::vector<gchar *> > drl(ndicts());
if (!Libs::LookupData(str.c_str(), &drl[0])) if (!Libs::LookupData(str.c_str(), &drl[0]))
return; return;
for (int idict = 0; idict < ndicts(); ++idict) for (int idict = 0; idict < ndicts(); ++idict)
for (gchar *res : drl[idict]) { for (gchar *res : drl[idict]) {
SimpleLookup(res, res_list); SimpleLookup(res, res_list);
g_free(res); g_free(res);
} }
} }
void Library::print_search_result(FILE *out, const TSearchResult &res, bool &first_result) void Library::print_search_result(FILE *out, const TSearchResult & res)
{ {
std::string loc_bookname, loc_def, loc_exp; std::string loc_bookname, loc_def, loc_exp;
if (!utf8_output_) { if (!utf8_output_){
loc_bookname = utf8_to_locale_ign_err(res.bookname); loc_bookname = utf8_to_locale_ign_err(res.bookname);
loc_def = utf8_to_locale_ign_err(res.def); loc_def = utf8_to_locale_ign_err(res.def);
loc_exp = utf8_to_locale_ign_err(res.exp); loc_exp = utf8_to_locale_ign_err(res.exp);
} }
if (json_) {
if (!first_result) {
fputs(",", out);
} else {
first_result = false;
}
fprintf(out, "{\"dict\": \"%s\",\"word\":\"%s\",\"definition\":\"%s\"}",
json_escape_string(res.bookname).c_str(),
json_escape_string(res.def).c_str(),
json_escape_string(res.exp).c_str());
} else { fprintf(out,
fprintf(out, "-->%s%s%s\n"
"-->%s%s%s\n" "-->%s%s%s\n"
"-->%s%s%s\n" "%s\n\n",
"%s\n\n", colorize_output_ ? NAME_OF_DICT_VISFMT : "",
colorize_output_ ? NAME_OF_DICT_VISFMT : "", utf8_output_ ? res.bookname.c_str() : loc_bookname.c_str(),
utf8_output_ ? res.bookname.c_str() : loc_bookname.c_str(), colorize_output_ ? ESC_END : "",
colorize_output_ ? ESC_END : "", colorize_output_ ? SEARCH_TERM_VISFMT : "",
colorize_output_ ? SEARCH_TERM_VISFMT : "", utf8_output_ ? res.def.c_str() : loc_def.c_str(),
utf8_output_ ? res.def.c_str() : loc_def.c_str(), colorize_output_ ? ESC_END : "",
colorize_output_ ? ESC_END : "", utf8_output_ ? res.exp.c_str() : loc_exp.c_str());
utf8_output_ ? res.exp.c_str() : loc_exp.c_str());
}
} }
namespace namespace {
{ class sdcv_pager {
class sdcv_pager final public:
{ sdcv_pager(bool ignore_env = false) {
public:
explicit sdcv_pager(bool ignore_env = false)
{
output = stdout;
if (ignore_env) {
return;
}
const gchar *pager = g_getenv("SDCV_PAGER");
if (pager && (output = popen(pager, "w")) == nullptr) {
perror(_("popen failed"));
output = stdout; output = stdout;
if (ignore_env)
return;
const gchar *pager = g_getenv("SDCV_PAGER");
if (pager && (output = popen(pager, "w")) == nullptr) {
perror(_("popen failed"));
output = stdout;
}
} }
} sdcv_pager(const sdcv_pager&) = delete;
sdcv_pager(const sdcv_pager &) = delete; sdcv_pager& operator=(const sdcv_pager&) = delete;
sdcv_pager &operator=(const sdcv_pager &) = delete; ~sdcv_pager() {
~sdcv_pager() if (output != stdout)
{ fclose(output);
if (output != stdout) {
pclose(output);
} }
} FILE *get_stream() { return output; }
FILE *get_stream() { return output; } private:
FILE *output;
};
}
private: bool Library::process_phrase(const char *loc_str, IReadLine &io, bool force)
FILE *output;
};
} // namespace
search_result Library::process_phrase(const char *loc_str, IReadLine &io, bool force)
{ {
if (nullptr == loc_str) if (nullptr == loc_str)
return SEARCH_SUCCESS; return true;
std::string query; std::string query;
analyze_query(loc_str, query); analyze_query(loc_str, query);
if (!query.empty()) if (!query.empty())
io.add_to_history(query.c_str()); io.add_to_history(query.c_str());
gsize bytes_read; gsize bytes_read;
gsize bytes_written; gsize bytes_written;
glib::Error err; glib::Error err;
search_result rval = SEARCH_SUCCESS;
glib::CharStr str; glib::CharStr str;
if (!utf8_input_) if (!utf8_input_)
str.reset(g_locale_to_utf8(loc_str, -1, &bytes_read, &bytes_written, get_addr(err))); str.reset(g_locale_to_utf8(loc_str, -1, &bytes_read, &bytes_written, get_addr(err)));
else else
str.reset(g_strdup(loc_str)); str.reset(g_strdup(loc_str));
if (nullptr == get_impl(str)) { if (nullptr == get_impl(str)) {
fprintf(stderr, _("Can not convert %s to utf8.\n"), loc_str); fprintf(stderr, _("Can not convert %s to utf8.\n"), loc_str);
fprintf(stderr, "%s\n", err->message); fprintf(stderr, "%s\n", err->message);
return SEARCH_FAILURE; return false;
} }
if (str[0] == '\0') if (str[0] == '\0')
return SEARCH_SUCCESS; return true;
TSearchResultList res_list; TSearchResultList res_list;
switch (analyze_query(get_impl(str), query)) { switch (analyze_query(get_impl(str), query)) {
case qtFUZZY: case qtFUZZY:
LookupWithFuzzy(query, res_list); LookupWithFuzzy(query, res_list);
break; break;
case qtREGEXP: case qtREGEXP:
LookupWithRule(query, res_list); LookupWithRule(query, res_list);
break; break;
case qtSIMPLE: case qtSIMPLE:
SimpleLookup(get_impl(str), res_list); SimpleLookup(get_impl(str), res_list);
if (res_list.empty() && fuzzy_) if (res_list.empty())
LookupWithFuzzy(get_impl(str), res_list); LookupWithFuzzy(get_impl(str), res_list);
break; break;
case qtDATA: case qtDATA:
LookupData(query, res_list); LookupData(query, res_list);
break; break;
default: default:
/*nothing*/; /*nothing*/;
} }
bool first_result = true; if (!res_list.empty()) {
if (json_) { /* try to be more clever, if there are
fputc('[', stdout);
}
if (!res_list.empty()) {
/* try to be more clever, if there are
one or zero results per dictionary show all one or zero results per dictionary show all
*/ */
bool show_all_results = true; bool show_all_results = true;
typedef std::map<std::string, int, std::less<std::string>> DictResMap; typedef std::map< std::string, int, std::less<std::string> > DictResMap;
if (!force) { if (!force) {
DictResMap res_per_dict; DictResMap res_per_dict;
for (const TSearchResult &search_res : res_list) { for (const TSearchResult& search_res : res_list) {
auto r = res_per_dict.equal_range(search_res.bookname); auto r = res_per_dict.equal_range(search_res.bookname);
DictResMap tmp(r.first, r.second); DictResMap tmp(r.first, r.second);
if (tmp.empty()) //there are no yet such bookname in map if (tmp.empty()) //there are no yet such bookname in map
res_per_dict.insert(DictResMap::value_type(search_res.bookname, 1)); res_per_dict.insert(DictResMap::value_type(search_res.bookname, 1));
else { else {
++((tmp.begin())->second); ++((tmp.begin())->second);
if (tmp.begin()->second > 1) { if (tmp.begin()->second > 1) {
show_all_results = false; show_all_results = false;
break; break;
} }
} }
} }
} //if (!force) }//if (!force)
if (!show_all_results && !force) { if (!show_all_results && !force) {
if (!json_) { printf(_("Found %zu items, similar to %s.\n"), res_list.size(),
printf(_("Found %zu items, similar to %s.\n"), res_list.size(), utf8_output_ ? get_impl(str) : utf8_to_locale_ign_err(get_impl(str)).c_str());
utf8_output_ ? get_impl(str) : utf8_to_locale_ign_err(get_impl(str)).c_str()); for (size_t i = 0; i < res_list.size(); ++i) {
}
for (size_t i = 0; i < res_list.size(); ++i) {
const std::string loc_bookname = utf8_to_locale_ign_err(res_list[i].bookname); const std::string loc_bookname = utf8_to_locale_ign_err(res_list[i].bookname);
const std::string loc_def = utf8_to_locale_ign_err(res_list[i].def); const std::string loc_def = utf8_to_locale_ign_err(res_list[i].def);
printf("%zu)%s%s%s-->%s%s%s\n", i, printf("%zu)%s%s%s-->%s%s%s\n", i,
colorize_output_ ? NAME_OF_DICT_VISFMT : "", colorize_output_ ? NAME_OF_DICT_VISFMT : "",
utf8_output_ ? res_list[i].bookname.c_str() : loc_bookname.c_str(), utf8_output_ ? res_list[i].bookname.c_str() : loc_bookname.c_str(),
colorize_output_ ? ESC_END : "", colorize_output_ ? ESC_END : "",
colorize_output_ ? SEARCH_TERM_VISFMT : "", colorize_output_ ? SEARCH_TERM_VISFMT : "",
utf8_output_ ? res_list[i].def.c_str() : loc_def.c_str(), utf8_output_ ? res_list[i].def.c_str() : loc_def.c_str(),
colorize_output_ ? ESC_END : ""); colorize_output_ ? ESC_END : "");
} }
int choise; int choise;
for (;;) { std::unique_ptr<IReadLine> choice_readline(create_readline_object());
for (;;) {
std::string str_choise; std::string str_choise;
io.read(_("Your choice[-1 to abort]: "), str_choise); choice_readline->read(_("Your choice[-1 to abort]: "), str_choise);
sscanf(str_choise.c_str(), "%d", &choise); sscanf(str_choise.c_str(), "%d", &choise);
if (choise >= 0 && choise < int(res_list.size())) { if (choise >= 0 && choise < int(res_list.size())) {
sdcv_pager pager; sdcv_pager pager;
io.add_to_history(res_list[choise].def.c_str()); io.add_to_history(res_list[choise].def.c_str());
print_search_result(pager.get_stream(), res_list[choise], first_result); print_search_result(pager.get_stream(), res_list[choise]);
break; break;
} else if (choise == -1) { } else if (choise == -1){
break; break;
} else } else
printf(_("Invalid choice.\nIt must be from 0 to %zu or -1.\n"), printf(_("Invalid choice.\nIt must be from 0 to %zu or -1.\n"),
res_list.size() - 1); res_list.size()-1);
} }
} else { } else {
sdcv_pager pager(force || json_); sdcv_pager pager(force);
if (!json_) { fprintf(pager.get_stream(), _("Found %zu items, similar to %s.\n"),
fprintf(pager.get_stream(), _("Found %zu items, similar to %s.\n"), res_list.size(), utf8_output_ ? get_impl(str) : utf8_to_locale_ign_err(get_impl(str)).c_str());
res_list.size(), utf8_output_ ? get_impl(str) : utf8_to_locale_ign_err(get_impl(str)).c_str()); for (const TSearchResult& search_res : res_list)
} print_search_result(pager.get_stream(), search_res);
for (const TSearchResult &search_res : res_list) { }
print_search_result(pager.get_stream(), search_res, first_result);
}
}
} else { } else {
std::string loc_str; std::string loc_str;
if (!utf8_output_) if (!utf8_output_)
loc_str = utf8_to_locale_ign_err(get_impl(str)); loc_str = utf8_to_locale_ign_err(get_impl(str));
if (!json_)
printf(_("Nothing similar to %s, sorry :(\n"), utf8_output_ ? get_impl(str) : loc_str.c_str());
rval = SEARCH_NO_RESULT;
}
if (json_) { printf(_("Nothing similar to %s, sorry :(\n"), utf8_output_ ? get_impl(str) : loc_str.c_str());
fputs("]\n", stdout); }
}
return rval; return true;
} }

View File

@@ -3,59 +3,41 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "readline.hpp"
#include "stardict_lib.hpp" #include "stardict_lib.hpp"
#include "readline.hpp"
//this structure is wrapper and it need for unification //this structure is wrapper and it need for unification
//results of search whith return Dicts class //results of search whith return Dicts class
struct TSearchResult { struct TSearchResult {
std::string bookname; std::string bookname;
std::string def; std::string def;
std::string exp; std::string exp;
TSearchResult(const std::string &bookname_, const std::string &def_, const std::string &exp_) TSearchResult(const std::string& bookname_, const std::string& def_, const std::string& exp_)
: bookname(bookname_) : bookname(bookname_), def(def_), exp(exp_)
, def(def_) {
, exp(exp_) }
{
}
}; };
typedef std::vector<TSearchResult> TSearchResultList; typedef std::vector<TSearchResult> TSearchResultList;
//possible return values for Library.process_phase()
enum search_result {
SEARCH_SUCCESS = 0,
SEARCH_FAILURE,
SEARCH_NO_RESULT
};
//this class is wrapper around Dicts class for easy use //this class is wrapper around Dicts class for easy use
//of it //of it
class Library : public Libs class Library : public Libs {
{
public: public:
Library(bool uinput, bool uoutput, bool colorize_output, bool use_json, bool no_fuzzy) Library(bool uinput, bool uoutput, bool colorize_output):
: utf8_input_(uinput) utf8_input_(uinput), utf8_output_(uoutput), colorize_output_(colorize_output) {}
, utf8_output_(uoutput)
, colorize_output_(colorize_output)
, json_(use_json)
{
setVerbose(!use_json);
setFuzzy(!no_fuzzy);
}
search_result process_phrase(const char *loc_str, IReadLine &io, bool force = false);
bool process_phrase(const char *loc_str, IReadLine &io, bool force = false);
private: private:
bool utf8_input_; bool utf8_input_;
bool utf8_output_; bool utf8_output_;
bool colorize_output_; bool colorize_output_;
bool json_;
void SimpleLookup(const std::string &str, TSearchResultList &res_list); void SimpleLookup(const std::string &str, TSearchResultList& res_list);
void LookupWithFuzzy(const std::string &str, TSearchResultList &res_list); void LookupWithFuzzy(const std::string &str, TSearchResultList& res_list);
void LookupWithRule(const std::string &str, TSearchResultList &res_lsit); void LookupWithRule(const std::string &str, TSearchResultList& res_lsit);
void LookupData(const std::string &str, TSearchResultList &res_list); void LookupData(const std::string &str, TSearchResultList& res_list);
void print_search_result(FILE *out, const TSearchResult &res, bool &first_result); void print_search_result(FILE *out, const TSearchResult & res);
}; };

67
src/main.rs Normal file
View File

@@ -0,0 +1,67 @@
extern crate getopts;
extern crate gettext;
extern crate ini;
extern crate byteorder;
extern crate libc;
extern crate flate2;
mod core;
mod index;
mod data;
mod mem_mapped_file;
use core::Dictionary;
use std::env;
use std::result::Result;
use getopts::Options;
use gettext::Catalog;
use std::fs::File;
use std::path::Path;
struct Library {
dicts : Vec<Dictionary>
}
impl Library {
fn new(dict_dirs: &[&Path], dict_order_names: &[String], dict_disable_names: &[String]) -> Result<Library, String> {
let dicts = vec![Dictionary::new(Path::new("/home/evgeniy/.stardict/dic/stardict-Mueller7accentGPL-2.4.2/Mueller7accentGPL.ifo")).unwrap()];
Ok(Library{dicts: dicts})
}
pub fn simple_lookup(&mut self, phrase: &str) /*-> Option<String>*/ {
for dict in self.dicts.iter_mut() {
if let Some(translation) = dict.find(phrase) {
println!("{}", translation);
}
}
}
}
fn print_usage(program: &str, opts: Options) {
let brief = format!("Usage: {} [options] [list of words]", program);
print!("{}", opts.usage(&brief));
}
fn main() {
let translation_f = File::open("/usr/share/locale/ru/LC_MESSAGES/sdcv.mo").expect("could not open the catalog");
let catalog = Catalog::parse(translation_f).expect("could not parse the catalog");
let args: Vec<String> = env::args().collect();
let program = args[0].clone();
let mut opts = Options::new();
opts.optflag("v", "version", catalog.gettext("display version information and exit"));
opts.optflag("h", "help", catalog.gettext("Show help options"));
let matches = match opts.parse(&args[1..]) {
Ok(m) => { m }
Err(f) => { panic!(f.to_string()) }
};
if matches.opt_present("h") {
print_usage(&program, opts);
return;
}
let dict_dirs = vec![Path::new("/tmp")];
let dict_order_names: Vec<String> = vec![];
let dict_disable_names: Vec<String> = vec![];
let mut library = Library::new(&dict_dirs, &dict_order_names, &dict_disable_names).unwrap();
library.simple_lookup("man");
}

View File

@@ -1,34 +1,31 @@
#pragma once #pragma once
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "config.h" # include "config.h"
#endif #endif
#ifdef HAVE_MMAP #ifdef HAVE_MMAP
#include <fcntl.h> # include <sys/types.h>
#include <sys/mman.h> # include <fcntl.h>
#include <sys/stat.h> # include <sys/mman.h>
#include <sys/types.h>
#endif #endif
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> # include <windows.h>
#endif #endif
#include <glib.h> #include <glib.h>
class MapFile class MapFile {
{
public: public:
MapFile() {} MapFile() {}
~MapFile(); ~MapFile();
MapFile(const MapFile &) = delete; MapFile(const MapFile&) = delete;
MapFile &operator=(const MapFile &) = delete; MapFile& operator=(const MapFile&) = delete;
bool open(const char *file_name, off_t file_size); bool open(const char *file_name, unsigned long file_size);
gchar *begin() { return data; } gchar *begin() { return data; }
private: private:
char *data = nullptr; char *data = nullptr;
unsigned long size = 0ul;
#ifdef HAVE_MMAP #ifdef HAVE_MMAP
size_t size = 0u;
int mmap_fd = -1; int mmap_fd = -1;
#elif defined(_WIN32) #elif defined(_WIN32)
HANDLE hFile = 0; HANDLE hFile = 0;
@@ -36,58 +33,53 @@ private:
#endif #endif
}; };
inline bool MapFile::open(const char *file_name, off_t file_size) inline bool MapFile::open(const char *file_name, unsigned long file_size)
{ {
size=file_size;
#ifdef HAVE_MMAP #ifdef HAVE_MMAP
if ((mmap_fd = ::open(file_name, O_RDONLY)) < 0) { if ((mmap_fd = ::open(file_name, O_RDONLY)) < 0) {
// g_print("Open file %s failed!\n",fullfilename); //g_print("Open file %s failed!\n",fullfilename);
return false; return false;
} }
struct stat st; data = (gchar *)mmap( nullptr, file_size, PROT_READ, MAP_SHARED, mmap_fd, 0);
if (fstat(mmap_fd, &st) == -1 || st.st_size < 0 || (st.st_size == 0 && S_ISREG(st.st_mode)) if ((void *)data == (void *)(-1)) {
|| st.st_size != file_size) { //g_print("mmap file %s failed!\n",idxfilename);
close(mmap_fd); data=nullptr;
return false; return false;
} }
#elif defined( _WIN32)
size = static_cast<size_t>(st.st_size); hFile = CreateFile(file_name, GENERIC_READ, 0, nullptr, OPEN_ALWAYS,
data = (gchar *)mmap(nullptr, size, PROT_READ, MAP_SHARED, mmap_fd, 0); FILE_ATTRIBUTE_NORMAL, 0);
if ((void *)data == (void *)(-1)) { hFileMap = CreateFileMapping(hFile, nullptr, PAGE_READONLY, 0,
// g_print("mmap file %s failed!\n",idxfilename); file_size, nullptr);
size = 0u; data = (gchar *)MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, file_size);
data = nullptr;
return false;
}
#elif defined(_WIN32)
hFile = CreateFile(file_name, GENERIC_READ, 0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
hFileMap = CreateFileMapping(hFile, nullptr, PAGE_READONLY, 0, file_size, nullptr);
data = (gchar *)MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, file_size);
#else #else
gsize read_len; gsize read_len;
if (!g_file_get_contents(file_name, &data, &read_len, nullptr)) if (!g_file_get_contents(file_name, &data, &read_len, nullptr))
return false; return false;
if (read_len != file_size) if (read_len != file_size)
return false; return false;
#endif #endif
return true; return true;
} }
inline MapFile::~MapFile() inline MapFile::~MapFile()
{ {
if (!data) if (!data)
return; return;
#ifdef HAVE_MMAP #ifdef HAVE_MMAP
munmap(data, size); munmap(data, size);
close(mmap_fd); close(mmap_fd);
#else #else
#ifdef _WIN32 # ifdef _WIN32
UnmapViewOfFile(data); UnmapViewOfFile(data);
CloseHandle(hFileMap); CloseHandle(hFileMap);
CloseHandle(hFile); CloseHandle(hFile);
#else # else
g_free(data); g_free(data);
#endif # endif
#endif #endif
} }

179
src/mem_mapped_file.rs Normal file
View File

@@ -0,0 +1,179 @@
use std::path::Path;
use std::fs::File;
use std::os::unix::io::AsRawFd;
use std::ptr;
use std::slice;
use libc::{c_void, c_int};
use libc;
pub struct MemMappedFile {
mem_addr: *const u8,
length: usize,
}
#[cfg(unix)]
impl MemMappedFile {
pub fn new(file_name: &Path) -> Result<MemMappedFile, String> {
let file = try!(File::open(file_name).map_err(|err| err.to_string()));
let metadata = try!(file.metadata().map_err(|err| err.to_string()));
if !metadata.is_file() {
return Err(format!("{} is not file", file_name.to_str().unwrap()));
}
let file_len = metadata.len();
let fd = file.as_raw_fd();
let addr: *const u8 = ptr::null();
let res = unsafe {
libc::mmap(addr as *mut c_void, file_len as libc::size_t, libc::PROT_READ, libc::MAP_SHARED,
fd, 0)
};
if res == libc::MAP_FAILED {
Err(format!("mmap for {} failed", file_name.to_str().unwrap()))
} else {
Ok(MemMappedFile{mem_addr: res as *mut u8, length: file_len as usize})
}
}
pub fn len(&self) -> usize { self.length }
pub fn get_chunk<'a>(&self, offset: usize, size: usize) -> Result<&'a[u8], String> {
let end = offset + size;
if end > self.length {
return Err(format!("offset out of range from 0 to {}", self.length));
}
Ok(unsafe {slice::from_raw_parts(self.mem_addr.offset(offset as isize), size) })
}
}
#[cfg(unix)]
impl Drop for MemMappedFile {
fn drop(&mut self) {
let res: c_int = unsafe {
// `munmap` only panics due to logic errors
libc::munmap(self.mem_addr as *mut c_void, self.length as libc::size_t)
};
if res == -1 {
panic!("munmap return error");
}
}
}
#[cfg(test)]
mod test {
use super::*;
use std::path::Path;
use std::mem;
use std::slice;
static CRCTAB: [u32; 256] = [
0x00000000,
0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
];
fn crc32(buf: &[u32]) -> u32 {
let mut crc = 0_u32;
for item in buf {
let mut byte: u32 = item & 0xFF_u32;
crc = (crc << 8) ^ CRCTAB[(((crc >> 24) ^ byte) & 0xFF) as usize];
byte = (item >> 8) & 0xFF_u32;
crc = (crc << 8) ^ CRCTAB[(((crc >> 24) ^ byte) & 0xFF) as usize];
byte = (item >> 16) & 0xFF_u32;
crc = (crc << 8) ^ CRCTAB[(((crc >> 24) ^ byte) & 0xFF) as usize];
byte = (item >> 24) & 0xFF_u32;
crc = (crc << 8) ^ CRCTAB[(((crc >> 24) ^ byte) & 0xFF) as usize];
}
!crc & 0xFFFFFFFF_u32
}
#[test]
fn mem_mapped_file_basic_test() {
let mfile = MemMappedFile::new(Path::new("tests/words_dic/stardict-words-2.4.2/words.idx")).unwrap();
let mem_chunk = mfile.get_chunk(0, 16).unwrap();
assert_eq!(mem_chunk.len(), 16);
let exp : Vec<u8> = vec![0x21, 0, 0, 0, 0, 0, 0, 0, 0, 0xa, 0x21, 0x20, 0x23, 0x24, 0x25, 0x5e];
assert_eq!(mem_chunk, exp.as_slice());
const CHUNK_SIZE: usize = 256;
let n_chunks = (mfile.len() + CHUNK_SIZE -1) / CHUNK_SIZE;
let mut data: Vec<u32> = Vec::with_capacity(mfile.len() / 4);
for i in 0..(n_chunks - 1) {
let mem_chunk = mfile.get_chunk(i * CHUNK_SIZE, CHUNK_SIZE).unwrap();
let data_bytes = &mem_chunk[0] as *const u8;
let data_words: *const u32 = unsafe { mem::transmute(data_bytes) };
let chunk_u32_view = unsafe { slice::from_raw_parts(data_words, CHUNK_SIZE / 4) };
data.extend_from_slice(chunk_u32_view);
}
let res_crc = crc32(data.as_slice());
assert_eq!(res_crc, 0x2a04c834_u32);
let rest = mfile.len() - (n_chunks - 1) * CHUNK_SIZE;
if rest > 0 {
let mem_chunk = mfile.get_chunk((n_chunks - 1) * CHUNK_SIZE, rest).unwrap();
let add_size = (rest + 3) / 4;
for i in 0..add_size {
let mut word = 0_u32;
for j in 0..4 {
if i * 4 + j < mem_chunk.len() {
word |= (mem_chunk[i * 4 + j] as u32) << (j * 8);
}
}
data.push(word);
}
}
let res_crc = crc32(data.as_slice());
assert_eq!(res_crc, 0x6ae491df);
}
}

View File

@@ -1,4 +1,4 @@
/* /*
* This file part of sdcv - console version of Stardict program * This file part of sdcv - console version of Stardict program
* http://sdcv.sourceforge.net * http://sdcv.sourceforge.net
* Copyright (C) 2005 Evgeniy <dushistov@mail.ru> * Copyright (C) 2005 Evgeniy <dushistov@mail.ru>
@@ -15,18 +15,18 @@
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "config.h" # include "config.h"
#endif #endif
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#ifdef WITH_READLINE #ifdef WITH_READLINE
#include <readline/history.h> # include <readline/readline.h>
#include <readline/readline.h> # include <readline/history.h>
#endif #endif
#include <glib.h> #include <glib.h>
@@ -34,96 +34,72 @@
#include "readline.hpp" #include "readline.hpp"
bool stdio_getline(FILE *in, std::string &str)
{
assert(in != nullptr);
str.clear();
int ch;
while ((ch = fgetc(in)) != EOF && ch != '\n')
str += ch;
return EOF != ch;
}
#ifndef WITH_READLINE #ifndef WITH_READLINE
namespace namespace {
{ static bool stdio_getline(FILE *in, std::string & str)
class dummy_readline : public IReadLine
{
public:
bool read(const std::string &banner, std::string &line) override
{ {
printf("%s", banner.c_str()); str.clear();
return stdio_getline(stdin, line); int ch;
while ((ch=fgetc(in)) != EOF && ch != '\n')
str += ch;
return EOF != ch;
} }
};
} // namespace class dummy_readline : public IReadLine {
public:
bool read(const string& banner, string& line) override {
printf("%s", banner.c_str());
return stdio_getline(stdin, line);
}
};
}
#else #else
namespace namespace {
{ class real_readline : public IReadLine {
std::string get_hist_file_path()
{
const gchar *hist_file_str = g_getenv("SDCV_HISTFILE");
if (hist_file_str != nullptr) public:
return std::string(hist_file_str); real_readline() {
rl_readline_name = "sdcv";
const std::string hist_file_path = std::string(g_get_home_dir()) + G_DIR_SEPARATOR + ".sdcv_history"; using_history();
if (g_file_test(hist_file_path.c_str(), G_FILE_TEST_IS_REGULAR)) const std::string histname = std::string(g_get_home_dir()) + G_DIR_SEPARATOR + ".sdcv_history";
return hist_file_path; read_history(histname.c_str());
return std::string(g_get_user_data_dir()) + G_DIR_SEPARATOR + "sdcv_history";
}
class real_readline : public IReadLine
{
public:
real_readline()
{
rl_readline_name = "sdcv";
using_history();
const std::string histname = get_hist_file_path();
read_history(histname.c_str());
}
~real_readline()
{
const std::string histname = get_hist_file_path();
write_history(histname.c_str());
const gchar *hist_size_str = g_getenv("SDCV_HISTSIZE");
int hist_size;
if (!hist_size_str || sscanf(hist_size_str, "%d", &hist_size) < 1)
hist_size = 2000;
history_truncate_file(histname.c_str(), hist_size);
}
bool read(const std::string &banner, std::string &line) override
{
char *phrase = nullptr;
phrase = readline(banner.c_str());
if (phrase) {
line = phrase;
free(phrase);
return true;
} }
return false;
}
void add_to_history(const std::string &phrase) override ~real_readline() {
{ const std::string histname = std::string(g_get_home_dir()) + G_DIR_SEPARATOR + ".sdcv_history";
add_history(phrase.c_str()); write_history(histname.c_str());
} const gchar *hist_size_str=g_getenv("SDCV_HISTSIZE");
}; int hist_size;
} // namespace if (!hist_size_str || sscanf(hist_size_str, "%d", &hist_size)<1)
#endif //WITH_READLINE hist_size = 2000;
history_truncate_file(histname.c_str(), hist_size);
}
bool read(const std::string &banner, std::string& line) override {
char *phrase = nullptr;
phrase = readline(banner.c_str());
if (phrase) {
line = phrase;
free(phrase);
return true;
}
return false;
}
void add_to_history(const std::string& phrase) override {
add_history(phrase.c_str());
}
};
}
#endif//WITH_READLINE
IReadLine *create_readline_object() IReadLine *create_readline_object()
{ {
#ifdef WITH_READLINE #ifdef WITH_READLINE
return new real_readline; return new real_readline;
#else #else
return new dummy_readline; return new dummy_readline;
#endif #endif
} }

View File

@@ -2,14 +2,13 @@
#include <string> #include <string>
class IReadLine class IReadLine {
{
public: public:
virtual ~IReadLine() {} virtual ~IReadLine() {}
virtual bool read(const std::string &banner, std::string &line) = 0; virtual bool read(const std::string &banner, std::string& line) = 0;
virtual void add_to_history(const std::string &) {} virtual void add_to_history(const std::string&) {}
}; };
extern std::string sdcv_readline; extern std::string sdcv_readline;
extern IReadLine *create_readline_object(); extern IReadLine *create_readline_object();
extern bool stdio_getline(FILE *in, std::string &str);

View File

@@ -1,4 +1,4 @@
/* /*
* This file part of sdcv - console version of Stardict program * This file part of sdcv - console version of Stardict program
* http://sdcv.sourceforge.net * http://sdcv.sourceforge.net
* Copyright (C) 2003-2006 Evgeniy <dushistov@mail.ru> * Copyright (C) 2003-2006 Evgeniy <dushistov@mail.ru>
@@ -15,20 +15,18 @@
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "config.h" #include "config.h"
#endif #endif
#include <algorithm>
#include <cerrno> #include <cerrno>
#include <clocale> #include <clocale>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <map>
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
@@ -53,22 +51,19 @@ static void free_str_array(gchar **arr)
g_free(*p); g_free(*p);
g_free(arr); g_free(arr);
} }
} // namespace }
namespace glib namespace glib
{ {
using StrArr = ResourceWrapper<gchar *, gchar *, free_str_array>; typedef ResourceWrapper<gchar *, gchar *, free_str_array> StrArr;
} }
static void list_dicts(const std::list<std::string> &dicts_dir_list, bool use_json); int main(int argc, char *argv[]) try {
int main(int argc, char *argv[])
try {
setlocale(LC_ALL, ""); setlocale(LC_ALL, "");
#if ENABLE_NLS #if ENABLE_NLS
bindtextdomain("sdcv", bindtextdomain("sdcv",
//"./locale"//< for testing //"./locale"//< for testing
GETTEXT_TRANSLATIONS_PATH //< should be GETTEXT_TRANSLATIONS_PATH //< should be
); );
textdomain("sdcv"); textdomain("sdcv");
#endif #endif
@@ -76,14 +71,10 @@ try {
gboolean show_list_dicts = FALSE; gboolean show_list_dicts = FALSE;
glib::StrArr use_dict_list; glib::StrArr use_dict_list;
gboolean non_interactive = FALSE; gboolean non_interactive = FALSE;
gboolean json_output = FALSE;
gboolean no_fuzzy = FALSE;
gboolean utf8_output = FALSE; gboolean utf8_output = FALSE;
gboolean utf8_input = FALSE; gboolean utf8_input = FALSE;
glib::CharStr opt_data_dir; glib::CharStr opt_data_dir;
gboolean only_data_dir = FALSE;
gboolean colorize = FALSE; gboolean colorize = FALSE;
glib::StrArr word_list;
const GOptionEntry entries[] = { const GOptionEntry entries[] = {
{ "version", 'v', 0, G_OPTION_ARG_NONE, &show_version, { "version", 'v', 0, G_OPTION_ARG_NONE, &show_version,
@@ -95,12 +86,6 @@ try {
_("bookname") }, _("bookname") },
{ "non-interactive", 'n', 0, G_OPTION_ARG_NONE, &non_interactive, { "non-interactive", 'n', 0, G_OPTION_ARG_NONE, &non_interactive,
_("for use in scripts"), nullptr }, _("for use in scripts"), nullptr },
{ "json-output", 'j', 0, G_OPTION_ARG_NONE, &json_output,
_("print the result formatted as JSON"), nullptr },
{ "json", 'j', 0, G_OPTION_ARG_NONE, &json_output,
_("print the result formatted as JSON"), nullptr },
{ "exact-search", 'e', 0, G_OPTION_ARG_NONE, &no_fuzzy,
_("do not fuzzy-search for similar words, only return exact matches"), nullptr },
{ "utf8-output", '0', 0, G_OPTION_ARG_NONE, &utf8_output, { "utf8-output", '0', 0, G_OPTION_ARG_NONE, &utf8_output,
_("output must be in utf8"), nullptr }, _("output must be in utf8"), nullptr },
{ "utf8-input", '1', 0, G_OPTION_ARG_NONE, &utf8_input, { "utf8-input", '1', 0, G_OPTION_ARG_NONE, &utf8_input,
@@ -108,17 +93,13 @@ try {
{ "data-dir", '2', 0, G_OPTION_ARG_STRING, get_addr(opt_data_dir), { "data-dir", '2', 0, G_OPTION_ARG_STRING, get_addr(opt_data_dir),
_("use this directory as path to stardict data directory"), _("use this directory as path to stardict data directory"),
_("path/to/dir") }, _("path/to/dir") },
{ "only-data-dir", 'x', 0, G_OPTION_ARG_NONE, &only_data_dir,
_("only use the dictionaries in data-dir, do not search in user and system directories"), nullptr },
{ "color", 'c', 0, G_OPTION_ARG_NONE, &colorize, { "color", 'c', 0, G_OPTION_ARG_NONE, &colorize,
_("colorize the output"), nullptr }, _("colorize the output"), nullptr },
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, get_addr(word_list), { nullptr },
_("search terms"), _(" words") },
{},
}; };
glib::Error error; glib::Error error;
GOptionContext *context = g_option_context_new(nullptr); GOptionContext *context = g_option_context_new(_(" words"));
g_option_context_set_help_enabled(context, TRUE); g_option_context_set_help_enabled(context, TRUE);
g_option_context_add_main_entries(context, entries, nullptr); g_option_context_add_main_entries(context, entries, nullptr);
const gboolean parse_res = g_option_context_parse(context, &argc, &argv, get_addr(error)); const gboolean parse_res = g_option_context_parse(context, &argc, &argv, get_addr(error));
@@ -137,108 +118,75 @@ try {
const gchar *stardict_data_dir = g_getenv("STARDICT_DATA_DIR"); const gchar *stardict_data_dir = g_getenv("STARDICT_DATA_DIR");
std::string data_dir; std::string data_dir;
if (!opt_data_dir) { if (!opt_data_dir) {
if (!only_data_dir) { if (stardict_data_dir)
if (stardict_data_dir) data_dir = stardict_data_dir;
data_dir = stardict_data_dir; else
else data_dir = "/usr/share/stardict/dic";
data_dir = "/usr/share/stardict/dic";
}
} else { } else {
data_dir = get_impl(opt_data_dir); data_dir = get_impl(opt_data_dir);
} }
std::string conf_dir = std::string(g_get_home_dir()) + G_DIR_SEPARATOR + ".stardict"; const char *homedir = g_getenv("HOME");
if (!g_file_test(conf_dir.c_str(), G_FILE_TEST_IS_DIR)) if (!homedir)
conf_dir = std::string(g_get_user_data_dir()) + G_DIR_SEPARATOR + "stardict"; homedir = g_get_home_dir();
const std::list<std::string> dicts_dir_list = {
std::string(homedir) + G_DIR_SEPARATOR + ".stardict" + G_DIR_SEPARATOR + "dic",
data_dir
};
std::list<std::string> dicts_dir_list;
if (!only_data_dir)
dicts_dir_list.push_back(conf_dir + G_DIR_SEPARATOR + "dic");
dicts_dir_list.push_back(data_dir);
if (show_list_dicts) { if (show_list_dicts) {
list_dicts(dicts_dir_list, json_output); printf(_("Dictionary's name Word count\n"));
std::list<std::string> order_list, disable_list;
for_each_file(dicts_dir_list, ".ifo", order_list,
disable_list, [](const std::string &filename, bool) -> void {
DictInfo dict_info;
if (dict_info.load_from_ifo_file(filename, false)) {
const std::string bookname = utf8_to_locale_ign_err(dict_info.bookname);
printf("%s %d\n", bookname.c_str(), dict_info.wordcount);
}
});
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
std::list<std::string> disable_list; std::list<std::string> disable_list;
std::map<std::string, std::string> bookname_to_ifo; if (use_dict_list) {
for_each_file(dicts_dir_list, ".ifo", std::list<std::string>(), std::list<std::string>(), std::list<std::string> empty_list;
[&bookname_to_ifo](const std::string &fname, bool) {
DictInfo dict_info;
const bool load_ok = dict_info.load_from_ifo_file(fname, false);
if (!load_ok)
return;
bookname_to_ifo[dict_info.bookname] = dict_info.ifo_file_name;
});
std::list<std::string> order_list; for_each_file(dicts_dir_list, ".ifo", empty_list, empty_list,
if (use_dict_list != nullptr) { [&disable_list, &use_dict_list](const std::string &filename, bool) -> void {
for (auto &&x : bookname_to_ifo) { DictInfo dict_info;
gchar **p = get_impl(use_dict_list); const bool load_ok = dict_info.load_from_ifo_file(filename, false);
for (; *p != nullptr; ++p) if (!load_ok)
if (x.first.compare(*p) == 0) { return;
break;
}
if (*p == nullptr) {
disable_list.push_back(x.second);
}
}
// add bookname to list for (gchar **p = get_impl(use_dict_list); *p != nullptr; ++p)
for (gchar **p = get_impl(use_dict_list); *p != nullptr; ++p) { if (strcmp(*p, dict_info.bookname.c_str()) == 0)
auto it = bookname_to_ifo.find(*p); return;
if (it != bookname_to_ifo.end()) { disable_list.push_back(dict_info.ifo_file_name);
order_list.push_back(it->second); });
} else {
fprintf(stderr, _("Unknown dictionary: %s\n"), *p);
}
}
} else {
std::string ordering_cfg_file = std::string(g_get_user_config_dir()) + G_DIR_SEPARATOR_S "sdcv_ordering";
FILE *ordering_file = fopen(ordering_cfg_file.c_str(), "r");
if (ordering_file == nullptr) {
ordering_cfg_file = std::string(g_get_home_dir()) + G_DIR_SEPARATOR_S ".sdcv_ordering";
ordering_file = fopen(ordering_cfg_file.c_str(), "r");
}
if (ordering_file != nullptr) {
std::string line;
while (stdio_getline(ordering_file, line)) {
auto it = bookname_to_ifo.find(line);
if (it != bookname_to_ifo.end()) {
order_list.push_back(it->second);
} else {
fprintf(stderr, _("Unknown dictionary: %s\n"), line.c_str());
}
}
fclose(ordering_file);
}
} }
if (g_mkdir(conf_dir.c_str(), S_IRWXU) == -1 && errno != EEXIST) { const std::string conf_dir = std::string(g_get_home_dir()) + G_DIR_SEPARATOR + ".stardict";
if (g_mkdir(conf_dir.c_str(), S_IRWXU) == -1 && errno != EEXIST)
fprintf(stderr, _("g_mkdir failed: %s\n"), strerror(errno)); fprintf(stderr, _("g_mkdir failed: %s\n"), strerror(errno));
}
Library lib(utf8_input, utf8_output, colorize, json_output, no_fuzzy); Library lib(utf8_input, utf8_output, colorize);
lib.load(dicts_dir_list, order_list, disable_list); std::list<std::string> empty_list;
lib.load(dicts_dir_list, empty_list, disable_list);
std::unique_ptr<IReadLine> io(create_readline_object()); std::unique_ptr<IReadLine> io(create_readline_object());
if (word_list != nullptr) { if (optind < argc) {
search_result rval = SEARCH_SUCCESS; for (int i = optind; i < argc; ++i)
gchar **p = get_impl(word_list); if (!lib.process_phrase(argv[i], *io, non_interactive))
while (*p) { return EXIT_FAILURE;
search_result this_rval = lib.process_phrase(*p++, *io, non_interactive);
// If we encounter any error, save it but continue through the word
// list to check all requested words.
if (rval == SEARCH_SUCCESS)
rval = this_rval;
}
if (rval != SEARCH_SUCCESS)
return rval;
} else if (!non_interactive) { } else if (!non_interactive) {
std::string phrase; std::string phrase;
while (io->read(_("Enter word or phrase: "), phrase)) { while (io->read(_("Enter word or phrase: "), phrase)) {
if (lib.process_phrase(phrase.c_str(), *io) == SEARCH_FAILURE) if (!lib.process_phrase(phrase.c_str(), *io))
return EXIT_FAILURE; return EXIT_FAILURE;
phrase.clear(); phrase.clear();
} }
@@ -252,32 +200,3 @@ try {
fprintf(stderr, "Internal error: %s\n", ex.what()); fprintf(stderr, "Internal error: %s\n", ex.what());
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
static void list_dicts(const std::list<std::string> &dicts_dir_list, bool use_json)
{
bool first_entry = true;
if (!use_json)
printf(_("Dictionary's name Word count\n"));
else
fputc('[', stdout);
std::list<std::string> order_list, disable_list;
for_each_file(dicts_dir_list, ".ifo", order_list,
disable_list, [use_json, &first_entry](const std::string &filename, bool) -> void {
DictInfo dict_info;
if (dict_info.load_from_ifo_file(filename, false)) {
const std::string bookname = utf8_to_locale_ign_err(dict_info.bookname);
if (use_json) {
if (first_entry) {
first_entry = false;
} else {
fputc(',', stdout); // comma between entries
}
printf("{\"name\": \"%s\", \"wordcount\": \"%d\"}", json_escape_string(bookname).c_str(), dict_info.wordcount);
} else {
printf("%s %d\n", bookname.c_str(), dict_info.wordcount);
}
}
});
if (use_json)
fputs("]\n", stdout);
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,17 +1,17 @@
#pragma once #pragma once
#include <cstdio>
#include <cstring> #include <cstring>
#include <functional>
#include <list> #include <list>
#include <memory> #include <memory>
#include <set>
#include <string> #include <string>
#include <vector> #include <vector>
#include <functional>
#include "dictziplib.hpp" #include "dictziplib.hpp"
const int MAX_MATCH_ITEM_PER_LIB = 100; const int MAX_MATCH_ITEM_PER_LIB=100;
const int MAX_FUZZY_DISTANCE = 3; // at most MAX_FUZZY_DISTANCE-1 differences allowed when find similar words const int MAX_FUZZY_DISTANCE= 3; // at most MAX_FUZZY_DISTANCE-1 differences allowed when find similar words
inline guint32 get_uint32(const gchar *addr) inline guint32 get_uint32(const gchar *addr)
{ {
@@ -25,202 +25,159 @@ inline void set_uint32(gchar *addr, guint32 val)
memcpy(addr, &val, sizeof(guint32)); memcpy(addr, &val, sizeof(guint32));
} }
struct cacheItem { struct cacheItem {
guint32 offset; guint32 offset;
gchar *data; gchar *data;
// write code here to make it inline //write code here to make it inline
cacheItem() { data = nullptr; } cacheItem() { data = nullptr;}
~cacheItem() { g_free(data); } ~cacheItem() { g_free(data); }
}; };
const int WORDDATA_CACHE_NUM = 10; const int WORDDATA_CACHE_NUM = 10;
const int INVALID_INDEX = -100; const int INVALID_INDEX=-100;
class DictBase class DictBase {
{
public: public:
DictBase() {} DictBase() {}
~DictBase() ~DictBase() {
{
if (dictfile) if (dictfile)
fclose(dictfile); fclose(dictfile);
} }
DictBase(const DictBase &) = delete; DictBase(const DictBase&) = delete;
DictBase &operator=(const DictBase &) = delete; DictBase& operator=(const DictBase&) = delete;
gchar *GetWordData(guint32 idxitem_offset, guint32 idxitem_size); gchar * GetWordData(guint32 idxitem_offset, guint32 idxitem_size);
bool containSearchData() const bool containSearchData() const {
{
if (sametypesequence.empty()) if (sametypesequence.empty())
return true; return true;
return sametypesequence.find_first_of("mlgxty") != std::string::npos; return sametypesequence.find_first_of("mlgxty") != std::string::npos;
} }
bool SearchData(std::vector<std::string> &SearchWords, guint32 idxitem_offset, guint32 idxitem_size, gchar *origin_data); bool SearchData(std::vector<std::string> &SearchWords, guint32 idxitem_offset, guint32 idxitem_size, gchar *origin_data);
protected: protected:
std::string sametypesequence; std::string sametypesequence;
FILE *dictfile = nullptr; FILE *dictfile = nullptr;
std::unique_ptr<DictData> dictdzfile; std::unique_ptr<DictData> dictdzfile;
private: private:
cacheItem cache[WORDDATA_CACHE_NUM]; cacheItem cache[WORDDATA_CACHE_NUM];
gint cache_cur = 0; gint cache_cur = 0;
}; };
// this structure contain all information about dictionary //this structure contain all information about dictionary
struct DictInfo { struct DictInfo {
std::string ifo_file_name; std::string ifo_file_name;
guint32 wordcount; guint32 wordcount;
guint32 syn_wordcount; std::string bookname;
std::string bookname; std::string author;
std::string author; std::string email;
std::string email; std::string website;
std::string website; std::string date;
std::string date; std::string description;
std::string description; guint32 index_file_size;
off_t index_file_size; std::string sametypesequence;
off_t syn_file_size;
std::string sametypesequence;
bool load_from_ifo_file(const std::string &ifofilename, bool istreedict); bool load_from_ifo_file(const std::string& ifofilename, bool istreedict);
}; };
class IIndexFile class IIndexFile {
{
public: public:
guint32 wordentry_offset; guint32 wordentry_offset;
guint32 wordentry_size; guint32 wordentry_size;
virtual ~IIndexFile() {} virtual ~IIndexFile() {}
virtual bool load(const std::string &url, gulong wc, off_t fsize, bool verbose) = 0; virtual bool load(const std::string& url, gulong wc, gulong fsize) = 0;
virtual const gchar *get_key(glong idx) = 0; virtual const gchar *get_key(glong idx) = 0;
virtual void get_data(glong idx) = 0; virtual void get_data(glong idx) = 0;
virtual const gchar *get_key_and_data(glong idx) = 0; virtual const gchar *get_key_and_data(glong idx) = 0;
virtual bool lookup(const char *str, std::set<glong> &idxs, glong &next_idx) = 0; virtual bool lookup(const char *str, glong &idx) = 0;
virtual bool lookup(const char *str, std::set<glong> &idxs)
{
glong unused_next_idx;
return lookup(str, idxs, unused_next_idx);
};
}; };
class SynFile class Dict : public DictBase {
{
public: public:
SynFile() {} Dict() {}
~SynFile() {} Dict(const Dict&) = delete;
bool load(const std::string &url, gulong wc); Dict& operator=(const Dict&) = delete;
bool lookup(const char *str, std::set<glong> &idxs, glong &next_idx); bool load(const std::string& ifofilename);
bool lookup(const char *str, std::set<glong> &idxs);
const gchar *get_key(glong idx) { return synlist[idx]; }
private: gulong narticles() const { return wordcount; }
MapFile synfile; const std::string& dict_name() const { return bookname; }
std::vector<gchar *> synlist; const std::string& ifofilename() const { return ifo_file_name; }
};
class Dict : public DictBase const gchar *get_key(glong index) { return idx_file->get_key(index); }
{ gchar *get_data(glong index) {
public:
Dict() {}
Dict(const Dict &) = delete;
Dict &operator=(const Dict &) = delete;
bool load(const std::string &ifofilename, bool verbose);
gulong narticles() const { return wordcount; }
const std::string &dict_name() const { return bookname; }
const std::string &ifofilename() const { return ifo_file_name; }
const gchar *get_key(glong index) { return idx_file->get_key(index); }
gchar *get_data(glong index)
{
idx_file->get_data(index); idx_file->get_data(index);
return DictBase::GetWordData(idx_file->wordentry_offset, idx_file->wordentry_size); return DictBase::GetWordData(idx_file->wordentry_offset, idx_file->wordentry_size);
} }
void get_key_and_data(glong index, const gchar **key, guint32 *offset, guint32 *size) void get_key_and_data(glong index, const gchar **key, guint32 *offset, guint32 *size) {
{
*key = idx_file->get_key_and_data(index); *key = idx_file->get_key_and_data(index);
*offset = idx_file->wordentry_offset; *offset = idx_file->wordentry_offset;
*size = idx_file->wordentry_size; *size = idx_file->wordentry_size;
} }
bool Lookup(const char *str, std::set<glong> &idxs, glong &next_idx); bool Lookup(const char *str, glong &idx) { return idx_file->lookup(str, idx); }
bool Lookup(const char *str, std::set<glong> &idxs)
{
glong unused_next_idx;
return Lookup(str, idxs, unused_next_idx);
}
bool LookupWithRule(GPatternSpec *pspec, glong *aIndex, int iBuffLen); bool LookupWithRule(GPatternSpec *pspec, glong *aIndex, int iBuffLen);
private:
std::string ifo_file_name;
gulong wordcount;
std::string bookname;
private: std::unique_ptr<IIndexFile> idx_file;
std::string ifo_file_name;
gulong wordcount; bool load_ifofile(const std::string& ifofilename, gulong &idxfilesize);
gulong syn_wordcount;
std::string bookname;
std::unique_ptr<IIndexFile> idx_file;
std::unique_ptr<SynFile> syn_file;
bool load_ifofile(const std::string &ifofilename, off_t &idxfilesize);
}; };
class Libs class Libs {
{
public: public:
Libs(std::function<void(void)> f = std::function<void(void)>()) Libs(std::function<void(void)> f = std::function<void(void)>()) {
{
progress_func = f; progress_func = f;
iMaxFuzzyDistance = MAX_FUZZY_DISTANCE; // need to read from cfg. iMaxFuzzyDistance = MAX_FUZZY_DISTANCE; //need to read from cfg.
} }
void setVerbose(bool verbose) { verbose_ = verbose; } ~Libs();
void setFuzzy(bool fuzzy) { fuzzy_ = fuzzy; } Libs(const Libs&) = delete;
~Libs(); Libs& operator=(const Libs&) = delete;
Libs(const Libs &) = delete;
Libs &operator=(const Libs &) = delete;
void load_dict(const std::string &url); void load_dict(const std::string& url);
void load(const std::list<std::string> &dicts_dirs, void load(const std::list<std::string>& dicts_dirs,
const std::list<std::string> &order_list, const std::list<std::string>& order_list,
const std::list<std::string> &disable_list); const std::list<std::string>& disable_list);
glong narticles(int idict) const { return oLib[idict]->narticles(); } void reload(const std::list<std::string>& dicts_dirs,
const std::string &dict_name(int idict) const { return oLib[idict]->dict_name(); } const std::list<std::string>& order_list,
gint ndicts() const { return oLib.size(); } const std::list<std::string>& disable_list);
const gchar *poGetWord(glong iIndex, int iLib) glong narticles(int idict) const { return oLib[idict]->narticles(); }
{ const std::string& dict_name(int idict) const { return oLib[idict]->dict_name(); }
return oLib[iLib]->get_key(iIndex); gint ndicts() const { return oLib.size(); }
}
gchar *poGetWordData(glong iIndex, int iLib)
{
if (iIndex == INVALID_INDEX)
return nullptr;
return oLib[iLib]->get_data(iIndex);
}
bool LookupWord(const gchar *sWord, std::set<glong> &iWordIndices, int iLib)
{
return oLib[iLib]->Lookup(sWord, iWordIndices);
}
bool LookupSimilarWord(const gchar *sWord, std::set<glong> &iWordIndices, int iLib);
bool SimpleLookupWord(const gchar *sWord, std::set<glong> &iWordIndices, int iLib);
bool LookupWithFuzzy(const gchar *sWord, gchar *reslist[], gint reslist_size); const gchar *poGetWord(glong iIndex, int iLib) {
gint LookupWithRule(const gchar *sWord, gchar *reslist[]); return oLib[iLib]->get_key(iIndex);
bool LookupData(const gchar *sWord, std::vector<gchar *> *reslist); }
gchar * poGetWordData(glong iIndex,int iLib) {
protected: if (iIndex == INVALID_INDEX)
bool fuzzy_; return nullptr;
return oLib[iLib]->get_data(iIndex);
}
const gchar *poGetCurrentWord(glong *iCurrent);
const gchar *poGetNextWord(const gchar *word, glong *iCurrent);
const gchar *poGetPreWord(glong *iCurrent);
bool LookupWord(const gchar* sWord, glong& iWordIndex, int iLib) {
return oLib[iLib]->Lookup(sWord, iWordIndex);
}
bool LookupSimilarWord(const gchar* sWord, glong & iWordIndex, int iLib);
bool SimpleLookupWord(const gchar* sWord, glong & iWordIndex, int iLib);
bool LookupWithFuzzy(const gchar *sWord, gchar *reslist[], gint reslist_size);
gint LookupWithRule(const gchar *sWord, gchar *reslist[]);
bool LookupData(const gchar *sWord, std::vector<gchar *> *reslist);
private: private:
std::vector<Dict *> oLib; // word Libs. std::vector<Dict *> oLib; // word Libs.
int iMaxFuzzyDistance; int iMaxFuzzyDistance;
std::function<void(void)> progress_func; std::function<void(void)> progress_func;
bool verbose_;
}; };
enum query_t { enum query_t {
qtSIMPLE, qtSIMPLE, qtREGEXP, qtFUZZY, qtDATA
qtREGEXP,
qtFUZZY,
qtDATA
}; };
extern query_t analyze_query(const char *s, std::string& res);
extern query_t analyze_query(const char *s, std::string &res);

View File

@@ -15,117 +15,78 @@
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "config.h" # include "config.h"
#endif #endif
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <glib.h> #include <glib.h>
#include <glib/gi18n.h> #include <glib/gi18n.h>
#include <iomanip> #include <cstdlib>
#include <sstream> #include <cstdio>
#include <algorithm>
#include "utils.hpp" #include "utils.hpp"
std::string utf8_to_locale_ign_err(const std::string &utf8_str) std::string utf8_to_locale_ign_err(const std::string& utf8_str)
{ {
std::string res; std::string res;
const char *charset; const char *charset;
if (g_get_charset(&charset)) if (g_get_charset(&charset))
res = utf8_str; res = utf8_str;
else { else {
gsize bytes_read, bytes_written; gsize bytes_read, bytes_written;
glib::Error err; glib::Error err;
glib::CharStr tmp(g_convert_with_fallback(utf8_str.c_str(), -1, charset, "UTF-8", nullptr, glib::CharStr tmp(g_convert_with_fallback(utf8_str.c_str(), -1, charset, "UTF-8", nullptr,
&bytes_read, &bytes_written, get_addr(err))); &bytes_read, &bytes_written, get_addr(err)));
if (nullptr == get_impl(tmp)) { if (nullptr == get_impl(tmp)){
fprintf(stderr, _("Can not convert %s to current locale.\n"), utf8_str.c_str()); fprintf(stderr, _("Can not convert %s to current locale.\n"), utf8_str.c_str());
fprintf(stderr, "%s\n", err->message); fprintf(stderr, "%s\n", err->message);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
res = get_impl(tmp); res = get_impl(tmp);
} }
return res; return res;
} }
static void __for_each_file(const std::string &dirname, const std::string &suff, static void __for_each_file(const std::string& dirname, const std::string& suff,
const std::list<std::string> &order_list, const std::list<std::string> &disable_list, const std::list<std::string>& order_list, const std::list<std::string>& disable_list,
const std::function<void(const std::string &, bool)> &f) const std::function<void (const std::string&, bool)>& f)
{ {
GDir *dir = g_dir_open(dirname.c_str(), 0, nullptr); GDir *dir = g_dir_open(dirname.c_str(), 0, nullptr);
if (dir) { if (dir) {
const gchar *filename; const gchar *filename;
while ((filename = g_dir_read_name(dir)) != nullptr) { while ((filename = g_dir_read_name(dir))!=nullptr) {
const std::string fullfilename(dirname + G_DIR_SEPARATOR_S + filename); const std::string fullfilename(dirname+G_DIR_SEPARATOR_S+filename);
if (g_file_test(fullfilename.c_str(), G_FILE_TEST_IS_DIR)) if (g_file_test(fullfilename.c_str(), G_FILE_TEST_IS_DIR))
__for_each_file(fullfilename, suff, order_list, disable_list, f); __for_each_file(fullfilename, suff, order_list, disable_list, f);
else if (g_str_has_suffix(filename, suff.c_str()) && std::find(order_list.begin(), order_list.end(), fullfilename) == order_list.end()) { else if (g_str_has_suffix(filename, suff.c_str()) &&
const bool disable = std::find(disable_list.begin(), std::find(order_list.begin(), order_list.end(),
disable_list.end(), fullfilename)==order_list.end()) {
fullfilename) const bool disable = std::find(disable_list.begin(),
!= disable_list.end(); disable_list.end(),
fullfilename)!=disable_list.end();
f(fullfilename, disable); f(fullfilename, disable);
} }
} }
g_dir_close(dir); g_dir_close(dir);
} }
} }
void for_each_file(const std::list<std::string> &dirs_list, const std::string &suff,
const std::list<std::string> &order_list, const std::list<std::string> &disable_list,
const std::function<void(const std::string &, bool)> &f)
{
for (const std::string &item : order_list) {
const bool disable = std::find(disable_list.begin(), disable_list.end(), item) != disable_list.end();
f(item, disable);
}
for (const std::string &item : dirs_list)
__for_each_file(item, suff, order_list, disable_list, f);
}
// based on https://stackoverflow.com/questions/7724448/simple-json-string-escape-for-c/33799784#33799784 void for_each_file(const std::list<std::string>& dirs_list, const std::string& suff,
std::string json_escape_string(const std::string &s) const std::list<std::string>& order_list, const std::list<std::string>& disable_list,
const std::function<void (const std::string&, bool)>& f)
{ {
std::ostringstream o; for (const std::string & item : order_list) {
for (auto c = s.cbegin(); c != s.cend(); c++) { const bool disable = std::find(disable_list.begin(), disable_list.end(), item) != disable_list.end();
switch (*c) { f(item, disable);
case '"': }
o << "\\\""; for (const std::string& item : dirs_list)
break; __for_each_file(item, suff, order_list, disable_list, f);
case '\\':
o << "\\\\";
break;
case '\b':
o << "\\b";
break;
case '\f':
o << "\\f";
break;
case '\n':
o << "\\n";
break;
case '\r':
o << "\\r";
break;
case '\t':
o << "\\t";
break;
default:
if ('\x00' <= *c && *c <= '\x1f') {
o << "\\u"
<< std::hex << std::setw(4) << std::setfill('0') << (int)*c;
} else {
o << *c;
}
}
}
return o.str();
} }

View File

@@ -1,78 +1,68 @@
#pragma once #pragma once
#include <cassert>
#include <cstddef>
#include <functional>
#include <glib.h> #include <glib.h>
#include <list> #include <cassert>
#include <string> #include <string>
#include <list>
#include <functional>
template <typename T, typename unref_res_t, void (*unref_res)(unref_res_t *)> template <typename T, typename unref_res_t, void (*unref_res)(unref_res_t *)>
class ResourceWrapper class ResourceWrapper {
{
public: public:
ResourceWrapper(T *p = nullptr) ResourceWrapper(T *p = nullptr) : p_(p) {}
: p_(p) ~ResourceWrapper() { free_resource(); }
{ ResourceWrapper(const ResourceWrapper&) = delete;
} ResourceWrapper& operator=(const ResourceWrapper&) = delete;
~ResourceWrapper() { free_resource(); } T *operator->() const { return p_; }
ResourceWrapper(const ResourceWrapper &) = delete; bool operator!() const { return p_ == nullptr; }
ResourceWrapper &operator=(const ResourceWrapper &) = delete; const T& operator[](size_t idx) const {
T *operator->() const { return p_; }
bool operator!() const { return p_ == nullptr; }
const T &operator[](size_t idx) const
{
assert(p_ != nullptr); assert(p_ != nullptr);
return p_[idx]; return p_[idx];
} }
void reset(T *newp) void reset(T *newp) {
{ if (p_ != newp) {
if (p_ != newp) { free_resource();
free_resource(); p_ = newp;
p_ = newp; }
} }
}
friend inline bool operator==(const ResourceWrapper &lhs, std::nullptr_t) noexcept friend inline T *get_impl(const ResourceWrapper& rw) {
{ return rw.p_;
return !lhs.p_; }
}
friend inline bool operator!=(const ResourceWrapper &lhs, std::nullptr_t) noexcept friend inline T **get_addr(ResourceWrapper& rw) {
{ return &rw.p_;
return !!lhs.p_; }
}
friend inline T *get_impl(const ResourceWrapper &rw)
{
return rw.p_;
}
friend inline T **get_addr(ResourceWrapper &rw)
{
return &rw.p_;
}
private: private:
T *p_; T *p_;
void free_resource() void free_resource() { if (p_) unref_res(p_); }
{
if (p_) // Helper for enabling 'if (sp)'
unref_res(p_); struct Tester {
Tester() {}
private:
void operator delete(void*);
};
public:
// enable 'if (sp)'
operator Tester*() const {
if (!*this)
return 0;
static Tester t;
return &t;
} }
}; };
namespace glib namespace glib {
{ typedef ResourceWrapper<gchar, void, g_free> CharStr;
typedef ResourceWrapper<gchar, void, g_free> CharStr; typedef ResourceWrapper<GError, GError, g_error_free> Error;
typedef ResourceWrapper<GError, GError, g_error_free> Error; }
} // namespace glib
extern std::string utf8_to_locale_ign_err(const std::string &utf8_str); extern std::string utf8_to_locale_ign_err(const std::string& utf8_str);
extern void for_each_file(const std::list<std::string> &dirs_list, const std::string &suff, extern void for_each_file(const std::list<std::string>& dirs_list, const std::string& suff,
const std::list<std::string> &order_list, const std::list<std::string> &disable_list, const std::list<std::string>& order_list, const std::list<std::string>& disable_list,
const std::function<void(const std::string &, bool)> &f); const std::function<void (const std::string&, bool)>& f);
extern std::string json_escape_string(const std::string &str);

View File

@@ -0,0 +1,27 @@
//g++ `pkg-config --cflags glib-2.0` call_stardict_strcmp.cpp `pkg-config --libs glib-2.0`
#include <glib.h>
#include <locale.h>
#include <cstdlib>
#include <cstring>
#include <iostream>
static inline gint stardict_strcmp(const gchar *s1, const gchar *s2)
{
const gint a = g_ascii_strcasecmp(s1, s2);
if (a == 0)
return strcmp(s1, s2);
else
return a;
}
int main()
{
setlocale(LC_ALL, "");
std::cin.sync_with_stdio(false);
std::string line1, line2;
while (std::getline(std::cin, line1) &&
std::getline(std::cin, line2)) {
std::cout << stardict_strcmp(line1.c_str(), line2.c_str()) << "\n";
}
return EXIT_SUCCESS;
}

42
tests/generate_strings_pairs.py Executable file
View File

@@ -0,0 +1,42 @@
#!/usr/bin/env python3
import random, sys
fname = "/home/evgeniy/projects/competitions/words/data/words.txt"
with open(fname, "r") as fin:
words = sorted(set([word.strip() for word in fin.readlines()]))
res = []
for i in range(0, len(words)):
max_idx = len(words) - 1
idx1 = random.randint(0, max_idx)
idx2 = random.randint(0, max_idx)
res.append((words[idx1], words[idx2]))
letters = "abcdefghijklmnopqrstuvwxyzабвгдеёжзийклмнопрстуфкцчщьъэюя"
letters += letters.upper()
letters += " \t!@#$%^&*()[]"
def gen_word(req_word_len):
max_idx = len(letters) - 1
res = ""
for i in range(0, req_word_len):
res += letters[random.randint(0, max_idx)]
return res
for i in range(0, 10000):
l1 = random.randint(1, 100)
l2 = random.randint(1, 100)
res.append((gen_word(l1), gen_word(l2)))
for i in range(0, 10000):
l1 = random.randint(1, 100)
res.append((gen_word(l1), gen_word(l1)))
for item in res:
print(item[0])
print(item[1])

View File

@@ -1,9 +0,0 @@
StarDict's dict ifo file
version=3.0.0
bookname=Russian-English Dictionary (ru-en)
wordcount=415144
idxfilesize=12344255
sametypesequence=h
synwordcount=1277580
author=Vuizur
description=

View File

@@ -1,7 +0,0 @@
StarDict's dict ifo file
version=3.0.0
bookname=Test multiple results
wordcount=246
idxfilesize=5977
synwordcount=124
description=

View File

@@ -1,7 +0,0 @@
StarDict's dict ifo file
version=2.4.2
bookname=Test synonyms
wordcount=2
synwordcount=2
idxfilesize=32
sametypesequence=m

View File

@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<stardict xmlns:xi="http://www.w3.org/2003/XInclude">
<info>
<version>2.4.2</version>
<bookname>Test synonyms</bookname>
<author></author>
<email></email>
<website></website>
<description></description>
<date></date>
<dicttype></dicttype>
</info>
<article><key>test</key><synonym>foo</synonym><synonym>bar</synonym>
<definition type="m">
<![CDATA[result of test]]>
</definition>
</article>
<article><key>testawordy</key>
<definition type="m">
<![CDATA[word that ends in y to test with fuzzy search in -ied]]>
</definition>
</article>
</stardict>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@ unset SDCV_PAGER
have=`"$PATH_TO_SDCV" --data-dir /tmp/bugagaga -l | wc -l` have=`"$PATH_TO_SDCV" --data-dir /tmp/bugagaga -l | wc -l`
#do not count header #do not count header
have=$(($have-1)) have=$(($have-1))
ndicts=`find "${XDG_DATA_HOME:-$HOME/.local/share}"/stardict/dic -name "*.ifo" -print | wc -l` ndicts=`find "${HOME}"/.stardict/dic -name "*.ifo" -print | wc -l`
#ndicts=$(($ndicts+1)) #ndicts=$(($ndicts+1))
if [ $have -ne $ndicts ]; then if [ $have -ne $ndicts ]; then
ndicts=$(($ndicts-1)) ndicts=$(($ndicts-1))

View File

@@ -1,24 +0,0 @@
#!/bin/sh
set -e
SDCV="$1"
TEST_DIR="$2"
unset SDCV_PAGER
test_word() {
WORD=$1
EXPECTED=$2
TAG=$3
RES=$($SDCV -e -n --data-dir "$TEST_DIR" -u "Test synonyms" $WORD | grep "$TAG")
if [ "$EXPECTED" != "$RES" ]; then
echo "synonym for $WORD should be '$EXPECTED' but was '$RES'"
exit 1
fi
}
test_word testawordies "Nothing similar to testawordies, sorry :(" "Nothing similar"
test_word testawordy "word that ends in y to test with fuzzy search in -ied" "fuzzy"
exit 0

View File

@@ -1,32 +0,0 @@
#!/bin/sh
set -e
SDCV="$1"
TEST_DIR="$2"
unset SDCV_PAGER
unset STARDICT_DATA_DIR
test_json() {
EXPECTED=$(echo "$1" | jq 'sort')
shift
RESULT=$($SDCV "$@" | jq 'sort')
if [ "$EXPECTED" != "$RESULT" ]; then
echo "expected $EXPECTED but got $RESULT"
exit 1
fi
}
test_json '[{"name": "Russian-English Dictionary (ru-en)", "wordcount": "415144"},
{"name": "Test synonyms", "wordcount": "2"},
{"name": "Test multiple results", "wordcount": "246"},
{"name": "Sample 1 test dictionary", "wordcount": "1"},
{"name": "test_dict", "wordcount": "1"}]' -x -j -l -n --data-dir "$TEST_DIR"
test_json '[{"dict": "Test synonyms","word":"test","definition":"\u000aresult of test"}]' -x -j -n --data-dir "$TEST_DIR" foo
test_json '[]' -x -j -n --data-dir "$TEST_DIR" foobarbaaz
# Test multiple searches, with the first failing.
test_json '[][{"dict": "Test synonyms","word":"test","definition":"\u000aresult of test"}]' -x -j -n --data-dir "$TEST_DIR" foobarbaaz foo
exit 0

View File

@@ -4,7 +4,7 @@ PATH_TO_SDCV="$1"
ndicts=`"$PATH_TO_SDCV" -l | wc -l` ndicts=`"$PATH_TO_SDCV" -l | wc -l`
ndicts=$(($ndicts-1)) ndicts=$(($ndicts-1))
ncom=`find /usr/share/stardict/dic -name "*.ifo" | wc -l` ncom=`find /usr/share/stardict/dic -name "*.ifo" | wc -l`
nspe=`find "${XDG_DATA_HOME:-$HOME/.local/share}"/stardict/dic -name "*.ifo" | wc -l` nspe=`find "${HOME}"/.stardict/dic -name "*.ifo" | wc -l`
nmy=$(($ncom+$nspe)) nmy=$(($ncom+$nspe))
if [ $nmy -ne $ndicts ]; then if [ $nmy -ne $ndicts ]; then

View File

@@ -1,67 +0,0 @@
#!/bin/sh
set -e
SDCV="$1"
TEST_DIR="$2"
unset SDCV_PAGER
unset STARDICT_DATA_DIR
test_json() {
word="$1"
jq_cmp="$2"
result="$("$SDCV" --data-dir "$TEST_DIR" -exjn "$word" | sed 's|\\n|\\u000a|g')"
cmp_result="$(echo "$result" | jq "$jq_cmp")"
if [ "$cmp_result" != "true" ]; then
echo "expected '$jq_cmp' to return true, but $result didn't"
exit 1
fi
}
# Basic two-result search for the same headword.
test_json bark \
'. == [
{"dict":"Test multiple results","word":"bark","definition":"\u000aThe harsh sound made by a dog."},
{"dict":"Test multiple results","word":"bark","definition":"\u000aThe tough outer covering of trees and other woody plants."}
]'
# Multi-result search where one word exists as both a synyonym and a separate
# headword. This ensures that if there is a matching synyonym we don't skip the
# regular search.
test_json cat \
'. == [
{"dict":"Test multiple results","word":"cat","definition":"\u000aA cute animal which (rarely) barks."},
{"dict":"Test multiple results","word":"lion","definition":"\u000aA larger cat which might bite your head off."},
{"dict":"Test multiple results","word":"panther","definition":"\u000aI know very little about panthers, sorry."}
]'
# Many-result search for a word that matches 120 distinct headwords.
test_json many_headwords 'length == 120'
test_json many_headwords 'all(.word == "many_headwords")'
test_json many_headwords \
'to_entries | map(.value.definition == "\u000aDefinition for [many_headwords] entry #\(.key+1) (same headword).") | all'
# Many-result search for 120 words that have the same synonym.
test_json many_synonyms 'length == 120'
test_json many_synonyms \
'to_entries | map(.value.word == "many_synonyms-\(.key+101)") | all'
test_json many_synonyms \
'to_entries | map(.value.definition == "\u000aDefinition for [many_synonyms-\(.key+101)] (same synonym).") | all'
# Ensure that we don't return more than one result even if a word can be
# resolved in more than one way.
#
# Most well-formed dictionaries don't have entries like this (it basically
# requires you to have a dictionary where there is a synonym that is identical
# to a word's headword or multiple identical synyonym entries).
#
# This entry was created by creating extra synonyms with different names then
# modifying the .syn file manually.
test_json many_resolution_paths \
'. == [
{"dict":"Test multiple results","word":"many_resolution_paths",
"definition":"\u000aDefinition for [many_resolution_paths] headword (same word, multiple synonym entries)."}
]'
exit 0

View File

@@ -1,18 +0,0 @@
#!/bin/sh
set -e
PATH_TO_SDCV="$1"
TEST_DIR="$2"
unset SDCV_PAGER
unset STARDICT_DATA_DIR
RES=$("$PATH_TO_SDCV" -n -x --data-dir="$TEST_DIR/not-unix-newlines-ifo" -l | tail -n 1)
if [ "$RES" = "Russian-English Dictionary (ru-en) 415144" ]; then
exit 0
else
echo "test failed, unexpected result: $RES" >&2
exit 1
fi

View File

@@ -1,19 +0,0 @@
#!/bin/sh
set -e
SDCV="$1"
TEST_DIR="$2"
unset SDCV_PAGER
unset STARDICT_DATA_DIR
DICTS=$($SDCV -x -n -l --data-dir "$TEST_DIR" | tail -n +2 | wc -l)
# the expected result:
ACTUAL_DICTS=$(find "$TEST_DIR" -name "*.ifo" | wc -l)
if [ $DICTS -ne $ACTUAL_DICTS ]; then
echo "number of dictionaries in $TEST_DIR should be $ACTUAL_DICTS but was $DICTS according to sdcv"
exit 1
fi
exit 0

View File

@@ -1,22 +0,0 @@
#!/bin/sh
SDCV="$1"
TEST_DIR="$2"
unset SDCV_PAGER
test_return_code() {
WORD=$1
EXPECTED=$2
$SDCV -e -n --data-dir "$TEST_DIR" -u "Test synonyms" $WORD > /dev/null
RC=$?
if [ $RC -ne $EXPECTED ]; then
echo "Return code for $WORD should be '$EXPECTED' but was '$RC'"
exit 1
fi
}
test_return_code testawordy 0
test_return_code testawordies 2
exit 0

View File

@@ -1,22 +0,0 @@
#!/bin/sh
set -e
SDCV="$1"
TEST_DIR="$2"
unset SDCV_PAGER
test_word() {
WORD=$1
RES=$($SDCV -n --data-dir "$TEST_DIR" -u "Test synonyms" $WORD | grep result)
if [ "result of test" != "$RES" ]; then
echo "synonym for $WORD should be 'result of test' but was '$RES'"
exit 1
fi
}
test_word foo
test_word bar
test_word test
exit 0

View File

@@ -5,8 +5,8 @@ set -e
PATH_TO_SDCV="$1" PATH_TO_SDCV="$1"
TESTS_DIR="$2" TESTS_DIR="$2"
mkdir -p "${XDG_DATA_HOME:-$HOME/.local/share}"/stardict/dic mkdir -p "${HOME}"/.stardict/dic
cp -R "${TESTS_DIR}/stardict-test_dict-2.4.2" "${XDG_DATA_HOME:-$HOME/.local/share}"/stardict/dic cp -R "${TESTS_DIR}/stardict-test_dict-2.4.2" "${HOME}"/.stardict/dic
unset SDCV_PAGER unset SDCV_PAGER
RES=`"$PATH_TO_SDCV" -n -u test_dict test | grep "test passed"` RES=`"$PATH_TO_SDCV" -n -u test_dict test | grep "test passed"`
@@ -15,6 +15,6 @@ if [ -z "$RES" ]; then
exit 1 exit 1
fi fi
rm -fr "${XDG_DATA_HOME:-$HOME/.local/share}"/stardict/dic/stardict-test_dict-2.4.2 rm -fr "${HOME}"/.stardict/dic/stardict-test_dict-2.4.2
exit 0 exit 0

View File

@@ -4,8 +4,9 @@ set -e
PATH_TO_SDCV="$1" PATH_TO_SDCV="$1"
TESTS_DIR="$2" TESTS_DIR="$2"
mkdir -p "${XDG_DATA_HOME:-$HOME/.local/share}"/stardict/dic
cp -R "${TESTS_DIR}/rus-eng-stardict-2.4.2" "${XDG_DATA_HOME:-$HOME/.local/share}"/stardict/dic/ mkdir -p "${HOME}"/.stardict/dic
cp -R "${TESTS_DIR}/rus-eng-stardict-2.4.2" "${HOME}"/.stardict/dic/
unset SDCV_PAGER unset SDCV_PAGER
export LANG=ru_RU.KOI8-R export LANG=ru_RU.KOI8-R

View File

@@ -0,0 +1,10 @@
#!/usr/bin/env python3
import sys
with open(sys.argv[1], "r") as f:
with open(sys.argv[2], "w") as out:
words = set([word.strip() for word in f.readlines()])
for word in words:
out.write(word + "\n")
out.write(word + "\n")

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,7 @@
StarDict's dict ifo file
version=2.4.2
wordcount=1671704
idxfilesize=30235592
bookname=
date=2016.06.18
sametypesequence=x

3343408
tests/words_dic/words.dummy Normal file

File diff suppressed because it is too large Load Diff