Arround Zim Desktop
Zim Desktop is a great software! It allows to create and manage one or more personnal wiki-like knowledge database, diligently and easily, on any desktop computer, without the use of any cloud service (preserving data privacy), and without the need of a webserver!
This web page is written to share my experience with persons who have minimal experience with both Zim Desktop and a debian based GNU/Linux operating system.
If you do not know the free software Zim Desktop yet, you can read this excellent presentation (PDF) from its creator Jaap Karssenberg and download the program from the official site (look at the bottom of zim page).
Bringing notebooks to portable environment
Working at my custommers'homes, I am often brought to fix computers whose are running under Microsoft Windows for the greater part. In addition to my differents tools such as Live Boot CDs/DVDs/USB sticks, portable programs and Windows batch programs, I have a huge polypropylene pockets display book filled by technical specifications and procedures of my own.
But the world of computers is like the medecine one: there is a continuous multiplication of IT sub-departments and IT specification references grow in the manner of an exponential curve. So like other perfectionist persons, I feed a personal IT notebook within Zim Desktop for years.
Synchronizing my IT Zim Desktop Linux notebook to my portableApps working USB stick before each intervention, I had to open it with the ultra-basic program notepad.exe for a long time, until I find the good way to make notebooks created within Zim Desktop Linux full compatibles both with Zim Dekstop Windows portable and Zim Dekstop Linux portable.
Below, I share my experience with other GNU/Linux users in order to port their own Zim notebooks to other PCs without the need to install Zim Desktop on them.
Synchronize Zim Linux Notebooks to Zim Windows Portable
Zim Windows Portable specifications
Here is a file tree summary of Zim Windows Portable with two notebooks :
ZimDesktopWikiPortable/
├── App/
├── Data/
│ ├── Config/
│ │ └── zim/
│ │ ├── accelmap
│ │ ├── notebooks.list
│ │ ├── preferences.conf
│ │ └── style.conf
│ ├── Notebooks/
│ │ ├── my-first-notebook/
│ │ │ └── notebook.zim
│ │ └── my-second-notebook/
│ │ └── notebook.zim
│ └── settings/
├── Other/
└── ZimDesktopWikiPortable.exe
Zim Desktop portability considerations
Invariant specifications used by Zim Desktop (interoperability)
All Zim files (configuration + notebooks ones) are always
UTF-8 encoded, regardless of the operating system Zim is running on
All Zim configuration files always contain
POSIX directory separators (slash /), regardless of the operating system Zim is running on
The page "Config files" of the official manual introduces the default content of the environment path variables XDG_CONFIG_HOME and XDG_DATA_HOME used by Zim. At the time I wrote this lines (13 sep 2015) – not having inspected zim/config/basedirs.py of source project –, thoses informations for XDG_DATA_HOME seems to me a little confused or outdated1).
Regardless of the operating system Zim is running on, let's remember the invariant specification data and meta-data storage paths are:
Zim Windows portable defaults this environment variables to the following values:
Limitation for Zim Windows portable:
Even though Zim works perfectly with absolute path names, nobody can know in advance the absolute path name for a USB stick! For that reason, one can store a notebook into any other directory which it is a descendant of $XDG_DATA_HOME, but it is highly inadvisable to store a notebook outside of $XDG_DATA_HOME if one wants Zim Windows portable to access it.
Porting a notebook
Each notebook is made up of a standalone directory containing a single configuration file notebook.zim
wich specifies what End Of Line (EOL) type the notebook files are made of (unix, dos…). This is why all kinds of Zim versions running on all kinds of computers can access all kinds of notebooks regardless of the operation system they were created.
Also, there is nothing special to do with a rsync notebook target, except converting EOL of notebook.zim into the target operating system's one, so that it can be read.
Example of a file notebook.zim created by Zim Desktop Windows portable :
[Notebook]
version=0.4
name=MyNewWiki
interwiki=
home=Home
icon=
document_root=
shared=True
endofline=dos
disable_trash=False
profile=
For data privacy, one must set the following options when working with Zim portable on a foreign computer:
Example of a file notebook.zim
that one must create if missing within a notebook directory called "MyNewWiki":
[Notebook]
version=0.4
name=MyNewWiki
interwiki=
home=MyNewWiki
icon=
document_root=
endofline=unix
profile=
shared=False
disable_trash=True
Porting the configuration files
The target file notebooks.list
must be created as follow :
Each rsync requested notebook must have an entry and some meta-datas
Each notebook path must be relative to the directory $XDG_DATA_HOME
(ZimDesktopWikiPortable/Data), symbolised by tilde ~. For example, the path to MyNewNoteBook witch is located into ZimDesktopWikiPortable/Data/Notebooks/ will be: ~/Notebooks/MyNewNoteBook
For wide compatibility the notation convention must conform zim version prior to 0.61.
(As of Zim 0.61, header section [Notebook]
is written [Notebook N]
where N is an incremental number starting at 1, and in the [NotebookList]
section lines begining by path such as ~/Notebooks/a_name
are replaced with N=~/Notebooks/a_name
where N is an incremental number starting at 1)
Example of a file notebooks.list
created by Zim prior to version 0.61, and that we shall create for full compatibility:
[NotebookList]
Default=~/Notebooks/MyNewWiki
~/Notebooks/MyNewWiki
[Notebook]
uri=~/Notebooks/MyNewWiki
name=MyNewWiki
interwiki=None
icon=None
Example of a file notebooks.list
created by Zim as of version 0.61:
[NotebookList]
Default=~/Notebooks/MyNewWiki
1=~/Notebooks/MyNewWiki
[Notebook 1]
uri=~/Notebooks/MyNewWiki
name=MyNewWiki
interwiki=None
icon=None
Bash function to port notebooks "made-in-linux" to Zim Windows portable
Here is a BASH (fr) function I wrote to rsync one or more notebooks created under Zim Desktop Linux to a Zim Windows Portable base directory. Feel free to modify and bring it to your scripts. It works well under debian 7 and the following package depends are required: coreutils, bash, findutils, grep, sed, rsync.
Usage:
rsync_zim_linux_to_zim_windows_portable [-v] source_dir dest_dir notebook1 [notebook2] [...] [notebookN]
Options:
-v : verbose mode to standard output
Example:
Assuming that:
I have 2 notebooks called work and personal in my directory $HOME/Documents/zim-database
My USB stick is mounted into /media/usb_stick
From the root of the USB stick, the Zim Windows portable directory is: PortableApps/ZimDesktopWikiPortable
All that I have to do within a BASH script is to call the function rsync_zim_to_zim_windows_portable() as following:
rsync_zim_to_zim_windows_portable -v $HOME/Documents/zim-database /media/usb_stick/PortableApps/ZimDesktopWikiPortable work personal
Here is the BASH function:
## Function to rsync one or more notebooks from linux zim to a windows ZimDesktopWikiPortable
## Note: Whatever OS is, each zim portable config file must contain UTF-8 CR/LF with UNIX slash
## Version: 1.0.1
## Debian depends: coreutils, bash, findutils, grep, sed, rsync
##
## Usage:
## rsync_zim_linux_to_zim_windows_portable [-v] source_dir dest_dir notebook_name1 [notebook_name2] [...] [notebook_nameN]
##
## Options:
## -v : verbose mode to standard output
##
##
## Example with 2 notebooks:
## rsync_zim_linux_to_zim_windows_portable -v $HOME/Documents/zim-database /media/usb_stick/PortableApps/ZimDesktopWikiPortable work personal
##
function rsync_zim_linux_to_zim_windows_portable() {
local _VERBOSE=""
local _OPTION
unset OPTIND
while getopts ":v" _OPTION; do
case ${_OPTION} in
v) _VERBOSE="-vvv";;
esac
done
test $OPTIND -ne 1 && shift $(($OPTIND - 1))
local _ERROR=0
local _CONFIG_VER="0.4"
local _NOTEBOOK_NR=1
local _SRC_DIR="$1"
local _DST_DIR="$2"
shift 2
test -d "${_SRC_DIR}" || return 1
test -d "${_DST_DIR}" || return 2
## Init directories
local _TEMP_DIR=`mktemp -d -p /tmp ${FUNCNAME}_XXXXXXXXXXXXXXXXXXXX`
test -d "${_DST_DIR}/Data/Config/zim" || mkdir -p "${_DST_DIR}/Data/Config/zim"
test -d "${_DST_DIR}/Data/Notebooks" || mkdir -p "${_DST_DIR}/Data/Notebooks"
## Prevent portable app warning by deleting last running path
rm -f "${_DST_DIR}/Data/settings/ZimDesktopWikiPortableSettings.ini"
## 1/4 - General configuration files
## ${XDG_CONFIG_HOME:-$HOME/.config}/zim/preferences.conf -> "${_DST_DIR}/Data/Config/zim/preferences.conf"
## Copy the config file by converting LF to CR/LF
test -f ${XDG_CONFIG_HOME:-$HOME/.config}/zim/preferences.conf &&
sed 's/$/\r/' ${XDG_CONFIG_HOME:-$HOME/.config}/zim/preferences.conf >"${_DST_DIR}/Data/Config/zim/preferences.conf"
## ${XDG_CONFIG_HOME:-$HOME/.config}/zim/style.conf -> "${_DST_DIR}/Data/Config/zim/style.conf"
## Copy the config file by converting LF to CR/LF
test -f ${XDG_CONFIG_HOME:-$HOME/.config}/zim/style.conf &&
sed 's/$/\r/' ${XDG_CONFIG_HOME:-$HOME/.config}/zim/style.conf >"${_DST_DIR}/Data/Config/zim/style.conf"
## Create the header of "${_DST_DIR}/Data/Config/zim/notebooks.list"
## with first notebook provided in args as default one
echo -en "[NotebookList]\r\nDefault=~/Notebooks/$1\r\n" >${_TEMP_DIR}/notebooks.list.0
## 2/4 - Remove any existing target notebook that is not in args
find "${_DST_DIR}/Data/Notebooks" -mindepth 1 -maxdepth 1 -type d | while read _THE_DIR; do
echo $@ | grep -Eq "\<${_THE_DIR##*/}\>" || rm -rf "${_THE_DIR}"
done
## 3/4 - rsync each notebook provided in args
while test -n "$1"; do
test -d "${_SRC_DIR}/$1" || _ERROR=$(( 10 + ${_NOTEBOOK_NR} ))
## Stop rsync loop at first error
test ${_ERROR} -ne 0 && break
## Add to the header of future file "${_DST_DIR}/Data/Config/zim/notebooks.list"
echo -en "~/Notebooks/$1\r\n" >>${_TEMP_DIR}/notebooks.list.0
## Add the current notebook to the file "${_DST_DIR}/Data/Config/zim/notebooks.list"
echo -en "[Notebook]\r\nuri=~/Notebooks/$1\r\nname=$1\r\ninterwiki=None\r\nicon=None\r\n\r\n" >>${_TEMP_DIR}/notebooks.list.${_NOTEBOOK_NR}
## rsync "${_SRC_DIR}/$1" into "${_DST_DIR}/Data/Notebooks"
## Note: --modify-window=1 is required for FAT target
eval rsync ${_VERBOSE} --recursive --times --delete-after --modify-window=1 --filter=\"exclude notebook.zim\" \"${_SRC_DIR}/$1\" \"${_DST_DIR}/Data/Notebooks\"
## "${_SRC_DIR}/$1.txt" -> "${_DST_DIR}/Data/Notebooks/$1.txt"
test -f "${_SRC_DIR}/$1.txt" && cp -u "${_SRC_DIR}/$1.txt" "${_DST_DIR}/Data/Notebooks/$1.txt"
## "${_SRC_DIR}/$1/notebook.zim" -> "${_DST_DIR}/Data/Notebooks/$1/notebook.zim"
if test -f "${_SRC_DIR}/$1/notebook.zim"; then
## Copy the config file by converting LF to CR/LF,
## disabling hard disk trash can and hard disk buffer
sed 's/$/\r/' "${_SRC_DIR}/$1/notebook.zim" >"${_DST_DIR}/Data/Notebooks/$1/notebook.zim"
sed -i '/^\(shared\|disable_trash\)=/d' "${_DST_DIR}/Data/Notebooks/$1/notebook.zim"
sed -i '/^\s*$/,$h;/^\s*$/d' "${_DST_DIR}/Data/Notebooks/$1/notebook.zim"
echo -en "shared=False\r\ndisable_trash=True\r\n\r\n" >>"${_DST_DIR}/Data/Notebooks/$1/notebook.zim"
else
## Create the file "${_DST_DIR}/Data/Notebooks/$1/notebook.zim"
echo -en "[Notebook]\r\nversion=${_CONFIG_VER}\r\nname=$1\r\ninterwiki=\r\nhome=$1\r\nicon=\r\ndocument_root=\r\nendofline=unix\r\nprofile=\r\nshared=False\r\ndisable_trash=True\r\n\r\n" >"${_DST_DIR}/Data/Notebooks/$1/notebook.zim"
fi
shift
_NOTEBOOK_NR=$(( ${_NOTEBOOK_NR} + 1 ))
done
## 4/4 Create the file "${_DST_DIR}/Data/Config/zim/notebooks.list"
echo -en "\r\n" >>${_TEMP_DIR}/notebooks.list.0
cat ${_TEMP_DIR}/notebooks.list.* > "${_DST_DIR}/Data/Config/zim/notebooks.list"
rm -rf ${_TEMP_DIR}
return ${_ERROR}
}
Share notebooks from Zim windows portable to Zim linux portable
Suppose you have to work on a foreign PC running on a debian based GNU/Linux and you want to read your IT knowledge base stored within a Zim Destkop Windows portable with the use of the Zim python sources…
The BASH script (fr) I have written turns an unpacked source Zim tarball into a Debian based GNU/Linux portable that works with Zim Windows portable notebooks. To do that, the following tasks are performed:
Check for package depends installation
Check for paths
Path to the most recent python2 ELF found within /usr/bin
Path to the most recent zim.py found within the script directory
Path to ZimDesktopWikiPortable/Data/Config found next to the script directory
Convert on the fly the config files of ZimDesktopWikiPortable to a portable linux context
(
EOL are converted from CRLF to LF. In the file notebooks.list, the notebook paths are converted to match the linux ones. In the files notebook.zim, options are set: shared=False and disable_trash=True. In the file preferences.conf, TrayIconPlugin is disabled if the standalone option is requested)
Execute python 2.X with zim.py script within a special environment.
(If the standalone option was requested and the script executed with the gui option --standalone
has refused it (old zim versions), re-execute without that gui option)
Wait for all processes of zim.py to terminate
Convert the config files of ZimDesktopWikiPortable back to a portable Windows context
(
EOL are converted from LF to CRLF and in the file notebooks.list, the path of the notebooks are converted to match the portable Windows ones. Original files preferences.conf and notebook.zim are restored)
That script was tested on Debian 7, Devuan 1 alpha 2 and Ubuntu 14.04.
The debian depend packages are: coreutils, bash, findutils, grep, sed, gawk, python (<3), python-gtk2, python-gobject, python-xdg, xdg-utils
Feel free to use and fork it.
In addition to the technical specifications introduced in paragraph "Zim Desktop portability considerations", that script pays heed to the following Zim properties:
notebooks.list:
preferences.conf:
Memory:
Here is the typical file layout for that script to run:
root of USB stick/
├── PortableApps/
│ └── ZimDesktopWikiPortable/
└── sh/
├── zim-0.xx/
└── that_script.sh
Usage assuming we call that script zim_linux_portable.sh:
zim_linux_portable.sh [-f|-g WxH+X+Y] [-h] [-s]
Options :
-f : start Zim portable with option '--fullscreen'
-g WxH+X+Y : start Zim portable with option '--geometry args'
example: -g 600x600+150+150 (default: '-g 700x500+100+100')
-h : print this help and exit
-s : start Zim Desktop Linux Portable with option '--standalone'
#!/bin/bash
SCRIPTNAME="${0##*/}"
SCRIPTDIR="$(readlink -f "${0%/*}")"
SCRIPTVERSION="1.0.0"
AUTHOR="Médéric"
RELEASE_DATE="2015"
LICENCE="GPLv3"
FRIENDLY_TASK="Launch Zim Desktop Windows Portable within a Linux context"
WELCOME="$SCRIPTNAME v.$SCRIPTVERSION by $AUTHOR ($RELEASE_DATE)"
## @author Médéric http://aide.ordi49.fr/wiki/contact-me.html
## Licence: GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
## THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
## APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
## HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
## OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
## THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
## PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
## IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
## ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
## Tested on Debian 7, Devuan 1 alpha 2, Ubuntu14.04
## Other variables
GEOMETRY_DEFAULT="--geometry 700x500+100+100"
## That script aims to launch a Zim linux portable program on a computer
## where zim package is not installed, in order to read the zim notebooks
## that reside within a Windows ZimDesktopWikiPortable USB stick
## That script was writen for debian and devuan GNU/Linux distribution,
## but it should run on any other debian based system
## Feel free to adapt and fork it at your convenience
## Debian depends: coreutils, bash, findutils, grep, sed, gawk,
## python (<3), python-gtk2, python-gobject, python-xdg, xdg-utils
## If one package of the second line is missing, an error is output
## telling the user that it is not installed
## That script can reside within a 1 level-max sub-directory of the USB stick
## (for example a subdir called sh/) or within the root of it
## The Zim linux source directory (downloaded from http://zim-wiki.org/downloads/)
## is supposed to reside within the same or a sub-directory of the script
## The ZimDesktopWikiPortable/Data/Config directory is supposed to reside
## somewhere on the USB stick
## Typical file layout for that script to run:
## root of USB stick/
## ├── PortableApps/
## │ └── ZimDesktopWikiPortable/
## └── sh/
## ├── zim-0.xx/
## └── that_script.sh
## How it runs:
## 1. Check for package depends installation
## 2. Check for paths :
## - Path to the most recent python2 ELF found within /usr/bin
## - Path to the most recent zim.py found within the script directory
## - Path to ZimDesktopWikiPortable/Data/Config found next to the script directory
## 3. Convert on the fly the config files of ZimDesktopWikiPortable to a portable linux context
## (EOL are converted from CRLF to LF. In the file notebooks.list,
## the notebook paths are converted to match the linux ones.
## In the file preferences.conf, TrayIconPlugin is disabled if the standalone option is requested.
## In the files notebook.zim, options are set: shared=False and disable_trash=True)
## 4. Execute python 2.X with zim.py script within a special environment.
## (If the standalone option was requested and the script executed with the gui option
## --standalone has refused it (old zim versions), re-execute without that gui option)
## 5. Wait for all processes of zim.py to terminate
## 6. Convert the config files of ZimDesktopWikiPortable back to a portable Windows context
## (EOL are converted from LF to CRLF and in the file notebooks.list,
## the path of the notebooks are converted to match the portable Windows ones.
## Original files preferences.conf and notebook.zim are restored)
## ____ Functions ____
function print_usage() {
## Depends: global variables SCRIPTNAME, GEOMETRY_DEFAULT
echo "Usage:"
echo "$SCRIPTNAME [-f|-g WxH+X+Y] [-h] [-s]"
echo "Options :"
echo " -f : start Zim portable with option '--fullscreen'"
echo " -g WxH+X+Y : start Zim portable with option '--geometry args'"
echo " example: -g 600x600+150+150 (default: '-g ${GEOMETRY_DEFAULT/#--geometry /}')"
echo " -h : print this help and exit"
echo " -s : start Zim Desktop Linux Portable with option '--standalone'"
echo
exit
}
function get_cmd_options() {
## Syntax:
## get_cmd_options $@; test $OPTIND -ne 0 && shift $(($OPTIND - 1))
unset OPTIND
local _OPTION
while getopts ":fg:hs" _OPTION; do
case ${_OPTION} in
f) GEOMETRY="--fullscreen";;
g) if [[ "$OPTARG" =~ ^[0-9]+x[0-9]+\+[0-9]+\+[0-9]+$ ]]; then
GEOMETRY="--geometry $OPTARG"
else
echo "Wrong value for option -g. Option ignored" >&2
fi
;;
h) print_usage; exit;;
s) STANDALONE="--standalone";;
esac
done
}
function check_zim_depends(){
## Based on debian package system (dpkg + APT)
local _THE_PKG; local _MISSING=( )
## First depends: python2.X package (X>=6)
if test -z "`/usr/bin/dpkg --get-selections | awk '$1~/^python2.[6789][0-9.]*$/{print $1}'`"; then
test -x /usr/bin/apt-cache &&
_MISSING[${#_MISSING[@]}]=`/usr/bin/apt-cache --names-only search python 2>/dev/null | grep -E '^python2\.[6789][0-9.]*\s' | cut -d' ' -f1 | sort | tail -n1` ||
_MISSING[0]=python2.7
echo "Package ${_MISSING[0]} not installed!" >&2
fi
## Other depends
for _THE_PKG in python-gtk2 python-gobject python-xdg xdg-utils; do
if ! /usr/bin/dpkg -s ${_THE_PKG} 2>/dev/null | grep -Eiq 'Status:\sinstall\sok\sinstalled'; then
echo "Package ${_THE_PKG} not installed!" >&2
_MISSING[${#_MISSING[@]}]=${_THE_PKG}
fi
done
return ${#_MISSING[@]}
}
function is_running() { ## {path/to/script/or/prog} [/path/to/a/lock/file [drop]]
## Return code is 0 if process is running, 1 if not
## 1st stage validation: is file $1 source of a process running in memory?
test -n "`ps -ef | awk '$0!~"awk '$1'" && $0~"'$1'" {print $0}'`"
local _RETURNCODE=$?
## 2e stage validation (optional):
## If a lockfile name $2 provided and fist test ok, test presence of $2
if test -n "$2"; then
if test ${_RETURNCODE} -eq 0; then
test -f "$2" && _RETURNCODE=0 || _RETURNCODE=1
fi
## If the optional lockfile validation fails, it is not usefull either,
## So if $3 say drop, delete its remains
test ${_RETURNCODE} -eq 1 -a "$3" == "drop" && rm -f "$2" 2>/dev/null
fi
return ${_RETURNCODE}
}
function locate_python_bin() {
echo "Searching for python program..." >&2
local _PYTHON_BIN="`find /usr/bin -maxdepth 1 -type f -name python2* | sort | tail -n1`"
test -n "${_PYTHON_BIN}" && echo "${_PYTHON_BIN}" || return 9
}
function locate_zim_py() {
## Search zim.py into current dir
## (if more than one version, get only the more recent one)
echo "Searching for file zim.py..." >&2
local _ZIM_PATH="`find . -type f -name zim.py 2>/dev/null | sort -V | tail -n1`"
if test -z "${_ZIM_PATH}"; then
echo "Not found!" >&2; return 10
fi
if test ! -r "${_ZIM_PATH}"; then
if ! chmod +r "${_ZIM_PATH}" 2>/dev/null; then
echo "Unable to read: '${_ZIM_PATH}'" >&2; return 11
fi
fi
readlink -f "${_ZIM_PATH}"
}
function get_zim_version() { ## {path/to/python_elf} {path/to/zim_py}
test -x "$1" -a -f "$1" || return 1
test -f "$2" || return 2
"$1" "$2" -v 2>/dev/null | head -n1 | grep -Eo '[0-9.]+'
}
function locate_zim_desktop_windows_portable_config() { ## {second_chance_base_search_dir} [first_try_path]
echo "Searching for Zim Desktop Windows Portable config dir..." >&2
local _ZIM_CONFIG; local _BASE_DIR="$1"
if test -d "$2"; then
## Consider optionnal arg #2 is the good path
_ZIM_CONFIG="$2"
else
## Perform a deep search from the base directory given by arg #1
test -d "${_BASE_DIR}" || _BASE_DIR=.
_ZIM_CONFIG="`find "${_BASE_DIR}" -type d -regex "^.*/ZimDesktopWikiPortable/Data$" | head -n1`"
if test -z "${_ZIM_CONFIG}"; then
echo "Unable to find ${_BASE_DIR}/ZimDesktopWikiPortable/Data" >&2
return 1
fi
_ZIM_CONFIG="${_ZIM_CONFIG}/Config"
## Config dir missing if ZimDesktopWikiPortable never run yet:
if test ! -d "${_ZIM_CONFIG}"; then
if ! mkdir "${_ZIM_CONFIG}" 2>/dev/null; then
echo "Fail to create subdirectory:" >&2
echo "${_ZIM_CONFIG}" >&2
fi
fi
fi
readlink -f "${_ZIM_CONFIG}"
}
function prepare_files_to_context() { ## [-s] {linux|windows} {ZimDesktopPortableConfigDir}
local _ERROR=0
local _CONFIG_VER="0.4"
local _STANDALONE=false
local _OPTION
unset OPTIND
while getopts ":s" _OPTION; do
case ${_OPTION} in
s) _STANDALONE=true;;
esac
done
unset _OPTION
test $OPTIND -ne 1 && shift $(($OPTIND - 1))
test -d "$2" || return 1 && local _ZDPCD="$2" ## Zim Desktop Portable Config Dir
local _ZDPDD="`readlink -f "${_ZDPCD}/.."`" ## Zim Desktop Portable Data Dir
## 1. Create zim directory if not exists yet
if test ! -d "${_ZDPCD}/zim"; then
if ! mkdir "${_ZDPCD}/zim" 2>/dev/null; then
echo "Unable to create directory:" >&2
echo "${_ZDPCD}/zim" >&2
return 1
fi
fi
## 2. Create Notebooks dir if not exists yet
if test ! -d "${_ZDPDD}/Notebooks"; then
if ! mkdir "${_ZDPDD}/Notebooks" 2>/dev/null; then
echo "Unable to create directory:" >&2
echo "${_ZDPDD}/Notebooks" >&2
return 1
fi
fi
case $1 in
linux)
## Prepare the files notebooks.list and .conf to the portable linux context
echo "Converting config files to UNIX context..."
## notebooks.list
if test -f "${_ZDPCD}/zim/notebooks.list"; then
if file -b "${_ZDPCD}/zim/notebooks.list" | grep -q CRLF; then
## Convert file notebooks.list to conform linux context
## Backup existing notebooks.list because it looks like a windows version (CRLF)
if ! cp -f "${_ZDPCD}/zim/notebooks.list" "${_ZDPCD}/zim/notebooks.list.winbak" 2>/dev/null; then
echo "Fail to backup file notebooks.list" >&2
_ERROR=1
fi
## Convert relative paths of notebooks.list to absolute ones
## (Note: Zim 0.61+ can easily upgrade old style notebooks.list
## into a new one, so convert it in old style without numbered
## notebooks and path if it comes from zim<0.61)
sed -i "s:^\(Default\|uri\)=~:\1=file\://${_ZDPDD}:" "${_ZDPCD}/zim/notebooks.list" 2>/dev/null || _ERROR=3
sed -i "s:^\([0-9]\+=\)*~:file\://${_ZDPDD}:" "${_ZDPCD}/zim/notebooks.list" 2>/dev/null || _ERROR=3
## Convert new style notebook names to old style
sed -i "s:^\[Notebook\s\+[0-9]\+\]:[Notebook]:" "${_ZDPCD}/zim/notebooks.list" 2>/dev/null || _ERROR=3
fi
else
## Or create a brand new notebooks.list called Notes
## (Note: Zim 0.61+ can easily upgrade old style notebooks.list
## into a new one, so set it in old style without numbered
## notebooks and path if it comes from zim<0.61)
echo -en "[NotebookList]\nDefault=file://${_ZDPDD}/Notebooks/Notes\nfile://${_ZDPDD}/Notebooks/Notes\n\n[Notebook]\nuri=file://${_ZDPDD}/Notebooks/Notes\nname=Notes\ninterwiki=None\nicon=None\n\n" >"${_ZDPCD}/zim/notebooks.list" 2>/dev/null || _ERROR=3
## If not exists, create the file "${_ZDPDD}/Notebooks/Notes/notebook.zim"
if test ! -f "${_ZDPDD}/Notebooks/Notes/notebook.zim"; then
test -d "${_ZDPDD}/Notebooks/Notes" || mkdir "${_ZDPDD}/Notebooks/Notes" 2>/dev/null || _ERROR=3
echo -e "[Notebook]\nversion=${_CONFIG_VER}\nname=Notes\ninterwiki=\nhome=Notes\nicon=\ndocument_root=\nendofline=unix\nprofile=\n" >"${_ZDPDD}/Notebooks/Notes/notebook.zim" 2>/dev/null || _ERROR=3
fi
fi
## preferences.conf: if optional arg -s (standalone) is provided,
## be sure standalone mode is active: do not load trayicon plugin
if ${_STANDALONE} && test -f "${_ZDPCD}/zim/preferences.conf"; then
## Backup file preferences.conf and
if cp -f "${_ZDPCD}/zim/preferences.conf" "${_ZDPCD}/zim/preferences.conf.winbak" 2>/dev/null; then
sed -i '/^\[TrayIconPlugin\]/,/^\s*$/d' "${_ZDPCD}/zim/preferences.conf" 2>/dev/null
sed -i '/^\[General\]/,/^\s*$/s/"trayicon",\?//' "${_ZDPCD}/zim/preferences.conf" 2>/dev/null
sed -i '/^\[General\]/,/^\s*$/s/,\]/]/' "${_ZDPCD}/zim/preferences.conf" 2>/dev/null
fi
fi
## Convert all config files from CRLF to LF
if file -b "${_ZDPCD}/zim/notebooks.list" | grep -q CRLF; then
sed -i 's/\x0D$//' "${_ZDPCD}/zim/notebooks.list" 2>/dev/null || _ERROR=3
fi
find "${_ZDPCD}/zim" -type f -name '*.conf' | while read THE_FILE; do
file -b "$THE_FILE" | grep -q CRLF && sed -i 's/\x0D$//' "$THE_FILE" 2>/dev/null || _ERROR=3
done
## Files notebook.zim: scan notebook folders searching for notebook.zim,
## secure them against data-leak (shared=False + disable_trash=True) or
## create them if missing
sed '/^file:/s/^file:\/\///p;d' "${_ZDPCD}/zim/notebooks.list" | while read THE_PATH; do
if test -f "$THE_PATH"/notebook.zim; then
## Disable hard disk trash can and hard disk buffer
cp -f "$THE_PATH"/notebook.zim "$THE_PATH"/notebook.zim.linbak 2>/dev/null || _ERROR=3
sed -i '/^\(shared\|disable_trash\)=/d' "$THE_PATH"/notebook.zim 2>/dev/null || _ERROR=3
sed -i '/^\s*$/,$h;/^\s*$/d' "$THE_PATH"/notebook.zim 2>/dev/null || _ERROR=3
echo -e "shared=False\ndisable_trash=True\n" >>"$THE_PATH"/notebook.zim 2>/dev/null || _ERROR=3
else
## notebook.zim not exists: create it
echo -e "[Notebook]\nversion=${_CONFIG_VER}\nname=${THE_PATH##*/}\ninterwiki=\nhome=${THE_PATH##*/}\nicon=\ndocument_root=\nendofline=unix\nprofile=\nshared=False\ndisable_trash=True\n" >"$THE_PATH"/notebook.zim 2>/dev/null || _ERROR=3
fi
done
;;
windows)
## Prepare the files notebooks.list and .conf to the portable windows context
echo "Reverting config files to Microsoft Windows context..."
## Notebooks.list
if test -f "${_ZDPCD}/zim/notebooks.list"; then
if ! file -b "${_ZDPCD}/zim/notebooks.list" | grep -q CRLF; then
## 3.2.1.1. Convert file notebooks.list to conform windows context
## Backup existing notebooks.list because it looks like a linux version (not CRLF)
cp -f "${_ZDPCD}/zim/notebooks.list" "${_ZDPCD}/zim/notebooks.list.linbak" 2>/dev/null
## Convert Zim linux portable paths of notebooks back to Zim Windows Portable ones
local _RELPATH_FROM_HOME="`echo ${_ZDPDD} | sed "s:^$HOME:~:"`"
## (Note: Zim 0.61+ can easily upgrade old style notebooks.list
## into a new one, so convert it in old style without numbered
## notebooks and path for full compatibility)
## Convert relative linux paths of notebooks back to relative "windows" ones
sed -i "s:^\(Default\|uri\)=${_RELPATH_FROM_HOME}:\1=~:" "${_ZDPCD}/zim/notebooks.list" 2>/dev/null || _ERROR=4
sed -i "s:^\([0-9]\+=\)*${_RELPATH_FROM_HOME}:~:" "${_ZDPCD}/zim/notebooks.list" 2>/dev/null || _ERROR=4
## Convert absolute linux paths of notebooks back to relative "windows" ones
sed -i "s:^\(Default\|uri\)=\(file\://\)*${_ZDPDD}:\1=~:" "${_ZDPCD}/zim/notebooks.list" 2>/dev/null || _ERROR=4
sed -i "s:^\([0-9]\+=\)*\(file\://\)*${_ZDPDD}:~:" "${_ZDPCD}/zim/notebooks.list" 2>/dev/null || _ERROR=4
## Convert new style notebook names to old style
sed -i "s:^\[Notebook\s\+[0-9]\+\]:[Notebook]:" "${_ZDPCD}/zim/notebooks.list" 2>/dev/null || _ERROR=4
fi
else
## Or create file notebooks.list from previous Windows backup version
## (at that time, EOL must stay LF)
if test -f "${_ZDPCD}/zim/notebooks.list.winbak"; then
if ! sed 's/\x0D$//' "${_ZDPCD}/zim/notebooks.list.winbak" >"${_ZDPCD}/zim/notebooks.list" 2>/dev/null; then
echo "Fail to restore notebooks.list from file:" >&2
echo "${_ZDPCD}/zim/notebooks.list.winbak" >&2
_ERROR=5
fi
fi
fi
## If optional arg -s (standalone) is provided and
## if preferences.conf was previously backup (for standalone option), restore it
if ${_STANDALONE} && test -f "${_ZDPCD}/zim/preferences.conf.winbak"; then
test -f "${_ZDPCD}/zim/preferences.conf" &&
cp -f "${_ZDPCD}/zim/preferences.conf" "${_ZDPCD}/zim/preferences.conf.linbak" 2>/dev/null
mv -f "${_ZDPCD}/zim/preferences.conf.winbak" "${_ZDPCD}/zim/preferences.conf" 2>/dev/null
fi
if test -f "${_ZDPCD}/zim/notebooks.list"; then
## Files notebook.zim: scan notebook folders searching for notebook.zim.linbak
grep -E '^(file:|~)' "${_ZDPCD}/zim/notebooks.list" | sed -e 's/^file:\/\///' -e "s:^\([0-9]\+=\)*~:${_ZDPDD}:" | while read THE_PATH; do
if test -f "$THE_PATH"/notebook.zim.linbak; then
## Restore previous version to revert secured options (shared= + disable_trash=)
cp -f "$THE_PATH"/notebook.zim.linbak "$THE_PATH"/notebook.zim 2>/dev/null || _ERROR=4
else
## notebook.zim not exists: create it
echo -e "[Notebook]\nversion=${_CONFIG_VER}\nname=${THE_PATH##*/}\ninterwiki=\nhome=${THE_PATH##*/}\nicon=\ndocument_root=\nendofline=unix\nprofile=\nshared=False\ndisable_trash=True\n" >"$THE_PATH"/notebook.zim 2>/dev/null || _ERROR=4
fi
done
fi
## Convert all config files from LF to CRLF
if ! file -b "${_ZDPCD}/zim/notebooks.list" | grep -q CRLF; then
sed -i 's/$/\r/' "${_ZDPCD}/zim/notebooks.list" 2>/dev/null || _ERROR=4
fi
find "${_ZDPCD}/zim" -type f -name '*.conf' | while read THE_FILE; do
if ! file -b "$THE_FILE" | grep -q CRLF; then
sed -i 's/$/\r/' "$THE_FILE" 2>/dev/null || _ERROR=4
fi
done
;;
*) echo "Config files conversion type not defined" >&2
return 5
;;
esac
return ${_ERROR}
}
## _____ Main _____
echo $WELCOME
echo $FRIENDLY_TASK
echo
cd "$SCRIPTDIR"
## 1. Analyse command line options
get_cmd_options $@
test $OPTIND -ne 0 && shift $(($OPTIND - 1))
test -z "$GEOMETRY" && GEOMETRY="$GEOMETRY_DEFAULT"
test -n "$STANDALONE" && STANDALONE_SHORT="-s" || unset STANDALONE_SHORT
## 2. Be sure that all depends are installed
check_zim_depends || exit $?
## 3. Locate the python2 ELF
PYTHON_BIN="`locate_python_bin`" || exit 9
## 4. Locate zim.py file
ZIM_PY="`locate_zim_py`" || exit 10
ZIM_VERSION="`get_zim_version $PYTHON_BIN "$ZIM_PY"`" || exit 11
echo "Found: Zim Desktop v.$ZIM_VERSION"
## 5. Locate ZimDesktopWikiPortable/Data/Config and set XDG_CONFIG_HOME
## (Note: XDG_CONFIG_HOME should not be equal to $HOME/.config
## because we are not interesting by the personal notebook(s) of the user)
## The script is supposed to be in a subdir of the USB stick (for example sh/),
## and we're not sure Zim Desktop Windows Portable reside into it's native dir.
## So: performs a 2 chances search to find its config path
XDG_CONFIG_HOME="`locate_zim_desktop_windows_portable_config .. "../PortableApps/ZimDesktopWikiPortable/Data/Config"`" || exit 12
## 6. Check for safety operations...
## Problem:
## If another instance is run from "$ZIM_PY", maybe it is from that
## script, or maybe not (direct mouse clic on zim.py) and in that case
## other config files should be used.
## Before to continue, we must be sure that no other instance of zim
## and of that script will modify our portable config files (this could
## lead to ruin them).
## Unfortunately:
## 1. Zim closes config files as soon as read and we cannot lsof them
## 2. Instances of /usr/bin/python2.x is detached (PPID=1) when
## it is executed from Zim (File > Open Another Notebook)
## it is executed from a Windows manager (tested with Openbox)
## So, it's impossible to strickly check (even with a lockfile) and the
## only solution is to exclude any other instance of $ZIM_PY in memory:
if is_running "$ZIM_PY"; then
## Alert the user, and stop the script
echo
echo "Zim instance found in memory from the following zim program:"
echo "$ZIM_PY"
echo "Please, close that instance before running $SCRIPTNAME again."
exit 0
fi
## 7. Convert config files from CRLF to LF and prepare
## the files notebooks.list, notebook.zim and .conf to conform linux context
prepare_files_to_context $STANDALONE_SHORT linux "${XDG_CONFIG_HOME}" || exit 13
## 8. Launch Zim Desktop Linux portable
ZIM_STDERR=/tmp/${SCRIPTNAME// /}-${RANDOM}${RANDOM}
echo "Launching Zim Desktop Linux portable $ZIM_VERSION..."
echo "Do not close this terminal window while Zim is in memory"
echo
## Execute Zim with special environment
## Note: Zim 0.63 ignore option --geometry if no other gui option is provided (minor bug?)
env XDG_CONFIG_HOME="$XDG_CONFIG_HOME" $PYTHON_BIN "$ZIM_PY" --gui $STANDALONE $GEOMETRY 2>$ZIM_STDERR
## If standalone error (old versions of zim), try without that option
if test -n "$STANDALONE" -a -s $ZIM_STDERR && grep -qE "option\s+--standalone\s+not\s+recognized" $ZIM_STDERR; then
env XDG_CONFIG_HOME="$XDG_CONFIG_HOME" $PYTHON_BIN "$ZIM_PY" --gui $GEOMETRY 2>$ZIM_STDERR
fi
## Print zim stdout
if test -s $ZIM_STDERR; then
echo "Zim error messages:" >&2
cat $ZIM_STDERR >&2
fi
rm -f $ZIM_STDERR
## 9. Wait until all instances of zim has quit memory
## (New instances of zim (others notebooks) are detached from the parent
## (=the first instance). So, if ones closes the first instance,
## the parent script continues but other instances remains in memory)
while is_running "$ZIM_PY"; do
if test ${ECHOED:-0} -eq 0; then
echo
echo "Waiting for last instance of zim to end..."
ECHOED=1
fi
sleep 1
done
## 10. Reverse config files format converting LF to CRLF
## and preparing them to conform to Zim Desktop Windows Portable
echo
prepare_files_to_context $STANDALONE_SHORT windows "${XDG_CONFIG_HOME}"
ERROR=$?
case $ERROR in
0) echo "All things done.";;
4) echo "An error occurs in configuration files!"
echo "Please, check file notebooks.list before runing this prog again";;
esac
echo
exit $ERROR
Download that script
References
-
-
-
Documentation:
Development:
-
PortableApps plateform (compatible with
Wine):
-
Official portableApps Zim self-extract
ZIP programs (ZimDesktopWikiPortable_X.Y.paf.exe) are provided by the
official site port