89 Commits

Author SHA1 Message Date
6dbd254992 add grep aliases, please shellcheck 2024-01-21 21:18:49 +01:00
ee68ec34c2 add base.sh 2024-01-21 21:16:09 +01:00
e8a218f7cb merge lorien/eowyn 2024-01-20 20:38:57 +01:00
63388aaa7a rename dot-files to non-dot, move pdf viewer to .bashrc.br 2024-01-20 19:52:36 +01:00
11b7cf40eb Add .bash_profile to avoid dup $HOME/bin in PATH(stupid Debian) 2024-01-20 19:12:04 +01:00
9801503eab Merge branch 'master' of github.com:braoult/Tools 2024-01-20 18:06:14 +01:00
de35fe60e7 typos 2024-01-20 15:25:12 +01:00
85e0ca1af5 add eowyn bashrc, add syncdir() 2024-01-20 15:19:05 +01:00
3d7f388fb8 vardir(): add '+' as default script to source 2023-12-29 18:46:45 +01:00
1aef3f1196 lorien.el: adjust for brlib 2023-12-29 18:45:50 +01:00
3da006ed73 emacs: Fix modeline VC symbols, update lorien.el opened files 2023-12-27 08:20:40 +01:00
6f58b0bddc replace multiple prj aliases with functions (and helper function) 2023-12-27 07:32:45 +01:00
608d80b3d8 add share/sync/COPYING 2023-12-27 07:19:49 +01:00
fc28134385 remove brlib 2023-12-27 06:45:53 +01:00
6ccb5f2264 cleanup 2023-12-24 17:38:53 +01:00
fb203bbcae move debug.h & pool.h main() to test dir (unmodified, can't compile) 2023-12-24 17:37:27 +01:00
4fe839df19 brlib Makefile 2023-12-22 11:26:32 +01:00
3f00c79d45 revert lost changes 2023-12-22 11:24:13 +01:00
0461fc185e Merge branch 'sep-brlib' of git.raoult.com:bruno/Tools into sep-brlib 2023-12-22 11:16:24 +01:00
013f5bf943 make brlib sources to new dir 2023-12-22 11:14:16 +01:00
b62f67d2c7 mkve brlib sources to new dir 2023-12-22 10:57:56 +01:00
e7b5d2ea4d debug.c: remove dependancies from bits.h 2023-12-22 10:37:49 +01:00
7d28c85bc6 debug.c: remove dependancies from bits.h 2023-12-22 10:32:22 +01:00
64a5b20ca5 bashrc.br: replace ls '-a' with '-A' in aliases, add fdiff function 2023-12-22 10:21:49 +01:00
0adb410321 bits.[ch]: remove logs in macros (moved to bits.c) 2023-12-16 17:05:07 +01:00
b76a8603a1 debug.[ch]: remove dependancies from bits.h 2023-12-16 16:55:29 +01:00
3b2062798b .bashrc: document _var_XXX funcs 2023-12-16 16:48:43 +01:00
107e3d045b comments 2023-12-16 11:50:15 +01:00
1ccef7e908 generic PATH-like functions (del/append...) + remove snap in PATH 2023-12-15 21:45:02 +01:00
0d1b271dba sync.sh: Test if nothing to do 2023-12-10 19:24:19 +01:00
5b01e92806 bashrc: add rehash() 2023-12-10 19:23:40 +01:00
f8a98f3c9a add rehash alias 2023-08-26 13:10:14 +02:00
49a8b7294f move emacs scripts to .emacs.d 2023-08-26 13:07:06 +02:00
6feb928205 add bash and emacs init files 2023-07-11 13:16:29 +02:00
e65ef9889e Fix invalid fonction definition with unnamed param 2023-06-22 15:17:56 +02:00
1084c9eb06 move typedefs alltogether 2023-06-22 15:17:32 +02:00
485e04c6fd add struct-group.h 2023-06-20 21:31:44 +02:00
5294dbe371 updates from changes in AoC 2022 2023-06-20 21:27:49 +02:00
15cc0e54e9 add pjwhash 2023-06-20 21:27:00 +02:00
3fe7315f7c more fixes for 32 bits architecture 2022-12-07 08:23:09 +01:00
7be875ac70 typo 2022-12-06 14:32:10 +01:00
bde6db19cc pool.c * fix size_t printf format 2022-12-06 14:22:38 +01:00
53dc36cdd6 bits.h: move popcount up, fix erroneous japanese '−' instead of '-' (minus) 2022-12-06 14:21:11 +01:00
0b0e344d6a cleanup 2022-12-03 16:12:42 +01:00
6025e338b5 rename hash func 2022-12-03 16:12:19 +01:00
e4dc90cbb7 add stringification macros 2022-12-03 16:11:34 +01:00
dd94f888ae add some reverse() macros 2022-12-03 16:10:59 +01:00
11fbf0866f update (C) date 2022-12-03 16:10:37 +01:00
8f86bf9ccc please valgrind 2022-12-03 16:10:08 +01:00
c01fa51403 add some macros: ilog2, is_power_of_2, bits_per, etc... 2022-12-03 16:07:32 +01:00
de251f60d0 rename hash func 2022-12-03 16:06:30 +01:00
54085fa351 include (untested) hash 2022-09-25 19:31:11 +02:00
e858f69167 add hash, plist, etc (untested) 2022-09-18 13:12:49 +02:00
20bfd915e6 simplify get_credentials(): use read instead of readarray 2022-09-06 21:24:29 +02:00
5235a3382e sms-free.sh: add error message if login not found in keyfile 2022-09-06 18:16:52 +02:00
876965ce96 sms-free.sh: tool to send SMS to Free Mobile 2022-09-06 17:03:53 +02:00
fb7ca621a5 gen-password: add option to avoid similar chars, dict list etc...
- new options:
  -l, --list-dictionaries: list dictionaries
  -n, --no-similar-chars:  avoid similar chars (0O/1l etc..)
- remove dead code
2022-09-06 16:52:13 +02:00
63f428047d add gen-password, links 2022-08-18 08:15:45 +02:00
c2620a995d testing '/'in headings 2022-08-17 20:53:33 +02:00
1c775f4a35 c: minor comment changes 2022-08-13 15:10:51 +02:00
99861c22a9 dictionaries: fix typos in README, add short names links 2022-08-12 20:49:52 +02:00
e356f63bed add english/french dictionaries 2022-08-12 20:41:54 +02:00
66fb96a7c8 gen-password: fix unsanitized strings for yad 2022-08-10 22:08:29 +02:00
32032d956f gen-password: various improvements for string and dictionary
strings password:
  - add shuffle
  - add European characters
  - fix hiragana support
  - add mandatory characters in generated password

dictionary:
  - check for dictionary file in  different directories
2022-08-10 21:13:01 +02:00
b5edeb2a8d sync-view: move logs earlier, do not check for ROOTDIR directory 2022-08-10 11:49:15 +02:00
b710a16037 gen-password.sh: Add string-type passwords 2022-08-09 16:17:59 +02:00
1c63e1836d gen-passwd.sh: add GUI, small fixes 2022-08-08 22:28:44 +02:00
818cb2a0fd gen-password.sh: 1st version. Includes passphrases, pincodes, mac addr. 2022-08-07 22:41:51 +02:00
530b0217a8 sync-view.sh: move comment to right place 2022-08-04 09:27:26 +02:00
03fc4a798b sync.sh: Fix LOCKDIR (was cgommon since .syncrc usage) 2022-07-30 11:53:44 +02:00
886d634799 sync.sh/sync-view.sh: Add .syncrc in backup dir as defaut config file 2022-07-29 22:48:46 +02:00
1079076fbe typo on RESOLVETARGET test after changing to empty=do not resolve 2022-07-22 19:05:00 +02:00
db48925066 add file type, option -a to find file from different machine. 2022-07-22 11:53:11 +02:00
d7a06202f9 typo in man EXAMPLES section 2022-07-13 15:48:24 +02:00
0b6563fe23 sync-view.sh: fix TARGETDIR variable name, missing '/' when ROOTDIR=/ 2022-07-13 15:29:53 +02:00
d8dde368d2 sync-view.sh: first version 2022-07-13 14:29:21 +02:00
c9387f3231 fix .gitignore 2022-07-13 14:27:55 +02:00
da6f2bacea man change 2022-07-11 17:05:13 +02:00
63a289b814 sync-conf-example.sh: dump also mysql/mariadb users and permissions 2022-07-02 10:43:42 +02:00
449929d7a7 add LC_ALL to avoid localized messages 2022-06-24 14:12:32 +02:00
228eba1133 suppression log 2022-06-24 02:06:14 +02:00
05e7ecb5e1 replace all "function foo" with "foo()" 2022-06-23 19:20:47 +02:00
54e9308268 Use only labels for partition matching 2022-06-23 19:13:07 +02:00
5ef543f760 change shebang line 2022-06-23 16:23:38 +02:00
7a8b6cdc73 dup-live-disk: RSYNCOPTS is now array, ignore exit 24 for rsync 2022-06-20 13:08:29 +02:00
f4f6bd06d1 C tools: move includes to subdir, Makefile 2022-06-06 16:45:07 +02:00
9d71467182 rwonce.h: __error__ attribute fix 2022-06-01 17:55:48 +02:00
d6eccb2caa bits.h: add 32 bits macros 2022-06-01 17:54:01 +02:00
97aea8ddf2 sync.sh: some shellcheck fixes 2022-06-01 17:46:57 +02:00
41 changed files with 25799 additions and 1861 deletions

8
.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
.ccls*
/*.c
/*.sh
/COPYING.*
/license-*
/LICENSE.*
/test/
/todo/

View File

@@ -1,8 +1,11 @@
### Some GNU/Linux tools, for fun... ### Some personal GNU/Linux tools.
#### bash #### bash
- **trans.sh**: a [linguee.com](https://linguee.com) based command-line translator. - **trans.sh**: a [linguee.com](https://linguee.com) based command-line translator.
- **sync.sh**: a rsync/ssh backup tool. - **sync.sh**: a rsync/ssh backup tool.
- **sync-view.sh**: view ~sync.sh~ file backups versions.
- **sync-conf-example.sh**: configuration example. - **sync-conf-example.sh**: configuration example.
- **dup-live-disk.sh**: duplicate (possibly live) disk partitions. - **dup-live-disk.sh**: duplicate (**possibly live**) disk partitions.
- **gen-password.sh**: a password generator.
- **base.sh**: bases 2/8/10/16 conversions.

10
bash/README.md Normal file
View File

@@ -0,0 +1,10 @@
### Some bash scripts, that I needed at some time...
- [**trans.sh**](trans.sh): a [linguee.com](https://linguee.com) based command-line translator.
- [**sync.sh**](sync.sh): a rsync/ssh backup tool.
- [**sync-conf-example.sh**](share/sync/sync-conf-example.sh): configuration example.
- [**sync-view.sh**](sync-view.sh): view `sync.sh` file backups versions.
- [**dup-live-disk.sh**](dup-live-disk.sh): duplicate (possibly live) disk partitions.
- [**gen-password.sh**](gen-password.sh): a password generator
- [**share/gen-password**](share/gen-password): [diceware](https://en.wikipedia.org/wiki/Diceware)-like word lists.
- [**base**](base.sh): bases 2/8/10/16 conversions.

283
bash/base.sh Executable file
View File

@@ -0,0 +1,283 @@
#!/usr/bin/env bash
#
# base.sh - convert decimal numbers from/to base 2, 8, 10 and 16.
#
# (C) Bruno Raoult ("br"), 2024
# Licensed under the GNU General Public License v3.0 or later.
# Some rights reserved. See COPYING.
#
# You should have received a copy of the GNU General Public License along with this
# program. If not, see <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
#
# SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
#
CMDNAME=${0##*/} # script name
usage() {
printf "usage: %s [OPTIONS] [NUMBER]...\n" "$CMDNAME"
printf "Use '%s -h' for more help\n" "$CMDNAME"
}
help() {
cat << _EOF
usage: $CMDNAME [OPTIONS] [NUMBER]...
-f, --from=BASE input base. Default is "g"
-t, --to=BASE output base. Default is "a"
-2, -8, -d, -x equivalent to -t2, -t8, -t10, -t16"
-g, --group=[SEP] group output (see OUTPUT below)
-0, --padding Not implemented. 0-pad output on block boundary (implies -g)
-n, --noprefix Remove base prefixes in output
-h, --help this help
$CMDNAME output the NUMBERS arguments in different bases. If no NUMBER is
given, standard input will be used.
BASE
2, b, B binary
8, o, O, 0 octal
10, d, D decimal
16, h, H, 0x hexadecimal
a, g all/any: Default, guess format for '-f', output all
bases for '-t'
INPUT NUMBER
If input base is not specified, some prefixes are supported.
'b' or '2/' for binary, '0', 'o' or '8/' for octal, '0x', 'x' or
'16/' for hexadecimal, and 'd' or '10/' for decimal.
If no prefix, decimal is assumed.
OUTPUT
By default, output is the input number converted in the 4 supported
bases (16, 10, 8, 2, in this order, separated by one tab character.
Without '-n' option, all output numbers but decimal will be prefixed:
'2#' for binary, '0' for octal, '0x' for hexadecimal, making them
usable for input in some otilities such as bash(1).]
With '-g' option, number digits will be grouped by 3 (octal,
decimal), or 4 (binary, hexadecimal)\n. If no SEP character is given,
the separator will be ',' (comma) for decimal, space otherwise.
This option may be useless if default output, with multiple numbers
on one line.
The '-0' option will left pad with '0' (zeros) to a group boundary.
EXAMPLES
$ $CMDNAME 123456
2#11110001001000000 0361100 123456 0x1e240
$ $CMDNAME -n 123456
11110001001000000 361100 123456 1e240
$ $CMDNAME -ng2 012345
1 0100 1110 0101
$ $CMDNAME -n2 012345
1 0100 1110 0101
_EOF
}
# some default values (blocks separator padchar)
declare -i ibase=0 obase=0 padding=0 noprefix=0 ogroup=0
declare -rA _bases=(
[2]=2 [b]=2 [B]=2
[8]=8 [o]=8 [O]=8 [0]=8
[10]=10 [d]=10 [D]=10
[16]=16 [h]=16 [H]=16 [0x]=16
[a]=-1 [g]=-1
)
declare -A _pad=(
[2]=" " [8]=" " [10]="," [16]=" "
)
declare -rA _ogroup=(
[2]=4 [8]=3 [10]=3 [16]=4
)
declare -rA _oprefix=(
[2]="2#" [8]="0" [10]="" [16]="0x"
)
zero_pad() {
local base="$1" str="$2"
local str="$1"
local -i n=${_ogroup[$base]}
#printf "str=$str #=${#str}" >&2
while (( ${#str} < $2 )); do
str="0$str"
done
printf "%s" "$str"
}
split() {
local base="$1" str="$2"
local res="$str" sep=${_pad[$base]}
local -i n=${_ogroup[$base]}
if (( ogroup )); then
res=""
while (( ${#str} )); do
if (( ${#str} < n )); then
str=$(zero_pad "$str" $n)
fi
res="${str: -$n}${res:+$sep$res}"
str="${str:0:-$n}"
done
fi
printf "%s" "$res"
}
bin() {
local n bits=""
for (( n = $1 ; n > 0 ; n >>= 1 )); do
bits=$((n&1))$bits
done
printf "%s\n" "${bits-0}"
}
hex() {
printf "%lx" "$1"
}
dec() {
printf "%lu" "$1"
}
oct() {
printf "%lo" "$1"
}
declare -a args=()
parse_opts() {
# short and long options
local sopts="f:t:28dxg::pnh"
local lopts="from:,to:,group::,padding,noprefix,help"
# set by options
local tmp=""
if ! tmp=$(getopt -o "$sopts" -l "$lopts" -n "$CMDNAME" -- "$@"); then
usage
exit 1
fi
eval set -- "$tmp"
while true; do
case "$1" in
"-f"|"--from")
ibase=${_bases[$2]}
if (( ! ibase )); then
usage
exit 1
fi
shift
;;
"-t"|"--to")
obase=${_bases[$2]}
if (( ! obase )); then
usage
exit 1
fi
shift
;;
"-2") obase=2 ;;
"-8") obase=8 ;;
"-d") obase=10 ;;
"-x") obase=16 ;;
"-g"|"--group")
ogroup=1
if [[ -n "$2" ]]; then
for i in 2 8 10 16; do _pad["$i"]="$2"; done
fi
shift
;;
"-p"|"--padding") padding=1 ;;
"-n"|"--noprefix") noprefix=1 ;;
"-h"|"--help") help ; exit 0 ;;
"--") shift; break ;;
*) usage; echo "Internal error [$1]!" >&2; exit 1 ;;
esac
shift
done
# parse remaining arguments
if (($# > 0)); then # type
args=("$@")
fi
}
# shellcheck disable=SC2317
addprefix() {
local base="$1" number="$2"
local prefix=""
(( noprefix )) || prefix="${_oprefix[$base]}"
printf "%s%s" "$prefix" "$number"
}
stripprefix() {
local number="$1"
number=${number#0x}
number=${number#[bodx0]}
number=${number#0}
number=${number#*/}
printf "%s" "$number"
}
guessbase() {
local input="$1"
local -i base=0
if [[ $input =~ ^b || $input =~ ^2/ ]]; then
base=2
elif [[ $input =~ ^0x || $input =~ ^x || $input =~ ^16/ ]]; then
base=16
elif [[ $input =~ ^0 || $input =~ ^o || $input =~ ^8/ ]]; then
base=8
elif [[ $input =~ ^d || $input =~ ^10/ ]]; then
base=10
fi
return $(( base ? base : 10 ))
}
doit() {
local number="$2" multi="" val inum
local -i base=$1 decval _obase=$obase
if (( base <= 0 )); then
guessbase "$number"
base=$?
fi
inum=$(stripprefix "$number")
(( decval = "$base#$inum" )) # input value in decimal
# mask for desired output: 1=decimal, others are same as base
if (( ! _obase )); then
(( _obase = 1|2|8|16 ))
multi=$'\t'
fi
if (( _obase & 16 )); then
val=$(addprefix 16 "$(split 16 "$(hex $decval)")")
printf "%s%s" "$val" "$multi"
fi
if (( _obase & 1 )); then
val=$(addprefix 10 "$(split 10 "$(dec $decval)")")
printf "%s%s" "$val" "$multi"
fi
if (( _obase & 8 )); then
val=$(addprefix 8 "$(split 8 "$(oct $decval)")")
printf "%s%s" "$val" "$multi"
fi
if (( _obase & 2 )); then
val=$(addprefix 2 "$(split 2 "$(bin $decval)")")
printf "%s%s" "$val" "$multi"
fi
printf "\n"
}
parse_opts "$@"
if ! (( ${#args[@]} )); then
while read -ra line; do
for input in "${line[@]}"; do
doit "ibase" "$input"
done
done
else
for input in "${args[@]}"; do
doit "$ibase" "$input"
done
fi
exit 0

View File

@@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# dup-live-disk.sh - duplicate (possibly live) system partitions # dup-live-disk.sh - duplicate (possibly live) system partitions
# #
@@ -147,16 +147,17 @@
# command line # command line
SCRIPT="$0" SCRIPT="$0"
CMD="${0##*/}" CMD="${0##*/}"
export LC_ALL=C
# valid filesystems # valid filesystems
# shellcheck disable=2034 # shellcheck disable=2034
VALIDFS=(ext3 ext4 btrfs vfat reiserfs xfs zfs) VALIDFS=(ext3 ext4 btrfs vfat reiserfs xfs zfs)
function man { man() {
sed -n '/^#%MAN_BEGIN%/,/^#%MAN_END%$/{//!s/^#[ ]\{0,1\}//p}' "$SCRIPT" | more sed -n '/^#%MAN_BEGIN%/,/^#%MAN_END%$/{//!s/^#[ ]\{0,1\}//p}' "$SCRIPT" | more
} }
function usage { usage() {
cat <<_EOF cat <<_EOF
Usage: $CMD [OPTIONS] [SRC] DST Usage: $CMD [OPTIONS] [SRC] DST
Duplicate SRC (or live system) disk partitions to DST disk partitions. Duplicate SRC (or live system) disk partitions to DST disk partitions.
@@ -190,7 +191,7 @@ _EOF
# -l, -s: long, or short prefix (default: none). Last one is used. # -l, -s: long, or short prefix (default: none). Last one is used.
# -t: timestamp # -t: timestamp
# -n: no newline # -n: no newline
function log { log() {
local timestr="" prefix="" opt=y newline=y local timestr="" prefix="" opt=y newline=y
while [[ $opt = y ]]; do while [[ $opt = y ]]; do
case $1 in case $1 in
@@ -211,7 +212,7 @@ function log {
} }
# prints out and run (maybe) a command. # prints out and run (maybe) a command.
function echorun_maybe { echorun_maybe() {
if [[ "$DRYRUN" == 'yes' ]]; then if [[ "$DRYRUN" == 'yes' ]]; then
log "dry-run: %s" "$*" log "dry-run: %s" "$*"
else else
@@ -246,7 +247,7 @@ yesno() {
} }
# mariadb start/stop # mariadb start/stop
function mariadb_maybe_stop { mariadb_maybe_stop() {
[[ $MARIADBSTOPPED == yes ]] && return 0 [[ $MARIADBSTOPPED == yes ]] && return 0
if systemctl is-active --quiet mysql; then if systemctl is-active --quiet mysql; then
if [[ $MARIADB == ask ]]; then if [[ $MARIADB == ask ]]; then
@@ -268,7 +269,7 @@ function mariadb_maybe_stop {
fi fi
} }
function mariadb_maybe_start { mariadb_maybe_start() {
if [[ $MARIADB == yes && $MARIADBSTOPPED == yes ]]; then if [[ $MARIADB == yes && $MARIADBSTOPPED == yes ]]; then
#log -n "restarting mariadb/mysql... " #log -n "restarting mariadb/mysql... "
echorun_maybe systemctl start mariadb echorun_maybe systemctl start mariadb
@@ -277,14 +278,14 @@ function mariadb_maybe_start {
fi fi
} }
function error_handler { error_handler() {
local ERROR=$2 local ERROR=$2
log "FATAL: Error line $1, exit code $2. Aborting." log "FATAL: Error line $1, exit code $2. Aborting."
exit "$ERROR" exit "$ERROR"
} }
trap 'error_handler $LINENO $?' ERR SIGHUP SIGINT SIGTERM trap 'error_handler $LINENO $?' ERR SIGHUP SIGINT SIGTERM
function exit_handler { exit_handler() {
local mnt local mnt
# log "exit handler (at line $1)" # log "exit handler (at line $1)"
@@ -302,7 +303,11 @@ function exit_handler {
} }
trap 'exit_handler $LINENO' EXIT trap 'exit_handler $LINENO' EXIT
function check_block_device { # check_block_device - check a file system device
# $1: device description
# $2: more ('w' for writable)
# $3: device
check_block_device() {
local devtype="$1" local devtype="$1"
local mode="$2" local mode="$2"
local dev="$3" local dev="$3"
@@ -323,7 +328,7 @@ function check_block_device {
} }
# check that /etc/fstab.DESTLABEL exists in SRC disk. # check that /etc/fstab.DESTLABEL exists in SRC disk.
function check_fstab { check_fstab() {
local etc="${AUTOFS_DIR}/$SRCROOTLABEL/etc" local etc="${AUTOFS_DIR}/$SRCROOTLABEL/etc"
local fstab="fstab.$DSTROOTLABEL" local fstab="fstab.$DSTROOTLABEL"
#if [[ "$FSTAB" != no ]]; then #if [[ "$FSTAB" != no ]]; then
@@ -336,7 +341,7 @@ function check_fstab {
return 0 return 0
} }
function fix_fstab { fix_fstab() {
local fstab="${AUTOFS_DIR}/$DSTROOTLABEL/etc/fstab" local fstab="${AUTOFS_DIR}/$DSTROOTLABEL/etc/fstab"
#[[ ! -f "$fstab" ]] && log "Warning: DST fstab will be wrong !" && FSTAB=no #[[ ! -f "$fstab" ]] && log "Warning: DST fstab will be wrong !" && FSTAB=no
@@ -352,7 +357,7 @@ function fix_fstab {
} }
# check if $1 is in array $2 ($2 is by reference) # check if $1 is in array $2 ($2 is by reference)
function in_array { in_array() {
local elt=$1 i local elt=$1 i
local -n arr=$2 local -n arr=$2
for ((i=0; i<${#arr[@]}; ++i)); do for ((i=0; i<${#arr[@]}; ++i)); do
@@ -365,7 +370,7 @@ function in_array {
SRC="" SRC=""
DST="" DST=""
SRCROOT="" SRCROOT=""
ROOTPARTNUM="" SRCROOTPARTNUM=""
AUTOFS_DIR=/mnt AUTOFS_DIR=/mnt
DRYRUN=no # dry-run DRYRUN=no # dry-run
@@ -454,9 +459,9 @@ while true; do
shift shift
;; ;;
'-r'|'--root') '-r'|'--root')
ROOTPARTNUM="$2" SRCROOTPARTNUM="$2"
if ! [[ "$ROOTPARTNUM" =~ ^[[:digit:]]+$ ]]; then if ! [[ "$SRCROOTPARTNUM" =~ ^[[:digit:]]+$ ]]; then
log "$CMD: $ROOTPARTNUM must be a partition number." log "$CMD: $SRCROOTPARTNUM must be a partition number."
exit 1 exit 1
fi fi
shift shift
@@ -485,25 +490,25 @@ fi
case "$#" in case "$#" in
1) 1)
if [[ -n "$ROOTPARTNUM" ]]; then if [[ -n "$SRCROOTPARTNUM" ]]; then
log "$CMD: cannot have --root option for live system." log "$CMD: cannot have --root option for live system."
log "Use '$CMD --help' or '$CMD --man' for help." log "Use '$CMD --help' or '$CMD --man' for help."
exit 1 exit 1
fi fi
# guess root partition disk name # guess root partition disk name
SRCROOT=$(findmnt -no SOURCE -M /) SRCROOT=$(findmnt -no SOURCE -M /)
ROOTPARTNUM=${SRCROOT: -1} SRCROOTPARTNUM=${SRCROOT: -1}
SRC="/dev/"$(lsblk -no pkname "$SRCROOT") SRC="/dev/"$(lsblk -no pkname "$SRCROOT")
DST="/dev/$1" DST="/dev/$1"
;; ;;
2) 2)
if [[ -z "$ROOTPARTNUM" ]]; then if [[ -z "$SRCROOTPARTNUM" ]]; then
log "$CMD: missing --root option for non live system." log "$CMD: missing --root option for non live system."
log "Use '$CMD --help' or '$CMD --man' for help." log "Use '$CMD --help' or '$CMD --man' for help."
exit 1 exit 1
fi fi
SRC="/dev/$1" SRC="/dev/$1"
SRCROOT="$SRC$ROOTPARTNUM" SRCROOT="$SRC$SRCROOTPARTNUM"
DST="/dev/$2" DST="/dev/$2"
;; ;;
0) 0)
@@ -527,7 +532,8 @@ check_block_device "destination disk" w "$DST"
check_block_device "source root partition" r "$SRCROOT" check_block_device "source root partition" r "$SRCROOT"
SRCROOTLABEL=$(lsblk -no label "$SRCROOT") SRCROOTLABEL=$(lsblk -no label "$SRCROOT")
ROOTLABEL=${SRCROOTLABEL:0:-1} # strip out last character
ROOTLABEL=${SRCROOTLABEL%%?}
# find out all partitions labels on SRC disk... # find out all partitions labels on SRC disk...
# shellcheck disable=SC2207 # shellcheck disable=SC2207
@@ -550,16 +556,21 @@ for ((i=0; i<${#SRCLABELS[@]}; ++i)); do
unset TMP TMPDEV TMPFS unset TMP TMPDEV TMPFS
done done
DSTROOT="$DST$ROOTPARTNUM" # find out DST root partition
check_block_device "destination root partition" w "$DSTROOT" # shellcheck disable=SC2207
DSTROOTLABEL=$(lsblk -no label "$DSTROOT") declare -a TMP_DSTLABELS=($(lsblk -lno LABEL "$DST"))
DSTCHAR=${DSTROOTLABEL: -1}
# check DSTROOTLABEL is compatible with ROOTLABEL for maybe_root in "${TMP_DSTLABELS[@]}"; do
if [[ "$DSTROOTLABEL" != "$ROOTLABEL$DSTCHAR" ]]; then log "rootlabel=%s maybe=%s" "$ROOTLABEL" "$maybe_root"
log "%s: Fatal: %s != %s%s." "$CMD" "$DSTROOTLABEL" "$ROOTLABEL" "$DSTCHAR" if [[ $maybe_root =~ ^${ROOTLABEL}.$ ]]; then
exit 1 log "Found destination root label: $maybe_root"
fi DSTROOTLABEL=$maybe_root
DSTCHAR=${DSTROOTLABEL: -1}
DSTROOT=$(findfs LABEL="$DSTROOTLABEL")
check_block_device "destination root partition" w "$DSTROOT"
break
fi
done
declare -a DSTLABELS DSTDEVS DSTFS DST_VALID_FS declare -a DSTLABELS DSTDEVS DSTFS DST_VALID_FS
# Do the same for corresponding DST partitions labels, device, and fstype # Do the same for corresponding DST partitions labels, device, and fstype
@@ -586,26 +597,29 @@ for ((i=0; i<${#LABELS[@]}; ++i)); do
unset TMP TMPDEV TMPFS unset TMP TMPDEV TMPFS
done done
for ((i=0; i<${#LABELS[@]}; ++i)); do {
log -n "%s %s " "${SRCDEVS[$i]}" "${DSTDEVS[$i]}" printf "DEV1 DEV2 LABEL1 LABEL2 FS1 FS2 SVALID\? DVALID\? ROOT\n"
log -n "%s %s " "${SRCLABELS[$i]}" "${DSTLABELS[$i]}" for ((i=0; i<${#LABELS[@]}; ++i)); do
log -n "%s %s " "${SRCFS[$i]}" "${DSTFS[$i]}" log -n "%s %s " "${SRCDEVS[$i]}" "${DSTDEVS[$i]}"
log -n "%s %s " "${SRC_VALID_FS[$i]}" "${DST_VALID_FS[$i]}" log -n "%s %s " "${SRCLABELS[$i]}" "${DSTLABELS[$i]}"
[[ "$DSTROOTLABEL" == "${DSTLABELS[$i]}" ]] && log "*" log -n "%s %s " "${SRCFS[$i]}" "${DSTFS[$i]}"
echo log -n "%s %s " "${SRC_VALID_FS[$i]}" "${DST_VALID_FS[$i]}"
done | column -N DEV1,DEV2,LABEL1,LABEL2,FS1,FS2,SVALID\?,DVALID\?,ROOT -t -o " | " [[ "$DSTROOTLABEL" == "${DSTLABELS[$i]}" ]] && log "*"
echo
done
} | column -t
check_fstab || exit 1 check_fstab || exit 1
RSYNCOPTS="-axH --delete --delete-excluded"
FILTER=--filter="dir-merge .rsync-disk-copy" FILTER=--filter="dir-merge .rsync-disk-copy"
declare -a RSYNCOPTS=(-axH "$FILTER" --delete --delete-excluded)
# copy loop # copy loop
for ((i=0; i<${#LABELS[@]}; ++i)); do for ((i=0; i<${#LABELS[@]}; ++i)); do
if [[ "${SRC_VALID_FS[$i]}" != y ]] || [[ "${DST_VALID_FS[$i]}" != y ]]; then if [[ "${SRC_VALID_FS[$i]}" != y ]] || [[ "${DST_VALID_FS[$i]}" != y ]]; then
log "skipping label %s" "${LABELS[$i]}" log "skipping label %s" "${LABELS[$i]}"
continue continue
fi fi
SRCPART="${AUTOFS_DIR}/${SRCLABELS[$i]}/" SRCPART="$AUTOFS_DIR/${SRCLABELS[$i]}/"
DSTPART="$AUTOFS_DIR/${DSTLABELS[$i]}" DSTPART="$AUTOFS_DIR/${DSTLABELS[$i]}"
#log -n "%s -> %s : " "$SRCPART" "$DSTPART" #log -n "%s -> %s : " "$SRCPART" "$DSTPART"
@@ -616,8 +630,14 @@ for ((i=0; i<${#LABELS[@]}; ++i)); do
fi fi
if [[ "$copy" == yes ]]; then if [[ "$copy" == yes ]]; then
mariadb_maybe_stop mariadb_maybe_stop
status=0
# shellcheck disable=SC2086 # shellcheck disable=SC2086
echorun_maybe rsync "$FILTER" ${RSYNCOPTS} "$SRCPART" "$DSTPART" echorun_maybe rsync "${RSYNCOPTS[@]}" "$SRCPART" "$DSTPART" || status=$?
if (( status != 24 && status != 0 )); then
log -s "rsync error %d" "$status"
exit 1
fi
if [[ "$DSTROOTLABEL" == "${DSTLABELS[$i]}" ]]; then if [[ "$DSTROOTLABEL" == "${DSTLABELS[$i]}" ]]; then
ROOTCOPIED=yes ROOTCOPIED=yes
fix_fstab fix_fstab

2
bash/free-sms-keys.txt Normal file
View File

@@ -0,0 +1,2 @@
bruno:01234567:abcdeABCDE1234
bodiccea:76543210:xyztXYZT123456

738
bash/gen-password.sh Executable file
View File

@@ -0,0 +1,738 @@
#!/usr/bin/env bash
#
# gen-passwd.sh - a simple password generator.
#
# (C) Bruno Raoult ("br"), 2022
# Licensed under the GNU General Public License v3.0 or later.
# Some rights reserved. See COPYING.
#
# You should have received a copy of the GNU General Public License along with this
# program. If not, see <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
#
# SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
#
#%MAN_BEGIN%
# NAME
# gen-passwwd.sh - a simple password generator.
#
# SYNOPSIS
# gen-passwd.sh [OPTIONS] TYPE [LENGTH]
#
# DESCRIPTION
# Generate a random TYPE password with length LENGTH.
# Available types are :
# dice
# A list of digits in range [1-6]. Default length is 5. The purpose of
# this is only to help choosing a word in a diceware word list.
# mac
# A "xx-xx-xx-xx-xx-xx" type address, where 'x' are hexadecimal digits
# (ranges 0-9 and a-h).
# Length is the number of "bytes" (groups of 2 hexadecimal digits), and
# defaults to 6. The default ":" delimiter can be changed with "-s"
# option.
# This is the default option.
# passphrase
# Generate words from a diceware-like dictionary. Length is the number
# of words ans defaults to 6.
# pincode
# A numeric password. default LENGTH is 4, with no separator.
# string
# Password will be a string taken from different character ranges.
# By default, alphabetic characters and digits. See -x option for
# different character sets.
#
# OPTIONS
# -c, --copy
# Copy password to clipboard.
#
# -C, --capitalize
# For 'passphrase' and 'mac' type only.
# Passphrase: Capitalize words (first letter of each word). Recommended
# if separator is set to null-string (--separator=0).
# Mac: use capital hexadecimal digits.
#
# -d, --dictionary=FILE
# Use FILE as wordlist file. Default is en-5.
# FILE will be searched in these directories : root, current directory,
# and /usr/local/share/br-tools/gen-password directory.
#
# -g, --gui
# Will use a GUI (yad based) to propose the password. This GUI
# simply displays the password, allows to copy it to clipboard,
# and to re-generate a new password.
#
# -h, --help
# Display usage and exit.
#
# -l, --list-dictionaries
# Display the list of available dictionaries, with names suitable for
# the "-d" option.
#
# -m, --man
# Print a man-like help and exit.
#
# -n, --no-similar-chars
# For "string" type only, this option removes similar characters which
# could be difficult to differenciate: 0-O, 1-l, 8-B, [], ø-Ø, ~--, ...
#
# -s, --separator=CHAR
# CHAR is used as separator when TYPE allows it. Use "0" to remove
# separators.
#
# -v, --verbose
# Print messages on what is being done.
#
# -x, --extended=RANGE
# Specify the ranges of string type. Default is "a:1:a1", as lower case
# alphabetic characters (a-z) and digits (0-9), with at least one letter
# and one digit. RANGE is a string composed of:
# a: lower case alphabetic characters (a-z)
# A: upper case alphabetic characters (A-Z)
# e: extra European characters (e.g. À, É, é, Ï, ï, Ø, ø...)
# 1: digits (0-9)
# x: extended characters set 1: #$%&@^`~.,:;{[()]}
# y: extended characters set 2: "'\/|_-<>*+!?=
# k: japanese hiragana: あいうえおかき...
# When a RANGED character is followed by a ':' exactly one character of
# this range will appear in generated password: If we want two or more
# digits, the syntax would be '-x1:1:1'.
#
# TODO
# Add different languages wordlists.
# Replace hiragana with half-width katakana ?
# Add usage examples
#
# AUTHOR
# Bruno Raoult.
#
# SEE ALSO
# Pages on Diceware/words lists :
# EFF: https://www.eff.org/dice
# diceware: https://theworld.com/~reinhold/diceware.html
#
#%MAN_END%
SCRIPT="$0" # full path to script
CMDNAME=${0##*/} # script name
SHELLVERSION=$(( BASH_VERSINFO[0] * 10 + BASH_VERSINFO[1] ))
export LC_CTYPE="C.UTF-8" # to handle non ascii chars
# character sets
declare -A pw_charsets=(
[a]="abcdefghijklmnopqrstuvwxyz"
[A]="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
[1]="0123456789"
[e]="âêîôûáéíóúàèìòùäëïöüãõñçøÂÊÎÔÛÁÉÍÓÚÀÈÌÒÙÄËÏÖÜÃÕÑÇØ¡¿"
[x]='#$%&@^`~.,:;{[()]}'
[y]=\''"\/|_-<>*+!?='
[k]="あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよらりるれろわをん"
)
# default type, length, separator
declare pw_type="mac"
declare pw_length=6
declare pw_sep=":"
declare pw_cap=""
declare pw_dict=""
declare pw_copy=""
declare pw_gui=""
declare pw_verbose=""
declare pw_no_similar=""
declare pw_charset="a:A:1:aA1"
declare -A pw_commands=()
declare -a pw_command=()
usage() {
printf "usage: %s [-s CHAR][-d DICT][-x CHARSET][-Ccgmv] [TYPE] [LENGTH]\n" "$CMDNAME"
printf "Use '%s --man' for more help\n" "$CMDNAME"
return 0
}
man() {
sed -n '/^#%MAN_BEGIN%/,/^#%MAN_END%$/{//!s/^#[ ]\{0,1\}//p}' "$SCRIPT" | more
}
# log() - log messages on stderr
# parameters:
# -l, -s: long, or short prefix (default: none). Last one is used.
# -t: timestamp
# -n: no newline
# This function accepts either a string, either a format string followed
# by arguments :
# log -s "%s" "foo"
# log -s "foo"
log() {
local timestr="" prefix="" newline=y todo OPTIND
[[ -z $pw_verbose ]] && return 0
while getopts lsnt todo; do
case $todo in
l) prefix=$(printf "*%.s" {1..30})
;;
s) prefix=$(printf "*%.s" {1..5})
;;
n) newline=n
;;
t) timestr=$(date "+%F %T%z ")
;;
*)
;;
esac
done
shift $((OPTIND - 1))
[[ $prefix != "" ]] && printf "%s " "$prefix" >&2
[[ $timestr != "" ]] && printf "%s" "$timestr" >&2
# shellcheck disable=SC2059
printf "$@" >&2
[[ $newline = y ]] && printf "\n" >&2
return 0
}
# check_dict() - check for dictionary file
# $1: the dictionary filename (variable reference).
#
# @return: 0 on success, $1 will contain full path to dictionary.
# @return: 1 if not found
# @return: 2 if format is wrong
check_dict() {
local -n dict="$1"
local tmp_dir tmp_dict tmp_key tmp_dummy
if [[ -n "$dict" ]]; then
for tmp_dir in / ./ /usr/local/share/br-tools/gen-password/; do
tmp_dict="$tmp_dir$dict.txt"
log -n "checking for %s dictionary... " "$tmp_dict"
if [[ -f "$tmp_dict" ]]; then
log -n "found, "
# shellcheck disable=SC2034
read -r tmp_key tmp_dummy < "$tmp_dict"
if ! [[ $tmp_key =~ ^[1-6]+$ ]]; then
log "wrong format [%s]" "$tmp_key"
return 2
fi
log "key length=%d" "${#tmp_key}"
dict="$tmp_dict"
return 0
else
log "not found."
fi
done
printf "cannot find '%s' dictionary file\n" "$dict"
exit 1
fi
return 0
}
# list_dict() - list available dictionaries.
#
# @return: 0 on success
# @return: 1 on error
list_dict() {
local datadir="/usr/local/share/br-tools/gen-password" file fn fn2 key dummy
local -a output
local -i res=1 cur=0 i
if [[ -d "$datadir" ]]; then
printf -v output[0] "#\tlen\tName"
for file in "$datadir"/*.txt; do
fn=${file##*/}
fn=${fn%.txt}
# shellcheck disable=SC2034
fn2="$fn"
if check_dict fn2; then
(( cur++ ))
# shellcheck disable=SC2034
read -r key dummy < "$file"
printf -v output[cur-1] "%d\t%d\t%s" "$cur" "${#key}" "$fn"
fi
done
if ((cur > 0)); then
printf "#\tlen\tName\n"
for (( i = 0; i < cur; ++i )); do
printf "%s\n" "${output[i]}"
done
return 0
fi
fi
printf "No dictionaries found.\n"
return 1
}
# sanitize() - sanitize string for HTML characters
# $1: string to cleanup
#
# @return: 0, $1 will contain the sanitized string
sanitize() {
local str="$1"
str=${str//&/&amp;}
str=${str//</&lt;}
str=${str//>/&gt;}
str=${str//'"'/&quot;}
log "sanitized string: '%s' -> '%s'" "$1" "$str"
printf -- "%str" "$str"
}
# srandom() - use RANDOM to simulate SRANDOM
# $1: Reference of variable to hold result
#
# Note: RANDOM is 15 bits, SRANDOM is 32 bits.
#
# @return: 0, $1 will contain the 32 bits random number
srandom() {
local -n _ret=$1
(( _ret = RANDOM << 17 | RANDOM << 2 | RANDOM & 3 ))
}
# rnd() - get a random number integer
# $1: An integer, the modulo value
#
# @return: 0, output a string with the random integer on stdout.
#
# This function uses SRANDOM for bash >= 5.1 and srandom() function
# above for lower versions.
rnd() {
local mod=$1 ret
if (( SHELLVERSION >= 51 )); then
# shellcheck disable=SC2153
(( ret = SRANDOM ))
else
srandom ret
fi
printf "%d" "$(( ret % mod ))"
}
# shuffle() - shuffle a string
# $1: The string to shuffle
#
# The string is shuffled using the FisherYates shuffle method :
# https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
#
# @return: 0, output the shuffled string to stdout.
shuffle() {
local _str="$1"
local _res=""
local -i _i _len=${#_str} _cur=0
for (( _i = _len ; _i > 0; --_i )); do
_cur=$(rnd "$_i")
_res+=${_str:$_cur:1}
_str="${_str:0:_cur}${_str:_cur+1}"
done
printf "%s" "$_res"
return 0
}
# rnd_hex() - get a random 2-digits hex number
#
# @return: 0, output a string with the random integer on stdout.
rnd_hex() {
printf "%02x" "$(rnd 256)"
}
# rnd_dice() - get a 6 faces 1-6 random number
#
# @return: 0, output a string {1..6}
rnd_dice() {
printf "%d" "$(( $(rnd 6) + 1 ))"
}
# rnd_digit() - get a digit random number
#
# @return: 0, output a string {0..9}
rnd_digit() {
printf "%d" "$(( $(rnd 10) ))"
}
# rnd_word() - get a word from file
# $1: The dice rolls
# $2: The word list file ()
#
# @return: 0, output a string {0..9}
rnd_word() {
local roll="$1" file="$2" word=""
word=$(sed -n "s/^${roll}[[:blank:]]\+//p" "$file")
printf "%s" "$word"
}
# rnd_charset() - get a random string from a charset
# $1: A string with characters to choose from
# $2: An integer, the length of returned string
#
# @return: 0, output a random string from charset $1, with length $2.
rnd_charset() {
local charset="$1" ret=""
local -i len=$2 _i
#log "rnd_charset: %d from '%s'" "$len" "$charset"
for ((_i=0; _i<len; ++_i)); do
ret+=${charset:$(rnd ${#charset}):1}
done
#log "rnd_charset: return '%s'" "$ret"
printf "%s" "$ret"
}
# pwd_dice() - get a random dice style string
# $1: Integer, the number dice runs
# $2: Separator
#
# @return: 0, output dice rolls
pwd_dice() {
local -i i n="${1:-6}"
local sep="" _sep="${2}"
local str="" _str=""
for ((i = 0; i < n; ++i)); do
printf -v _str "%s%s" "$sep" "$(rnd_dice)"
str+="$_str"
sep="$_sep"
done
printf "%s" "$str"
return 0
}
pw_commands["dice"]=pwd_dice
# pwd_mac() - get a random MAC-address style string
# $1: Integer, the number of hex values
# $2: Separator
# $3: Capitalize
#
# @return: 0, output a random MAC-address style string.
pwd_pincode() {
local -i i n="${1:-6}"
local sep="" _sep="${2}" _cap="$3"
local str="" _str=""
for ((i = 0; i < n; ++i)); do
printf -v _str "%s%s" "$sep" "$(rnd_digit)"
str+="$_str"
sep="$_sep"
done
[[ -n $_cap ]] && str=${str^^}
printf "%s" "$str"
return 0
}
pw_commands["pincode"]=pwd_pincode
# pwd_mac() - get a random MAC-address style string
# $1: Integer, the number of hex values (default: 6)
# $2: Separator (default: "-")
# $3: Capitalize (default: "")
#
# @return: 0, output a random MAC-address style string.
pwd_mac() {
local -i i n="$1"
local sep="" _sep="${2}" _cap="$3"
local str="" _str=""
for ((i = 0; i < n; ++i)); do
str+="$sep$(rnd_hex)"
sep="$_sep"
done
[[ -n $_cap ]] && str=${str^^}
printf "%s" "$str"
return 0
}
pw_commands["mac"]=pwd_mac
# pwd_passphrase() - get a list of words from a diceware-style file
# $1: Integer, the number of words
# $2: Separator
# $3: Capitalize
# $4: diceware file
#
# @return: 0, output a random MAC-address style string.
pwd_passphrase() {
local -i i n="$1" _digits=0
local sep="" _sep="${2}" _cap="$3" _file="$4"
local str="" _str="" _key="" _dummy=""
# get the number of digits from 1st file line
read -r _key _dummy < "$_file"
_digits=${#_key}
log "passphrase setup: key 1=%s digits=%d" "$_key" "$_digits"
for ((i = 0; i < n; ++i)); do
_key=$(pwd_dice "$_digits" "")
_str=$(rnd_word "$_key" "$_file")
[[ -n $_cap ]] && _str=${_str^}
log "passphrase: key=%s str=%s" "$_key" "$_str"
str+="$sep$_str"
sep="$_sep"
done
printf "%s" "$str"
return 0
}
pw_commands["passphrase"]=pwd_passphrase
# pwd_string() - generate a string from a charset
# $1: Integer, the string length
# $5: The charset definition (e.g. "a:1:")
#
# @return: 0, output a random string from $5 charset.
pwd_string() {
local -i i n="$1"
local _charset="${5}" _allchars=""
local str="" _c="" _char=""
log "string setup: len=%d charset=[%s]" "$n" "$_charset"
# finds out mandatory characters and build final charset
log -n "mandatory chars:"
for (( i = 0; i < ${#_charset}; ++i )); do
_c="${_charset:i:1}"
if [[ ${_charset:i+1:1} == ":" ]]; then
_char=$(rnd_charset "${pw_charsets[$_c]}" 1)
log -n " [%s]" "$_char"
str+="$_char"
(( i++ ))
else
_allchars+=${pw_charsets[$_c]}
fi
done
log ""
if (( ${#str} < n && ${#_allchars} == 0 )); then
printf "Fatal: No charset to choose from ! Please check '-x' option."
exit 1
fi
log -n "generating %d remaining chars:" "$((n-${#str}))"
for ((i = ${#str}; i < n; ++i)); do
_char=$(rnd_charset "$_allchars" 1)
log -n " [%s]" "$_char"
str+="$_char"
done
log ""
log "string before shuffle : %s" "$str"
str="$(shuffle "$str")"
log "string after shuffle : %s" "$str"
# cut string if too long (may happen if too many mandatory chars)
(( ${#str} > n)) && log "truncating '%s' to '%s'" "$str" "${str:0:n}"
printf "%s" "${str:0:n}"
return 0
}
pw_commands["string"]=pwd_string
# print command() - print a pwd_command parameters
# $1: reference of pwd_command array
#
# @return: 0
print_command() {
local -n arr="$1"
local -a label=("function" "length" "sep" "cap" "dict" "charset")
local -i i
for i in "${!arr[@]}"; do
log -s "%s=[%s]" "${label[$i]}" "${arr[$i]}"
done
return 0
}
# gui_passwd() - GUI for passwords
# $1: reference pwd_command array
#
# @return: 0
gui_passwd() {
local -a _command=("$@")
local passwd="" res=0 sane=""
while
passwd=$("${_command[@]}")
sane=$(sanitize "$passwd")
yad --title="Password Generator" --text-align=center --text="$sane" \
--borders=20 --button=gtk-copy:0 --button=gtk-refresh:1 \
--button=gtk-ok:252 --window-icon=dialog-password
res=$?
log "res=%d\n" "$res"
if (( res == 0 )); then
printf "%s" "$passwd" | xsel -bi
fi
((res != 252))
do true; done
return $res
}
parse_opts() {
# short and long options
local sopts="cCd:ghlmns:vx:"
local lopts="copy,capitalize,dictionary:,gui,help,list-dictionaries,man,no-similar-chars,separator:,verbose,extended:"
# set by options
local tmp="" tmp_length="" tmp_sep="" tmp_cap="" tmp_dict="" tmp_dir=""
local tmp_charset=""
local c2="" c3=""
local -i i
if ! tmp=$(getopt -o "$sopts" -l "$lopts" -n "$CMDNAME" -- "$@"); then
log "Use '$CMD --help' or 'zob $CMDNAME --man' for help."
exit 1
fi
eval set -- "$tmp"
while true; do
case "$1" in
'-c'|'--copy')
pw_copy=y
;;
'-C'|'--capitalize')
tmp_cap=y
;;
'-d'|'--dictionary')
tmp_dict="$2"
shift
;;
'-g'|'--gui')
if ! type -P "yad" > /dev/null; then
printf "%s: Please install 'yad' package tu use 'g' option.\n" \
"$CMDNAME"
fi
pw_gui="y"
;;
'-h'|'--help')
usage
exit 0
;;
'-l'|'--list-dictionaries')
list_dict
exit 0
;;
'-m'|'--man')
man
exit 0
;;
'-n'|'no-similar-chars')
pw_no_similar=y
;;
'-s'|'--separator')
tmp_sep="$2"
shift
;;
'-v'|'--verbose')
pw_verbose=y
;;
'-x'|'--extended')
for (( i = 0; i < ${#2}; ++i)); do
c2="${2:i:1}"
case "$c2" in
a|A|1|x|y|k|e)
tmp_charset+="$c2"
c3="${2:i+1:1}"
if [[ "$c3" == ":" ]]; then
tmp_charset+=":"
(( i++ ))
fi
;;
*) printf "unknown character set '%s\n" "${2:$i:1}"
usage
exit 1
esac
done
shift
;;
'--')
shift
break
;;
*)
usage
log 'Internal error!'
exit 1
;;
esac
shift
done
# parse remaining arguments
if (($# > 0)); then # type
type=$1
case "$type" in
dice)
pw_type="dice"
tmp_length=5
[[ -z $tmp_sep ]] && tmp_sep=" "
;;
mac)
pw_type="mac"
tmp_length=6
[[ -z $tmp_sep ]] && tmp_sep=":"
;;
pincode)
pw_type="pincode"
tmp_length=4
[[ -z $tmp_sep ]] && tmp_sep="0"
;;
passphrase)
pw_type="passphrase"
tmp_length=6
[[ -z $tmp_dict ]] && tmp_dict="en-5"
[[ -z $tmp_sep ]] && tmp_sep=" "
[[ -z $tmp_cap ]] && tmp_cap=""
;;
string)
pw_type="string"
tmp_length=10
if [[ -n $pw_no_similar ]]; then
pw_charsets[A]="ABCDEFGHIJKLMNPQRSTUVWXYZ"
pw_charsets[a]="abcdefghijkmnopqrstuvwxyz"
pw_charsets[1]="23456789"
pw_charsets[e]="âêîôûáéíóúàèìòùñçÂÊÎÔÛÁÉÍÓÚÀÈÌÒÙÇ¡¿"
pw_charsets[x]='#$%&@^`.,:;{()}'
pw_charsets[y]='\/|_<>*+!?='
fi
if [[ -n $tmp_charset ]]; then
pw_charset="$tmp_charset"
fi
;;
*)
printf "%s: Unknown '%s' password type.\n" "$CMDNAME" "$type"
usage
exit 1
esac
shift
fi
if (($# > 0)); then # length
if ! [[ $1 =~ ^[0-9]+$ ]]; then
printf "%s: Bad '%s' length.\n" "$CMDNAME" "$1"
usage
exit 1
fi
tmp_length="$1"
shift
fi
[[ -n $tmp_length ]] && pw_length=$tmp_length
if ! (( pw_length )); then
printf "%s: Bad '%d' length.\n" "$CMDNAME" "$tmp_length"
usage
exit 1
fi
[[ -n $tmp_sep ]] && pw_sep=$tmp_sep
[[ $pw_sep = "0" ]] && pw_sep=""
[[ -n $tmp_cap ]] && pw_cap=$tmp_cap
[[ -n $tmp_dict ]] && pw_dict=$tmp_dict
# look for dictionary file
check_dict pw_dict || exit 1
}
parse_opts "$@"
pw_command=("${pw_commands[$pw_type]}" "$pw_length" "$pw_sep" "$pw_cap" "$pw_dict"
"$pw_charset")
print_command pw_command
if [[ -z $pw_gui ]]; then
passwd=$("${pw_command[@]}")
if [[ -n $pw_copy ]]; then
printf "%s" "$passwd" | xsel -bi
fi
printf "%s\n" "$passwd"
else
gui_passwd "${pw_command[@]}"
fi
exit 0

View File

@@ -0,0 +1,18 @@
### Dictionaries for gen-password.sh
#### English
Lists taken from :
[EFF word list](https://www.eff.org/fr/deeplinks/2016/07/new-wordlists-random-passphrases). I am unsure about licensing.
* [Large list](eff_large_wordlist.txt): To use with 5 dices.
* [Short list 1](eff_short_wordlist_1.txt): To use with 4 dices.
* [Short list 2](eff_short_wordlist_2_0.txt): 4 dices, with some improvements.
#### Français
Listes prises sur [github.com/mbelivo/diceware-wordlists-fr](https://github.com/mbelivo/diceware-wordlists-fr). La licence est CC BY-NC-SA 3.0.
* [Liste Longue](wordlist_fr_5d.txt): Pour 5 dés.
* [Liste Courte 1](wordlist_fr_4d.txt): Pour 4 dés.
* [Liste Courte 2](wordlist_fr_4d_2.txt): Pour 4 dés, avec quelques améliorations.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
eff_short_wordlist_2_0.txt

View File

@@ -0,0 +1 @@
eff_short_wordlist_1.txt

View File

@@ -0,0 +1 @@
eff_large_wordlist.txt

View File

@@ -0,0 +1 @@
wordlist_fr_4d_2.txt

View File

@@ -0,0 +1 @@
wordlist_fr_4d.txt

View File

@@ -0,0 +1 @@
wordlist_fr_5d.txt

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# sync-conf-example.sh - a "sync.sh" configuration file example. # sync-conf-example.sh - a "sync.sh" configuration file example.
# #
@@ -18,13 +18,17 @@
# The only mandatory ones are SOURCEDIR, SERVER, and DESTDIR. # The only mandatory ones are SOURCEDIR, SERVER, and DESTDIR.
###### source directory full path, destination server and path. ###### source directory full path, destination server and path.
###### SERVER could user@host, or "local" if local machine ###### SERVER could be user@host, or "local" if local machine
# SOURCEDIR="" # SOURCEDIR=""
# SERVER="" # SERVER=""
# DESTDIR="" # DESTDIR=""
SOURCEDIR=/example-srcdir export SOURCEDIR=/example-srcdir
SERVER=root@backuphost export SERVER=root@backuphost
DESTDIR=/mnt/nas1/example-destdir export DESTDIR=/mnt/nas1/example-destdir
###### backups mount point on local machine.
###### it is used by sync-view.sh only.
export BACKUPDIR=/mnt/backup-example-srcdir
###### backups to keep ###### backups to keep
# NYEARS=3 # NYEARS=3
@@ -59,6 +63,14 @@ beforesync() {
log -s "cannot get maria databases directory" log -s "cannot get maria databases directory"
exit 1 exit 1
fi fi
# dump users and permissions
log -n "dumping users and permissions... "
mysqldump --user=root --system=users > "$datadir/mariadb_users.sql"
log -n "compressing... "
gzip -f "$datadir/mariadb_users.sql"
log "done."
rm -f "$datadir/$FILTERNAME" rm -f "$datadir/$FILTERNAME"
# shellcheck disable=2207 # shellcheck disable=2207
if ! databases=( $(mysql -sN -u root -e "SHOW DATABASES;") ); then if ! databases=( $(mysql -sN -u root -e "SHOW DATABASES;") ); then
@@ -96,7 +108,7 @@ aftersync() {
log -s -t "calling user aftersync" log -s -t "calling user aftersync"
} }
# For Emacs, shell-mode: # For Emacs, shell-script-mode:
# Local Variables: # Local Variables:
# mode: shell-script # mode: shell-script
# End: # End:

324
bash/sms-free.sh Executable file
View File

@@ -0,0 +1,324 @@
#!/usr/bin/env bash
#
# sms-free.sh - send SMS to Free Mobile.
#
# (C) Bruno Raoult ("br"), 2022
# Licensed under the GNU General Public License v3.0 or later.
# Some rights reserved. See COPYING.
#
# You should have received a copy of the GNU General Public License along with this
# program. If not, see <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
#
# SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
#
#%MAN_BEGIN%
# NAME
# sms-free.sh - Send SMS to Free Mobile account.
#
# SYNOPSIS
# sms-free.sh [OPTIONS] [-k KEYFILE] USER [MESSAGE]
# sms-free.sh [OPTIONS] [-l LOGIN:PASSWORD] [MESSAGE]
#
# DESCRIPTION
# Send a SMS to a Free Mobile (french mobile operator). This script will
# only work for phones numbers for which you have the "SMS key" (see FREE
# MOBILE SMS SETUP below). Therefore yourself, close relatives, and other
# people who trust you).
# MESSAGE is the text to be sent. If missing, it will be read from standard
# input.
#
# OPTIONS
# -d, --dry-run
# Will not send the SMS.
#
# -h, --help
# Display usage and exit.
#
# -l, --login=ACCOUNT:SMSKEY
# Do not use a KEYFILE, and provide directly the Free Mobile ACCOUNT
# and SMSKEY.
#
# -k, --keyfile=KEYFILE
# Use KEYFILE instead of default ~/data/private/free-sms-keys.txt.
#
# -m, --man
# Print a man-like help and exit.
#
# -v, --verbose
# Print messages on what is being done.
#
# FREE MOBILE SMS SETUP
# You should first connect on https://mobile.free.fr/account/, and
# activate the option "Mes options / Notifications par SMS". You will be
# given a key.
#
# KEY FILE SYNTAX
# The key file contains lines of the form:
# id:login:password
# id
# A mnemonic for the user (firstname, etc...), it should be unique.
# login
# A valid Free Mobile account number (usually 8 digits).
# key
# The SMS key associated with the Free Mobile login (usually a 14
# alphanumeric string).
#
# Example:
# bruno:01234567:abcdeABCDE1234
# bodiccea:76543210:xyztXYZT123456
#
# AUTHOR
# Bruno Raoult.
#%MAN_END%
#
# PERSONAL NOTES/TODO
# In example above, "%20" can be replaced by "+"
# See: https://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1
# utf8 characters look supported (tested on French accentuated characters,
# Japanese kana and kanji, and Chinese)
#
# FREE MOBILE DOCUMENTATION
#
# L'envoi du SMS se fait en appelant le lien suivant :
#
# https://smsapi.free-mobile.fr/sendmsg
# avec les paramètres suivants :
#
# user : votre login
# pass : votre clé d'identification générée automatiquement par notre
# service
# msg : le contenu du SMS encodé sous forme d'url (Percent-encoding)
#
# Exemple : Envoyer le message "Hello World !" sur votre mobile :
#
# https://smsapi.free-mobile.fr/sendmsg?user=12345678&pass=abcABC12345678&msg=Hello%20World%20!
#
# Vous pouvez également, si vous le préférez, envoyer les paramètres en POST.
# Dans ce cas, le contenu du message n'a pas besoin d'être encodé.
# Le code de retour HTTP indique le succès ou non de l'opération :
#
# 200 : Le SMS a été envoyé sur votre mobile.
# 400 : Un des paramètres obligatoires est manquant.
# 402 : Trop de SMS ont été envoyés en trop peu de temps.
# 403 : Le service n'est pas activé sur l'espace abonné, ou login / clé
# incorrect.
# 500 : Erreur côté serveur. Veuillez réessayer ultérieurement.
#set -x
script="$0" # full path to script
cmdname=${0##*/} # script name
export LC_CTYPE="C.UTF-8" # to handle non ascii chars
declare sms_keyfile=~/data/private/free-sms-keys.txt
declare sms_verbose=""
declare sms_credentials=""
declare sms_dryrun=""
declare sms_message=""
declare sms_url="https://smsapi.free-mobile.fr/sendmsg"
declare -A sms_status=(
[-]="Unknown error"
[200]="OK"
[400]="Missing parameter"
[402]="Too many SMS sent in short time"
[403]="Service non activated or incorrect credentials"
[500]="Server error"
)
usage() {
printf "usage: %s [-hmv] [-k KEYFILE] USER [MESSAGE]\n" "$cmdname"
printf " %s [-hmv] [-l LOGIN:PASSWORD] [MESSAGE]\n" "$cmdname"
printf "Use '%s --man' for more help\n" "$cmdname"
return 0
}
man() {
sed -n '/^#%MAN_BEGIN%/,/^#%MAN_END%$/{//!s/^#[ ]\{0,1\}//p}' "$script" | more
}
# log() - log messages on stderr
# parameters:
# -l, -s: long, or short prefix (default: none). Last one is used.
# -t: timestamp
# -n: no newline
# This function accepts either a string, either a format string followed
# by arguments :
# log -s "%s" "foo"
# log -s "foo"
log() {
local timestr="" prefix="" newline=y todo OPTIND
[[ -z $sms_verbose ]] && return 0
while getopts lsnt todo; do
case $todo in
l) prefix=$(printf "*%.s" {1..30})
;;
s) prefix=$(printf "*%.s" {1..5})
;;
n) newline=n
;;
t) timestr=$(date "+%F %T%z ")
;;
*)
;;
esac
done
shift $((OPTIND - 1))
[[ $prefix != "" ]] && printf "%s " "$prefix" >&2
[[ $timestr != "" ]] && printf "%s" "$timestr" >&2
# shellcheck disable=SC2059
printf "$@" >&2
[[ $newline = y ]] && printf "\n" >&2
return 0
}
# echorun() - logs and run (maybe) a command.
# $1: reference of variable which will get the output of command
# $2..$n: command to log and run
echorun() {
local -n _out="$1"
shift
[[ -n $sms_dryrun ]] && log -n "dry-run: "
log "%s" "$*"
[[ -z $sms_dryrun ]] && _out=$("$@")
return $?
}
# get_credentials() - get credentials from keyfile
# $1: reference of variable which will contain credentials
# $2: keyfile
# $3: user to find
#
# @return: 0 on success
# @return: 1 on file not present or not readable
# @return: 2 if user not found
get_credentials() {
local -n _cred=$1
local _keyfile="$2" _user="$3" _name=""
log "get_credentials: ref=%s user=%s keyfile=%s" "$!_cred" "$_user" "$_keyfile"
if [[ ! -r "$_keyfile" ]]; then
printf "%s: cannot read keyfile %s\n" "$cmdname" "$_keyfile"
return 2
fi
while IFS=: read -r _name _cred; do
log -n "key: name=[%s] creds=[%s]... " "$_name" "$_cred"
[[ $_name = "$_user" ]] && log "match." && return 0
log "skipping."
done < "$_keyfile"
printf "%s: cannot find credentials for user '%s'\n" "$cmdname" "$_user"
return 2
}
parse_opts() {
# short and long options
local sopts="dhk:l:mv"
local lopts="dry-run,help,keyfile:,login:,man,verbose"
if ! tmp=$(getopt -o "$sopts" -l "$lopts" -n "$cmdname" -- "$@"); then
log "Use '%s --help' or '%s --man' for help." "$cmdname" "$cmdname"
exit 1
fi
eval set -- "$tmp"
while true; do
case "$1" in
'-d'|'--dry-run')
sms_dryrun=y
;;
'-h'|'--help')
usage
exit 0
;;
'-k'|'--keyfile')
sms_keyfile="$2"
shift
;;
'-l'|'--login')
sms_credentials="$2"
log "sms_creds=%s" "$sms_credentials"
shift
;;
'-m'|'--man')
man
exit 0
;;
'-v'|'--verbose')
sms_verbose=y
;;
'--')
shift
break
;;
*)
usage
log 'Internal error!'
exit 1
;;
esac
shift
done
# parse remaining arguments
case "$#" in
0)
# no user, no message: we need sms_credentials
if [[ -z $sms_credentials ]]; then
printf "%s: Missing credentials.\n" "$cmdname"
exit 1
fi
;;
1|2)
# get credentials from KEYFILE
if [[ -z $sms_credentials ]]; then
get_credentials sms_credentials "$sms_keyfile" "$1" || exit 1
shift
else
# cannot have user and sms_credentials
(( $# == 2 )) && usage && exit 1
fi
if [[ $# == 1 ]]; then
sms_message="$1"
else
readarray sms_message
printf -v sms_message "%s" "${sms_message[@]}"
sms_message=${sms_message%$'\n'} # remove trailing '\n'
fi
;;
*)
usage
;;
esac
log "credentials=%s" "$sms_credentials"
log "message=[%s]" "$sms_message"
}
# send-sms() - send SMS (GET method)
send_sms() {
local _login=${sms_credentials%:*} _pass=${sms_credentials#*:} _res="" _str=""
log "send_sms(): login=%s pass=%s" "$_login" "$_pass"
echorun _res curl --silent --get --write-out '%{http_code}' \
--data "user=$_login" \
--data "pass=$_pass" \
--data-urlencode "msg=$sms_message" \
"$sms_url"
[[ -n $sms_dryrun ]] && _res=200
_str="${sms_status[$_res]:-${sms_status[-]}}"
log "send_sms(): curl status=%s (%s)" "$_res" "$_str"
if [[ $_res != 200 ]]; then
printf "%s: %s\n" "$cmdname" "$_str"
fi
}
parse_opts "$@"
send_sms
exit 0
# Indent style for emacs
# Local Variables:
# sh-basic-offset: 4
# sh-indentation: 4
# indent-tabs-mode: nil
# comment-column: 32
# End:

391
bash/sync-view.sh Executable file
View File

@@ -0,0 +1,391 @@
#!/usr/bin/env bash
#
# sync-view.sh - view file versions in a sync.sh backup directory.
#
# (C) Bruno Raoult ("br"), 2007-2022
# Licensed under the GNU General Public License v3.0 or later.
# Some rights reserved. See COPYING.
#
# You should have received a copy of the GNU General Public License along with this
# program. If not, see <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
#
# SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
#
#%MAN_BEGIN%
# NAME
# sync-view.sh - list file versions from rsync.sh backups.
#
# SYNOPSIS
# sync-view.sh [OPTIONS] TARGET
#
# DESCRIPTION
# List TARGET versions from a sync.sh backup directory.
#
# OPTIONS
# -1, --unique
# Skip duplicate files. This option do not apply if TARGET is a
# directory.
#
# -a, --absolute-target
# Do not try to resolve TARGET path. By default, the script will try to
# guess TARGET absolute path. This is not possible if current system is
# different from the one from which the backup was made, or if some
# path component are missing or were changed.
# If this option is used, TARGET must be an absolute path as it was on
# backuped machine.
#
# -b, --backupdir=DIR
# DIR is the local path where the backups can be found. It can be a
# network mount, or the destination directory if the backup was local.
# This option is mandatory.
#
# -c, --config
# A sync.sh configuration file where the script could find variables
# SOURCEDIR (option '-r') and BACKUPDIR (option '-b').
# If this option is missing, the script will try to find a .syncrc file
# in DIR/daily-01 directory, where DIR is the local path of backups
# (option -b).
#
# -d, --destdir
# Directory which will hold links to actual files. It will be created
# if non-existant. If this option is missing, a temporary directory will
# be created in /tmp.
#
# -h, --help
# Display short help and exit.
#
# -m, --man
# Display a "man-like" description and exit.
#
# -r, --root=DIR
# DIR is the path of the backup source. If '-c' option is used, the
# variable SOURCEDIR will be used. By default '/'.
#
# -v, --verbose
# Print messages on what is being done.
#
# -x, --exclude=REGEX
# Filenames matching REGEX (with relative path to backup directory,
# as specified with '-b' option) will be excluded. This option can be
# useful
#
# EXAMPLES
# The next command will list all .bashrc versions for current user, from
# backups in /mnt/backup. yearly and monthly-03 to monthly-09 are
# excluded. Source directory (-r) of backups are taken from sync.sh
# configuration file named s.conf. A temporary directory will be created
# in /mnt to handle links to actual files.
# $ sync-view.sh -c s.conf -b /mnt/backup -x "^(yearly|monthly-0[3-9]).*$" ~/.bashrc
#
# The simplest invocation: the versions of users' .bashrc will be retrieved
# in backups from /mnt/backup. A /mnt/backup/daily-01/.syncrc must exist.
# $ sync-view.sh -b /mnt/backup ~/.bashrc
#
# Links to user's .bashrc backups will be put in /tmp/test. Files are in
# /mnt/backup, which contains backups of /export directory. The /tmp/test
# directory will be created if necessary.
# $ sync-view.sh -r /export -b /mnt/backup -d /tmp/test ~/.bashrc
#
# AUTHOR
# Bruno Raoult.
#
#%MAN_END%
# internal variables, cannot (and *should not*) be changed unless you
# understand exactly what you do.
SCRIPT="$0" # full path to script
CMDNAME=${0##*/} # script name
HOSTNAME="$(hostname)"
ROOTDIR="/" # root of backup source
BACKUPDIR="" # the local view of backup dirs
TARGETDIR="" # temp dir to hold links
TARGET="" # the file/dir to find
RESOLVETARGET=y # resolve TARGET
UNIQUE="" # omit duplicate files
EXCLUDE="" # regex for files to exclude
VERBOSE="" # -v option
declare -A INODES # inodes table (for -1 option)
# error management
set -o errexit
#set -o xtrace
usage() {
printf "usage: %s [-b BACKUPDIR][-c CONF][-d DSTDIR][-r ROOTDIR][-x EXCLUDE][-1ahmv] file\n" "$CMDNAME"
return 0
}
man() {
sed -n '/^#%MAN_BEGIN%/,/^#%MAN_END%$/{//!s/^#[ ]\{0,1\}//p}' "$SCRIPT" | more
}
# log function
# parameters:
# -l, -s: long, or short prefix (default: none). Last one is used.
# -t: timestamp
# -n: no newline
# This function accepts either a string, either a format string followed
# by arguments :
# log -s "%s" "foo"
# log -s "foo"
log() {
local timestr="" prefix="" newline=y todo OPTIND
[[ -z $VERBOSE ]] && return 0
while getopts lsnt todo; do
case $todo in
l) prefix=$(printf "*%.s" {1..30})
;;
s) prefix=$(printf "*%.s" {1..5})
;;
n) newline=n
;;
t) timestr=$(date "+%F %T%z ")
;;
*)
;;
esac
done
shift $((OPTIND - 1))
[[ $prefix != "" ]] && printf "%s " "$prefix"
[[ $timestr != "" ]] && printf "%s" "$timestr"
# shellcheck disable=SC2059
printf "$@"
[[ $newline = y ]] && printf "\n"
return 0
}
# filetype() - get file type
#
# $1: the file to check
#
# @return: 0, output a string with file type on stdout.
filetype() {
local file="$1" type="unknown"
if [[ ! -e "$file" ]]; then
type="missing"
elif [[ -h "$file" ]]; then
type="symlink"
elif [[ -f "$file" ]]; then
type="file"
elif [[ -d "$file" ]]; then
type="directory"
elif [[ -p "$file" ]]; then
type="fifo"
elif [[ -b "$file" || -c "$file" ]]; then
type="device"
fi
printf "%s" "$type"
return 0
}
# command-line parsing / configuration file read.
parse_opts() {
# short and long options
local sopts="1ab:c:d:hmr:vx:"
local lopts="unique,absolute-target,backupdir:,config:,destdir:,help,man,root:,verbose,exclude:"
local tmp tmp_destdir="" tmp_destdir="" tmp_rootdir="" tmp_config=""
if ! tmp=$(getopt -o "$sopts" -l "$lopts" -n "$CMD" -- "$@"); then
log "Use '$CMD --help' or '$CMD --man' for help."
exit 1
fi
eval set -- "$tmp"
while true; do
case "$1" in
-1|--unique)
UNIQUE=yes
;;
'-a'|'--absolute-target')
RESOLVETARGET=""
;;
'-b'|'--backupdir')
tmp_backupdir="$2"
shift
;;
'-c'|'--config')
# The configuration file contains the variable SOURCEDIR, which will allow
# to find the relative path of TARGET in backup tree.
# it may also contain BACKUPDIR variable, which the local root of backup
# tree.
tmp_config="$2"
shift
;;
'-d'|'--destdir')
tmp_destdir="$2"
shift
;;
'-h'|'--help')
usage
exit 0
;;
'-m'|'--man')
man
exit 0
;;
'-r'|'--rootdir')
tmp_rootdir="$2"
shift
;;
'-v'|'--verbose')
VERBOSE=yes
;;
'-x'|'--exclude')
EXCLUDE="$2"
shift
;;
'--')
shift
break
;;
*)
usage
log 'Internal error!'
exit 1
;;
esac
shift
done
# Now check remaining argument (searched file).
if (( $# != 1 )); then
usage
exit 1
fi
TARGET="$1"
[[ -z $RESOLVETARGET ]] || TARGET="$(realpath -L "$TARGET")"
# if $config is not set, look for .syncrc in BACKUPDIR
tmp_config=${tmp_config:-$tmp_backupdir/daily-01/.syncrc}
if [[ -z "$tmp_config" ]]; then
printf "%s: Missing configuration file.\n" "$CMDNAME"
exit 10
elif [[ ! -r "$tmp_config" ]]; then
printf "%s: Cannot open %s file. Exiting.\n" "$CMDNAME" "$tmp_config"
exit 9
fi
# shellcheck source=sync-conf-example.sh
source "$tmp_config"
[[ -n "$SOURCEDIR" ]] && ROOTDIR="$SOURCEDIR"
[[ -n "$tmp_backupdir" ]] && BACKUPDIR="$tmp_backupdir"
[[ -n "$tmp_destdir" ]] && TARGETDIR="$tmp_destdir"
[[ -n "$tmp_rootdir" ]] && ROOTDIR="$tmp_rootdir"
return 0
}
check_paths() {
local tmp
[[ -z "$BACKUPDIR" ]] && printf "%s: backup directory is not set.\n" "$CMDNAME" && \
! usage
[[ -z "$ROOTDIR" ]] && printf "%s: source directory is not set.\n" "$CMDNAME" && \
! usage
if [[ -n "$TARGETDIR" ]]; then
if [[ ! -e $TARGETDIR ]]; then
log "Creating destination directory %s." "$TARGETDIR"
mkdir "$TARGETDIR"
fi
else
tmp="$(basename "$TARGET")"
TARGETDIR="$(mktemp -d /tmp/"$tmp"-XXXXXXXX)"
log "%s target directory created." "$TARGETDIR"
fi
log "ROOTDIR=[%s]" "$ROOTDIR"
log "BACKUPDIR=[%s]" "$BACKUPDIR"
log "TARGETDIR=[%s]" "$TARGETDIR"
log "TARGET=[%s]" "$TARGET"
for var in BACKUPDIR TARGETDIR; do
[[ $var = ROOTDIR && -z $RESOLVETARGET ]] && continue
if [[ ! -d "${!var}" ]]; then
printf "%s is not a directory.\n" "$var"
exit 1
fi
done
if ! pushd "$TARGETDIR" > /dev/null; then
printf "cannot change to directory %s.\n" "$TARGETDIR"
exit 1
fi
# remove existing files
if [[ -n "$(ls -A .)" ]]; then
log "Cleaning existing directory %s." "$TARGETDIR"
for target in *; do
rm "$target"
done
fi
return 0
}
parse_opts "$@"
check_paths
# add missing directories
declare -a DIRS
DIRS=("$BACKUPDIR"/{dai,week,month,year}ly-[0-9][0-9])
log "DIRS=%s" "${DIRS[*]}"
for file in "${DIRS[@]}"; do
# src is file/dir in backup tree
_tmp=${TARGET#"$ROOTDIR"}
[[ $_tmp =~ ^/.*$ ]] || _tmp="/$_tmp"
src="$file$_tmp"
#printf "src=%s\n" "$src"
if [[ ! -e $src ]]; then
log "Skipping non-existing %s" "$src"
continue
fi
#ls -li "$src"
# last modification time in seconds since epoch
inode=$(stat --dereference --printf="%i" "$src")
date=$(stat --printf="%Y" "$src")
date_backup=$(stat --dereference --printf="%Y" "$file")
# target is daily-01, etc...
#target=$(date --date="@$date" "+%Y-%m-%d %H:%M")" - ${file#"$BACKUPDIR/"}"
target="${file#"$BACKUPDIR/"}"
#printf "target=[%s] src=[%s]\n" "$target" "$src"
if [[ -n $EXCLUDE && $target =~ $EXCLUDE ]]; then
log "Skipping %s\n" "$file"
continue
fi
if [[ -z $UNIQUE || ! -v INODES[$inode] ]]; then
log "Adding %s inode %s (%s)" "$file" "$inode" "$target"
ln -fs "$src" "$TARGETDIR/$target"
else
log "Skipping duplicate inode %s (%s)" "$inode" "$target"
fi
INODES[$inode]=${INODES[$inode]:-$date}
INODES[backup-$inode]=${INODES[backup-$inode]:-$date_backup}
done
if [[ -n "$(ls -A .)" ]]; then
printf "backup date (backup)|last changed|inode|size|perms|type|path\n"
# for file in {dai,week,month,year}ly-[0-9][0-9]; do
for symlink in *; do
file=$(readlink "$symlink")
#printf "file=<%s> link=<%s>\n" "$file" "$symlink" >&2
inode=$(stat --printf="%i" "$file")
type=$(filetype "$file")
#links=$(stat --printf="%h" "$file")
date=$(date --date="@${INODES[$inode]}" "+%Y-%m-%d %H:%M")
backup_date=$(date --date="@${INODES[backup-$inode]}" "+%Y-%m-%d %H:%M")
size=$(stat --printf="%s" "$file")
perms=$(stat --printf="%A" "$file")
printf "%s (%s)|" "$backup_date" "$symlink"
printf "%s|" "$date"
#printf "%s|" "$links"
printf "%s|" "$inode"
printf "%s|" "$size"
printf "%s|" "$perms"
printf "%s|" "$type"
printf "%s\n" "$file"
# ls -lrt "$TARGETDIR"
done | sort -r
fi | column -t -s\|
printf "temporary files directory is: %s\n" "$PWD"
exit 0

View File

@@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# sync.sh - a backup utility using ssh/rsync facilities. # sync.sh - a backup utility using ssh/rsync facilities.
# #
@@ -16,12 +16,13 @@
# sync.sh - a backup utility using ssh/rsync facilities. # sync.sh - a backup utility using ssh/rsync facilities.
# #
# SYNOPSIS # SYNOPSIS
# sync.sh [-a PERIOD][-DflmnruvzZ] CONFIG # sync.sh [OPTIONS] [SOURCE_DIR]
# #
# DESCRIPTION # DESCRIPTION
# Perform a backup to a local or remote destination, keeping different # Perform a backup to a local or remote destination, keeping different
# versions (daily, weekly, monthly, yearly). All options can be set in # versions (daily, weekly, monthly, yearly). All options can be set in
# CONFIG file, which is mandatory. # a mandatory configuration file, which is either SOURCE_DIR/.syncrc,
# either set with the '-c' option. See option '-c' below.
# The synchronization is make with rsync(1), and only files changed or # The synchronization is make with rsync(1), and only files changed or
# modified are actually copied; files which are identical with previous # modified are actually copied; files which are identical with previous
# backup are hard-linked to previous one. # backup are hard-linked to previous one.
@@ -42,6 +43,10 @@
# depending on the current day or date: daily backup every day, # depending on the current day or date: daily backup every day,
# weekly every sunday, monthly every first day of month, and yearly # weekly every sunday, monthly every first day of month, and yearly
# every Jan 1st. # every Jan 1st.
# -c CONFIG
# Use CONFIG as configuration file. See sync-conf-example.sh.
# If this option is used, the script will ignore SOURCE_DIR/.syncrc
# file.
# -D # -D
# By default, this script re-routes all outputs (stdout and stderr) # By default, this script re-routes all outputs (stdout and stderr)
# to a temporary file after basic initialization (mainly options # to a temporary file after basic initialization (mainly options
@@ -221,7 +226,8 @@ man() {
} }
usage() { usage() {
printf "usage: %s [-a PERIOD][-DflmnruvzZ] config-file\n" "$CMDNAME" printf "usage: %s [-a PERIOD][-c CONFIG][-DflmnruvzZ] [backup_directory]\n" \
"$CMDNAME"
exit 8 exit 8
} }
@@ -417,9 +423,11 @@ exit_handler() {
############################################################################### ###############################################################################
# command-line parsing / configuration file read. # command-line parsing / configuration file read.
parse_opts() { parse_opts() {
local _config="" _backup_dir=""
OPTIND=0 OPTIND=0
shopt -s extglob # to parse "-a" option shopt -s extglob # to parse "-a" option
while getopts a:DflmnruvzZ todo; do while getopts a:c:DflmnruvzZ todo; do
case "$todo" in case "$todo" in
a) a)
# we use US (Unit Separator, 0x1F, control-_) as separator # we use US (Unit Separator, 0x1F, control-_) as separator
@@ -437,6 +445,13 @@ parse_opts() {
esac esac
done done
;; ;;
c)
_config="$OPTARG"
if [[ ! -f "$_config" ]]; then
printf "%s: invalid %s configuration file\n" "$CMDNAME" "$_config"
usage
fi
;;
f) f)
FILTERLNK=y FILTERLNK=y
;; ;;
@@ -470,20 +485,38 @@ parse_opts() {
;; ;;
esac esac
done done
# Now check remaining argument (configuration file), which should be unique, # Now check remaining argument (backup directory)
# and read the file.
shift $((OPTIND - 1)) shift $((OPTIND - 1))
(( $# != 1 )) && usage
CONFIG="$1"
if [[ ! -r "$CONFIG" ]]; then (( $# > 1 )) && usage
printf "%s: Cannot open $CONFIG file. Exiting.\n" "$CMDNAME" if (( $# == 1 )); then
_backup_dir="$1"
if [[ ! -d $_backup_dir ]]; then
printf "%s: %s: not a directory\n" "$CMDNAME" "$_backup_dir"
usage
fi
[[ -f "$_backup_dir/.syncrc" ]] && _config=${_config:-"$_backup_dir/.syncrc"}
fi
# We do not know what to do...
[[ -z "$_config" ]] && usage
# see https://unix.stackexchange.com/questions/406216
CONFIG=$(realpath -sm "$_config")
if [[ -z "$CONFIG" ]]; then
printf "%s: Missing configuration file\n" "$CMDNAME"
exit 9
elif [[ ! -r "$CONFIG" ]]; then
printf "%s: Cannot open %s file\n" "$CMDNAME" "$CONFIG"
exit 9 exit 9
fi fi
# shellcheck source=/dev/null # shellcheck source=share/sync/sync-conf-example.sh
source "$CONFIG" source "$CONFIG"
LOCKDIR="/tmp/$CMDNAME-$HOSTNAME-${CONFIG##*/}.lock" # _backup_dir takes precedence on SOURCEDIR (useless ?)
SOURCEDIR=${_backup_dir:-$SOURCEDIR}
LOCKDIR="/tmp/$CMDNAME-$HOSTNAME${SOURCEDIR////-}.lock"
} }
parse_opts "$@" parse_opts "$@"
@@ -529,13 +562,13 @@ TODO=()
[[ $YEARLY = y ]] && (( NYEARS > 0 )) && TODO+=(yearly "$NYEARS") [[ $YEARLY = y ]] && (( NYEARS > 0 )) && TODO+=(yearly "$NYEARS")
log -l -t "Starting %s" "$CMDNAME" log -l -t "Starting %s" "$CMDNAME"
log "Bash version: %s.%s.%s" \ log "Bash version: %s.%s.%s" "${BASH_VERSINFO[@]:0:3}"
"${BASH_VERSINFO[0]}" "${BASH_VERSINFO[1]}" "${BASH_VERSINFO[2]}"
log "Hostname: %s" "$HOSTNAME" log "Hostname: %s" "$HOSTNAME"
log "Operating System: %s on %s" "$(uname -sr)" "$(uname -m)" log "Operating System: %s on %s" "$(uname -sr)" "$(uname -m)"
log "Config : %s\n" "$CONFIG" log "Config : %s\n" "$CONFIG"
log "Src dir: %s" "$SOURCEDIR" log "Src dir: %s" "$SOURCEDIR"
log "Dst dir: %s" "$SERVER:$DESTDIR" log "Dst dir: %s" "$SERVER:$DESTDIR"
log "Lock dir: %s" "$LOCKDIR"
log "Actions: %s" "${TODO[*]}" log "Actions: %s" "${TODO[*]}"
if (( ${#RSYNCOPTS[@]} )); then if (( ${#RSYNCOPTS[@]} )); then
log -n "Rsync additional options (%d): " "${#RSYNCOPTS[@]}" log -n "Rsync additional options (%d): " "${#RSYNCOPTS[@]}"

178
c/bits.h
View File

@@ -1,178 +0,0 @@
/* bits.h - bits functions.
*
* Copyright (C) 2021-2022 Bruno Raoult ("br")
* Licensed under the GNU General Public License v3.0 or later.
* Some rights reserved. See COPYING.
*
* You should have received a copy of the GNU General Public License along with this
* program. If not, see <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
*
* SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
*
*/
#ifndef BITS_H
#define BITS_H
#include <stdint.h>
/* next include will define __WORDSIZE: 32 or 64
*/
#include <bits/wordsize.h>
#ifndef __has_builtin
#define __has_builtin(x) 0
#endif
/* no plan to support 32bits for now...
*/
#if __WORDSIZE != 64
#error "Only 64 bits word size supported."
#endif
/* fixed-size types
*/
typedef int64_t s64;
typedef int32_t s32;
typedef int16_t s16;
typedef int8_t s8;
typedef uint64_t u64;
typedef uint32_t u32;
typedef uint16_t u16;
typedef uint8_t u8;
/* convenience types
*/
typedef unsigned long int ulong;
typedef unsigned int uint;
typedef unsigned short ushort;
typedef unsigned char uchar;
/* char is a special case, as it can be signed or unsigned
*/
typedef signed char schar;
/* count trailing zeroes : 00101000 -> 3
* ^^^
*/
static inline int ctz64(u64 n)
{
# if __has_builtin(__builtin_ctzl)
# ifdef DEBUG_BITS
log_f(1, "builtin ctzl.\n");
# endif
return __builtin_ctzl(n);
# elif __has_builtin(__builtin_clzl)
# ifdef DEBUG_BITS
log_f(1, "builtin clzl.\n");
# endif
return __WORDSIZE - (__builtin_clzl(n & -n) + 1);
# else
# ifdef DEBUG_BITS
log_f(1, "emulated.\n");
# endif
return popcount64((n & n) 1);
# endif
}
/* count leading zeroes : 00101000 -> 2
* ^^
*/
static inline int clz64(u64 n)
{
# if __has_builtin(__builtin_clzl)
# ifdef DEBUG_BITS
log_f(1, "builtin.\n");
# endif
return __builtin_clzl(n);
# else
# ifdef DEBUG_BITS
log_f(1, "emulated.\n");
# endif
u64 r, q;
r = (n > 0xFFFFFFFF) << 5; n >>= r;
q = (n > 0xFFFF) << 4; n >>= q; r |= q;
q = (n > 0xFF ) << 3; n >>= q; r |= q;
q = (n > 0xF ) << 2; n >>= q; r |= q;
q = (n > 0x3 ) << 1; n >>= q; r |= q;
r |= (n >> 1);
return __WORDSIZE - r - 1;
# endif
}
/* find first set : 00101000 -> 4
* ^
*/
static inline uint ffs64(u64 n)
{
# if __has_builtin(__builtin_ffsl)
# ifdef DEBUG_BITS
log_f(1, "builtin ffsl.\n");
# endif
return __builtin_ffsll(n);
# elif __has_builtin(__builtin_ctzl)
# ifdef DEBUG_BITS
log_f(1, "builtin ctzl.\n");
# endif
if (n == 0)
return (0);
return __builtin_ctzl(n) + 1;
# else
# ifdef DEBUG_BITS
log_f(1, "emulated.\n");
# endif
return popcount64(n ^ ~-n);
# endif
}
static inline int popcount64(u64 n)
{
# if __has_builtin(__builtin_popcountl)
# ifdef DEBUG_BITS
log_f(1, "builtin.\n");
# endif
return __builtin_popcountl(n);
# else
# ifdef DEBUG_BITS
log_f(1, "emulated.\n");
# endif
int count = 0;
while (n) {
count++;
n &= (n - 1);
}
return count;
# endif
}
/** bit_for_each64 - iterate over an u64 bits
* @pos: an int used as current bit
* @tmp: a temp u64 used as temporary storage
* @ul: the u64 to loop over
*
* Usage:
* u64 u=139, _t; // u=b10001011
* int cur;
* bit_for_each64(cur, _t, u) {
* printf("%d\n", cur);
* }
* This will display the position of each bit set in ul: 1, 2, 4, 8
*
* I should probably re-think the implementation...
*/
#define bit_for_each64(pos, tmp, ul) \
for (tmp = ul, pos = ffs64(tmp); tmp; tmp &= (tmp - 1), pos = ffs64(tmp))
/** or would it be more useful (counting bits from zero instead of 1) ?
*/
#define bit_for_each64_2(pos, tmp, ul) \
for (tmp = ul, pos = ctz64(tmp); tmp; tmp ^= 1UL<<pos, pos = ctz64(tmp))
#endif /* BITS_H */

111
c/debug.c
View File

@@ -1,111 +0,0 @@
/* debug.c - debug/log management
*
* Copyright (C) 2021 Bruno Raoult ("br")
* Licensed under the GNU General Public License v3.0 or later.
* Some rights reserved. See COPYING.
*
* You should have received a copy of the GNU General Public License along with this
* program. If not, see <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
*
* SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
*
*/
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
#ifndef DEBUG_DEBUG
#define DEBUG_DEBUG
#endif
#include "debug.h"
#define NANOSEC 1000000000 /* nano sec in sec */
#define MILLISEC 1000000 /* milli sec in sec */
static s64 timer_start; /* in nanosecond */
static u32 debug_level=0;
void debug_level_set(u32 level)
{
debug_level = level;
log(1, "debug level set to %u\n", level);
}
void debug_init(u32 level)
{
struct timespec timer;
debug_level_set(level);
if (!clock_gettime(CLOCK_MONOTONIC, &timer)) {
timer_start = timer.tv_sec * NANOSEC + timer.tv_nsec;
}
else {
timer_start = 0;
}
log(0, "timer started.\n");
}
inline static s64 timer_elapsed()
{
struct timespec timer;
clock_gettime(CLOCK_MONOTONIC, &timer);
return (timer.tv_sec * NANOSEC + timer.tv_nsec) - timer_start;
}
/* void debug - log function
* @timestamp : boolean
* @indent : indent level (2 spaces each)
* @src : source file/func name (or NULL)
* @line : line number
*/
void debug(u32 level, bool timestamp, u32 indent, const char *src,
u32 line, const char *fmt, ...)
{
if (level > debug_level)
return;
va_list ap;
if (indent)
printf("%*s", 2*(indent-1), "");
if (timestamp) {
s64 diff = timer_elapsed();
printf("%ld.%03ld ", diff/NANOSEC, (diff/1000000)%1000);
printf("%010ld ", diff);
}
if (src) {
if (line)
printf("[%s:%u] ", src, line);
else
printf("[%s] ", src);
}
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
}
#ifdef BIN_debug
#include <unistd.h>
int main()
{
int foo=1;
debug_init(5);
log(0, "log0=%d\n", foo++);
log(1, "log1=%d\n", foo++);
log(2, "log2=%d\n", foo++);
log_i(2, "log_i 2=%d\n", foo++);
log_i(5, "log_i 5=%d\n", foo++);
log_i(6, "log_i 6=%d\n", foo++);
log_it(4, "log_it 4=%d\n", foo++);
log_f(1, "log_f 5=%d\n", foo++);
}
#endif

View File

@@ -1,84 +0,0 @@
/* debug.h - debug/log management.
*
* Copyright (C) 2021-2022 Bruno Raoult ("br")
* Licensed under the GNU General Public License v3.0 or later.
* Some rights reserved. See COPYING.
*
* You should have received a copy of the GNU General Public License along with this
* program. If not, see <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
*
* SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
*
*/
#ifndef DEBUG_H
#define DEBUG_H
#include <stdbool.h>
#include <stdint.h>
#include "bits.h"
#define _unused __attribute__((__unused__))
#define _printf __attribute__ ((format (printf, 6, 7)))
#ifdef DEBUG_DEBUG
void debug_init(u32 level);
void debug_level_set(u32 level);
void _printf debug(u32 level, bool timestamp,
u32 indent, const char *src,
u32 line, const char *, ...);
#else /* DEBUG_DEBUG */
static inline void debug_init(_unused u32 level) {}
static inline void debug_level_set(_unused u32 level) {}
static inline void _printf debug(_unused u32 level, _unused bool timestamp,
_unused u32 indent, _unused const char *src,
_unused u32 line, const char *, ...) {}
#endif /* DEBUG_DEBUG */
#undef _unused
#undef _printf
/* format: only printf
*/
#define log(level, fmt, args...) \
debug((level), false, 0, NULL, 0, fmt, ##args)
/* format : indent, no func name, no timestamp
* >>>>val=2
*/
#define log_i(level, fmt, args...) \
debug((level), false, (level), NULL, 0, fmt, ##args)
/* format : func name, no indent, no timestamp
* [foo] val=2
*/
#define log_f(level, fmt, args...) \
debug((level), false, 0, __func__, 0, fmt, ##args)
/* format : func name, no indent, no timestamp
* >>>> [foo:15] val=2
*/
#define log_if(level, fmt, args...) \
debug((level), false, (level), __func__, __LINE__, fmt, ##args)
/* format : func name, indent, timestamp
* >>>>foo:15 val=2
*/
#define log_it(level, fmt, args...) \
debug((level), true, (level), __func__, __LINE__, fmt, ##args)
/* format: file name, no indent, no timestamp
* foo:15 val=2
*
* #define log_f(level, fmt, args...) \
* debug((level), false, 0, __FILE__, __LINE__, fmt, args)
*/
#else
#define log(level, fmt, args...)
#define log_i(...)
#define log_f(...)
#define log_if(...)
#define log_it(...)
#define log_f(...)
#endif /* DEBUG_H */

997
c/list.h
View File

@@ -1,997 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* adaptation of kernel's <linux/list.h>
* Main change is that I don't use READ_ONCE and WRITE_ONCE
* See https://www.kernel.org/doc/Documentation/memory-barriers.txt
*/
#ifndef __BR_LIST_H
#define __BR_LIST_H
#include <stddef.h>
#include <stdbool.h>
#include "rwonce.h"
/************ originally in <include/linux/types.h> */
struct list_head {
struct list_head *next, *prev;
};
struct hlist_head {
struct hlist_node *first;
};
struct hlist_node {
struct hlist_node *next, **pprev;
};
/************ originally in <include/linux/poison.h> */
# define POISON_POINTER_DELTA 0
/* These are non-NULL pointers that will result in page faults
* under normal circumstances, used to verify that nobody uses
* non-initialized list entries.
*/
#define LIST_POISON1 ((void *) 0x100 + POISON_POINTER_DELTA)
#define LIST_POISON2 ((void *) 0x200 + POISON_POINTER_DELTA)
/************ originally in <include/linux/kernel.h> */
#define container_of(ptr, type, member) ({ \
void *__mptr = (void *)(ptr); \
((type *)(__mptr - offsetof(type, member))); })
/*
* Circular doubly linked list implementation.
*
* Some of the internal functions ("__xxx") are useful when
* manipulating whole lists rather than single entries, as
* sometimes we already know the next/prev entries and we can
* generate better code by using them directly rather than
* using the generic single-entry routines.
*/
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
/**
* INIT_LIST_HEAD - Initialize a list_head structure
* @list: list_head structure to be initialized.
*
* Initializes the list_head to point to itself. If it is a list header,
* the result is an empty list.
*/
static inline void INIT_LIST_HEAD(struct list_head *list)
{
WRITE_ONCE(list->next, list);
list->prev = list;
}
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
WRITE_ONCE(prev->next, new);
}
/**
* list_add - add a new entry
* @new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
/**
* list_add_tail - add a new entry
* @new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
/*
* Delete a list entry by making the prev/next entries
* point to each other.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
WRITE_ONCE(prev->next, next);
}
/*
* Delete a list entry and clear the 'prev' pointer.
*
* This is a special-purpose list clearing method used in the networking code
* for lists allocated as per-cpu, where we don't want to incur the extra
* WRITE_ONCE() overhead of a regular list_del_init(). The code that uses this
* needs to check the node 'prev' pointer instead of calling list_empty().
*/
static inline void __list_del_clearprev(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->prev = NULL;
}
static inline void __list_del_entry(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
/**
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
* Note: list_empty() on entry does not return true after this, the entry is
* in an undefined state.
*/
static inline void list_del(struct list_head *entry)
{
__list_del_entry(entry);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
/**
* list_replace - replace old entry by new one
* @old : the element to be replaced
* @new : the new element to insert
*
* If @old was empty, it will be overwritten.
*/
static inline void list_replace(struct list_head *old,
struct list_head *new)
{
new->next = old->next;
new->next->prev = new;
new->prev = old->prev;
new->prev->next = new;
}
/**
* list_replace_init - replace old entry by new one and initialize the old one
* @old : the element to be replaced
* @new : the new element to insert
*
* If @old was empty, it will be overwritten.
*/
static inline void list_replace_init(struct list_head *old,
struct list_head *new)
{
list_replace(old, new);
INIT_LIST_HEAD(old);
}
/**
* list_swap - replace entry1 with entry2 and re-add entry1 at entry2's position
* @entry1: the location to place entry2
* @entry2: the location to place entry1
*/
static inline void list_swap(struct list_head *entry1,
struct list_head *entry2)
{
struct list_head *pos = entry2->prev;
list_del(entry2);
list_replace(entry1, entry2);
if (pos == entry1)
pos = entry2;
list_add(entry1, pos);
}
/**
* list_del_init - deletes entry from list and reinitialize it.
* @entry: the element to delete from the list.
*/
static inline void list_del_init(struct list_head *entry)
{
__list_del_entry(entry);
INIT_LIST_HEAD(entry);
}
/**
* list_move - delete from one list and add as another's head
* @list: the entry to move
* @head: the head that will precede our entry
*/
static inline void list_move(struct list_head *list, struct list_head *head)
{
__list_del_entry(list);
list_add(list, head);
}
/**
* list_move_tail - delete from one list and add as another's tail
* @list: the entry to move
* @head: the head that will follow our entry
*/
static inline void list_move_tail(struct list_head *list,
struct list_head *head)
{
__list_del_entry(list);
list_add_tail(list, head);
}
/**
* list_bulk_move_tail - move a subsection of a list to its tail
* @head: the head that will follow our entry
* @first: first entry to move
* @last: last entry to move, can be the same as first
*
* Move all entries between @first and including @last before @head.
* All three entries must belong to the same linked list.
*/
static inline void list_bulk_move_tail(struct list_head *head,
struct list_head *first,
struct list_head *last)
{
first->prev->next = last->next;
last->next->prev = first->prev;
head->prev->next = first;
first->prev = head->prev;
last->next = head;
head->prev = last;
}
/**
* list_is_first -- tests whether @list is the first entry in list @head
* @list: the entry to test
* @head: the head of the list
*/
static inline int list_is_first(const struct list_head *list,
const struct list_head *head)
{
return list->prev == head;
}
/**
* list_is_last - tests whether @list is the last entry in list @head
* @list: the entry to test
* @head: the head of the list
*/
static inline int list_is_last(const struct list_head *list,
const struct list_head *head)
{
return list->next == head;
}
/**
* list_empty - tests whether a list is empty
* @head: the list to test.
*/
static inline int list_empty(const struct list_head *head)
{
return READ_ONCE(head->next) == head;
}
/**
* list_rotate_left - rotate the list to the left
* @head: the head of the list
*/
static inline void list_rotate_left(struct list_head *head)
{
struct list_head *first;
if (!list_empty(head)) {
first = head->next;
list_move_tail(first, head);
}
}
/**
* list_rotate_to_front() - Rotate list to specific item.
* @list: The desired new front of the list.
* @head: The head of the list.
*
* Rotates list so that @list becomes the new front of the list.
*/
static inline void list_rotate_to_front(struct list_head *list,
struct list_head *head)
{
/*
* Deletes the list head from the list denoted by @head and
* places it as the tail of @list, this effectively rotates the
* list so that @list is at the front.
*/
list_move_tail(head, list);
}
/**
* list_is_singular - tests whether a list has just one entry.
* @head: the list to test.
*/
static inline int list_is_singular(const struct list_head *head)
{
return !list_empty(head) && (head->next == head->prev);
}
static inline void __list_cut_position(struct list_head *list,
struct list_head *head, struct list_head *entry)
{
struct list_head *new_first = entry->next;
list->next = head->next;
list->next->prev = list;
list->prev = entry;
entry->next = list;
head->next = new_first;
new_first->prev = head;
}
/**
* list_cut_position - cut a list into two
* @list: a new list to add all removed entries
* @head: a list with entries
* @entry: an entry within head, could be the head itself
* and if so we won't cut the list
*
* This helper moves the initial part of @head, up to and
* including @entry, from @head to @list. You should
* pass on @entry an element you know is on @head. @list
* should be an empty list or a list you do not care about
* losing its data.
*
*/
static inline void list_cut_position(struct list_head *list,
struct list_head *head, struct list_head *entry)
{
if (list_empty(head))
return;
if (list_is_singular(head) &&
(head->next != entry && head != entry))
return;
if (entry == head)
INIT_LIST_HEAD(list);
else
__list_cut_position(list, head, entry);
}
/**
* list_cut_before - cut a list into two, before given entry
* @list: a new list to add all removed entries
* @head: a list with entries
* @entry: an entry within head, could be the head itself
*
* This helper moves the initial part of @head, up to but
* excluding @entry, from @head to @list. You should pass
* in @entry an element you know is on @head. @list should
* be an empty list or a list you do not care about losing
* its data.
* If @entry == @head, all entries on @head are moved to
* @list.
*/
static inline void list_cut_before(struct list_head *list,
struct list_head *head,
struct list_head *entry)
{
if (head->next == entry) {
INIT_LIST_HEAD(list);
return;
}
list->next = head->next;
list->next->prev = list;
list->prev = entry->prev;
list->prev->next = list;
head->next = entry;
entry->prev = head;
}
static inline void __list_splice(const struct list_head *list,
struct list_head *prev,
struct list_head *next)
{
struct list_head *first = list->next;
struct list_head *last = list->prev;
first->prev = prev;
prev->next = first;
last->next = next;
next->prev = last;
}
/**
* list_splice - join two lists, this is designed for stacks
* @list: the new list to add.
* @head: the place to add it in the first list.
*/
static inline void list_splice(const struct list_head *list,
struct list_head *head)
{
if (!list_empty(list))
__list_splice(list, head, head->next);
}
/**
* list_splice_tail - join two lists, each list being a queue
* @list: the new list to add.
* @head: the place to add it in the first list.
*/
static inline void list_splice_tail(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list))
__list_splice(list, head->prev, head);
}
/**
* list_splice_init - join two lists and reinitialise the emptied list.
* @list: the new list to add.
* @head: the place to add it in the first list.
*
* The list at @list is reinitialised
*/
static inline void list_splice_init(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list)) {
__list_splice(list, head, head->next);
INIT_LIST_HEAD(list);
}
}
/**
* list_splice_tail_init - join two lists and reinitialise the emptied list
* @list: the new list to add.
* @head: the place to add it in the first list.
*
* Each of the lists is a queue.
* The list at @list is reinitialised
*/
static inline void list_splice_tail_init(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list)) {
__list_splice(list, head->prev, head);
INIT_LIST_HEAD(list);
}
}
/**
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
/**
* list_first_entry - get the first element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*
* Note, that list is expected to be not empty.
*/
#define list_first_entry(ptr, type, member) \
list_entry((ptr)->next, type, member)
/**
* list_last_entry - get the last element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*
* Note, that list is expected to be not empty.
*/
#define list_last_entry(ptr, type, member) \
list_entry((ptr)->prev, type, member)
/**
* list_first_entry_or_null - get the first element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*
* Note that if the list is empty, it returns NULL.
*/
#define list_first_entry_or_null(ptr, type, member) ({ \
struct list_head *head__ = (ptr); \
struct list_head *pos__ = READ_ONCE(head__->next); \
pos__ != head__ ? list_entry(pos__, type, member) : NULL; \
})
/**
* list_next_entry - get the next element in list
* @pos: the type * to cursor
* @member: the name of the list_head within the struct.
*/
#define list_next_entry(pos, member) \
list_entry((pos)->member.next, __typeof__(*(pos)), member)
/**
* list_prev_entry - get the prev element in list
* @pos: the type * to cursor
* @member: the name of the list_head within the struct.
*/
#define list_prev_entry(pos, member) \
list_entry((pos)->member.prev, __typeof__(*(pos)), member)
/**
* list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop cursor.
* @head: the head for your list.
*/
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
/**
* list_for_each_continue - continue iteration over a list
* @pos: the &struct list_head to use as a loop cursor.
* @head: the head for your list.
*
* Continue to iterate over a list, continuing after the current position.
*/
#define list_for_each_continue(pos, head) \
for (pos = pos->next; pos != (head); pos = pos->next)
/**
* list_for_each_prev - iterate over a list backwards
* @pos: the &struct list_head to use as a loop cursor.
* @head: the head for your list.
*/
#define list_for_each_prev(pos, head) \
for (pos = (head)->prev; pos != (head); pos = pos->prev)
/**
* list_for_each_safe - iterate over a list safe against removal of list entry
* @pos: the &struct list_head to use as a loop cursor.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
*/
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
/**
* list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
* @pos: the &struct list_head to use as a loop cursor.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
*/
#define list_for_each_prev_safe(pos, n, head) \
for (pos = (head)->prev, n = pos->prev; \
pos != (head); \
pos = n, n = pos->prev)
/**
* list_entry_is_head - test if the entry points to the head of the list
* @pos: the type * to cursor
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_entry_is_head(pos, head, member) \
(&pos->member == (head))
/**
* list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_for_each_entry(pos, head, member) \
for (pos = list_first_entry(head, __typeof__(*pos), member); \
!list_entry_is_head(pos, head, member); \
pos = list_next_entry(pos, member))
/**
* list_for_each_entry_reverse - iterate backwards over list of given type.
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_for_each_entry_reverse(pos, head, member) \
for (pos = list_last_entry(head, __typeof__(*pos), member); \
!list_entry_is_head(pos, head, member); \
pos = list_prev_entry(pos, member))
/**
* list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
* @pos: the type * to use as a start point
* @head: the head of the list
* @member: the name of the list_head within the struct.
*
* Prepares a pos entry for use as a start point in list_for_each_entry_continue().
*/
#define list_prepare_entry(pos, head, member) \
((pos) ? : list_entry(head, __typeof__(*pos), member))
/**
* list_for_each_entry_continue - continue iteration over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Continue to iterate over list of given type, continuing after
* the current position.
*/
#define list_for_each_entry_continue(pos, head, member) \
for (pos = list_next_entry(pos, member); \
!list_entry_is_head(pos, head, member); \
pos = list_next_entry(pos, member))
/**
* list_for_each_entry_continue_reverse - iterate backwards from the given point
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Start to iterate over list of given type backwards, continuing after
* the current position.
*/
#define list_for_each_entry_continue_reverse(pos, head, member) \
for (pos = list_prev_entry(pos, member); \
!list_entry_is_head(pos, head, member); \
pos = list_prev_entry(pos, member))
/**
* list_for_each_entry_from - iterate over list of given type from the current point
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Iterate over list of given type, continuing from current position.
*/
#define list_for_each_entry_from(pos, head, member) \
for (; !list_entry_is_head(pos, head, member); \
pos = list_next_entry(pos, member))
/**
* list_for_each_entry_from_reverse - iterate backwards over list of given type
* from the current point
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Iterate backwards over list of given type, continuing from current position.
*/
#define list_for_each_entry_from_reverse(pos, head, member) \
for (; !list_entry_is_head(pos, head, member); \
pos = list_prev_entry(pos, member))
/**
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_first_entry(head, __typeof__(*pos), member), \
n = list_next_entry(pos, member); \
!list_entry_is_head(pos, head, member); \
pos = n, n = list_next_entry(n, member))
/**
* list_for_each_entry_safe_continue - continue list iteration safe against removal
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Iterate over list of given type, continuing after current point,
* safe against removal of list entry.
*/
#define list_for_each_entry_safe_continue(pos, n, head, member) \
for (pos = list_next_entry(pos, member), \
n = list_next_entry(pos, member); \
!list_entry_is_head(pos, head, member); \
pos = n, n = list_next_entry(n, member))
/**
* list_for_each_entry_safe_from - iterate over list from current point safe against removal
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Iterate over list of given type from current point, safe against
* removal of list entry.
*/
#define list_for_each_entry_safe_from(pos, n, head, member) \
for (n = list_next_entry(pos, member); \
!list_entry_is_head(pos, head, member); \
pos = n, n = list_next_entry(n, member))
/**
* list_for_each_entry_safe_reverse - iterate backwards over list safe against removal
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Iterate backwards over list of given type, safe against removal
* of list entry.
*/
#define list_for_each_entry_safe_reverse(pos, n, head, member) \
for (pos = list_last_entry(head, __typeof__(*pos), member), \
n = list_prev_entry(pos, member); \
!list_entry_is_head(pos, head, member); \
pos = n, n = list_prev_entry(n, member))
/**
* list_safe_reset_next - reset a stale list_for_each_entry_safe loop
* @pos: the loop cursor used in the list_for_each_entry_safe loop
* @n: temporary storage used in list_for_each_entry_safe
* @member: the name of the list_head within the struct.
*
* list_safe_reset_next is not safe to use in general if the list may be
* modified concurrently (eg. the lock is dropped in the loop body). An
* exception to this is if the cursor element (pos) is pinned in the list,
* and list_safe_reset_next is called after re-taking the lock and before
* completing the current iteration of the loop body.
*/
#define list_safe_reset_next(pos, n, member) \
n = list_next_entry(pos, member)
/*
* Double linked lists with a single pointer list head.
* Mostly useful for hash tables where the two pointer list head is
* too wasteful.
* You lose the ability to access the tail in O(1).
*/
#define HLIST_HEAD_INIT { .first = NULL }
#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
static inline void INIT_HLIST_NODE(struct hlist_node *h)
{
h->next = NULL;
h->pprev = NULL;
}
/**
* hlist_unhashed - Has node been removed from list and reinitialized?
* @h: Node to be checked
*
* Not that not all removal functions will leave a node in unhashed
* state. For example, hlist_nulls_del_init_rcu() does leave the
* node in unhashed state, but hlist_nulls_del() does not.
*/
static inline int hlist_unhashed(const struct hlist_node *h)
{
return !h->pprev;
}
/**
* hlist_unhashed_lockless - Version of hlist_unhashed for lockless use
* @h: Node to be checked
*
* This variant of hlist_unhashed() must be used in lockless contexts
* to avoid potential load-tearing. The READ_ONCE() is paired with the
* various WRITE_ONCE() in hlist helpers that are defined below.
*/
static inline int hlist_unhashed_lockless(const struct hlist_node *h)
{
return !READ_ONCE(h->pprev);
}
/**
* hlist_empty - Is the specified hlist_head structure an empty hlist?
* @h: Structure to check.
*/
static inline int hlist_empty(const struct hlist_head *h)
{
return !READ_ONCE(h->first);
}
static inline void __hlist_del(struct hlist_node *n)
{
struct hlist_node *next = n->next;
struct hlist_node **pprev = n->pprev;
WRITE_ONCE(*pprev, next);
if (next)
WRITE_ONCE(next->pprev, pprev);
}
/**
* hlist_del - Delete the specified hlist_node from its list
* @n: Node to delete.
*
* Note that this function leaves the node in hashed state. Use
* hlist_del_init() or similar instead to unhash @n.
*/
static inline void hlist_del(struct hlist_node *n)
{
__hlist_del(n);
n->next = LIST_POISON1;
n->pprev = LIST_POISON2;
}
/**
* hlist_del_init - Delete the specified hlist_node from its list and initialize
* @n: Node to delete.
*
* Note that this function leaves the node in unhashed state.
*/
static inline void hlist_del_init(struct hlist_node *n)
{
if (!hlist_unhashed(n)) {
__hlist_del(n);
INIT_HLIST_NODE(n);
}
}
/**
* hlist_add_head - add a new entry at the beginning of the hlist
* @n: new entry to be added
* @h: hlist head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
{
struct hlist_node *first = h->first;
WRITE_ONCE(n->next, first);
if (first)
WRITE_ONCE(first->pprev, &n->next);
WRITE_ONCE(h->first, n);
WRITE_ONCE(n->pprev, &h->first);
}
/**
* hlist_add_before - add a new entry before the one specified
* @n: new entry to be added
* @next: hlist node to add it before, which must be non-NULL
*/
static inline void hlist_add_before(struct hlist_node *n,
struct hlist_node *next)
{
WRITE_ONCE(n->pprev, next->pprev);
WRITE_ONCE(n->next, next);
WRITE_ONCE(next->pprev, &n->next);
WRITE_ONCE(*(n->pprev), n);
}
/**
* hlist_add_behind - add a new entry after the one specified
* @n: new entry to be added
* @prev: hlist node to add it after, which must be non-NULL
*/
static inline void hlist_add_behind(struct hlist_node *n,
struct hlist_node *prev)
{
WRITE_ONCE(n->next, prev->next);
WRITE_ONCE(prev->next, n);
WRITE_ONCE(n->pprev, &prev->next);
if (n->next)
WRITE_ONCE(n->next->pprev, &n->next);
}
/**
* hlist_add_fake - create a fake hlist consisting of a single headless node
* @n: Node to make a fake list out of
*
* This makes @n appear to be its own predecessor on a headless hlist.
* The point of this is to allow things like hlist_del() to work correctly
* in cases where there is no list.
*/
static inline void hlist_add_fake(struct hlist_node *n)
{
n->pprev = &n->next;
}
/**
* hlist_fake: Is this node a fake hlist?
* @h: Node to check for being a self-referential fake hlist.
*/
static inline bool hlist_fake(struct hlist_node *h)
{
return h->pprev == &h->next;
}
/**
* hlist_is_singular_node - is node the only element of the specified hlist?
* @n: Node to check for singularity.
* @h: Header for potentially singular list.
*
* Check whether the node is the only node of the head without
* accessing head, thus avoiding unnecessary cache misses.
*/
static inline bool
hlist_is_singular_node(struct hlist_node *n, struct hlist_head *h)
{
return !n->next && n->pprev == &h->first;
}
/**
* hlist_move_list - Move an hlist
* @old: hlist_head for old list.
* @new: hlist_head for new list.
*
* Move a list from one list head to another. Fixup the pprev
* reference of the first entry if it exists.
*/
static inline void hlist_move_list(struct hlist_head *old,
struct hlist_head *new)
{
new->first = old->first;
if (new->first)
new->first->pprev = &new->first;
old->first = NULL;
}
#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
#define hlist_for_each(pos, head) \
for (pos = (head)->first; pos ; pos = pos->next)
#define hlist_for_each_safe(pos, n, head) \
for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
pos = n)
#define hlist_entry_safe(ptr, type, member) \
({ __typeof__(ptr) ____ptr = (ptr); \
____ptr ? hlist_entry(____ptr, type, member) : NULL; \
})
/**
* hlist_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry(pos, head, member) \
for (pos = hlist_entry_safe((head)->first, __typeof__(*(pos)), member); \
pos; \
pos = hlist_entry_safe((pos)->member.next, __typeof__(*(pos)), member))
/**
* hlist_for_each_entry_continue - iterate over a hlist continuing after current point
* @pos: the type * to use as a loop cursor.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_continue(pos, member) \
for (pos = hlist_entry_safe((pos)->member.next, __typeof__(*(pos)), member); \
pos; \
pos = hlist_entry_safe((pos)->member.next, __typeof__(*(pos)), member))
/**
* hlist_for_each_entry_from - iterate over a hlist continuing from current point
* @pos: the type * to use as a loop cursor.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_from(pos, member) \
for (; pos; \
pos = hlist_entry_safe((pos)->member.next, __typeof__(*(pos)), member))
/**
* hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop cursor.
* @n: a &struct hlist_node to use as temporary storage
* @head: the head for your list.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_safe(pos, n, head, member) \
for (pos = hlist_entry_safe((head)->first, __typeof__(*pos), member); \
pos && ({ n = pos->member.next; 1; }); \
pos = hlist_entry_safe(n, __typeof__(*pos), member))
#endif /* __BR_LIST_H */

225
c/pool.c
View File

@@ -1,225 +0,0 @@
/* pool.c - A simple pool manager.
*
* Copyright (C) 2021 Bruno Raoult ("br")
* Licensed under the GNU General Public License v3.0 or later.
* Some rights reserved. See COPYING.
*
* You should have received a copy of the GNU General Public License along with this
* program. If not, see <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
*
* SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
*
*/
#include <stddef.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include "list.h"
#include "pool.h"
#include "debug.h"
#include "bits.h"
void pool_stats(pool_t *pool)
{
if (pool) {
# ifdef DEBUG_POOL
block_t *block;
log_f(1, "[%s] pool [%p]: blocks:%u avail:%u alloc:%u grow:%u eltsize:%lu\n",
pool->name, (void *)pool, pool->nblocks, pool->available, pool->allocated,
pool->growsize, pool->eltsize);
log(5, "\tblocks: ");
list_for_each_entry(block, &pool->list_blocks, list_blocks) {
log(5, "%p ", block);
}
log(5, "\n");
# endif
}
}
pool_t *pool_create(const char *name, u32 growsize, size_t eltsize)
{
pool_t *pool;
# ifdef DEBUG_POOL
log_f(1, "name=[%s] growsize=%u eltsize=%lu\n",
name, growsize, eltsize);
# endif
/* we need at least sizeof(struct list_head) space in pool elements
*/
if (eltsize < sizeof (struct list_head)) {
# ifdef DEBUG_POOL
log_f(1, "[%s]: structure size too small (%lu < %lu), adjusting to %lu.\n",
name, eltsize, sizeof(struct list_head), sizeof(struct list_head));
# endif
eltsize = sizeof(struct list_head);
}
if ((pool = malloc(sizeof (*pool)))) {
strncpy(pool->name, name, POOL_NAME_LENGTH - 1);
pool->name[POOL_NAME_LENGTH - 1] = 0;
pool->growsize = growsize;
pool->eltsize = eltsize;
pool->available = 0;
pool->allocated = 0;
pool->nblocks = 0;
INIT_LIST_HEAD(&pool->list_available);
INIT_LIST_HEAD(&pool->list_blocks);
}
return pool;
}
static u32 _pool_add(pool_t *pool, struct list_head *elt)
{
# ifdef DEBUG_POOL
log_f(6, "pool=%p &head=%p elt=%p off1=%lu off2=%lu\n",
(void *)pool,
(void *)&pool->list_available,
(void *)elt,
(void *)&pool->list_available-(void *)pool,
offsetof(pool_t, list_available));
# endif
list_add(elt, &pool->list_available);
return ++pool->available;
}
u32 pool_add(pool_t *pool, void *elt)
{
return _pool_add(pool, elt);
}
static struct list_head *_pool_get(pool_t *pool)
{
struct list_head *res = pool->list_available.next;
pool->available--;
list_del(res);
return res;
}
void *pool_get(pool_t *pool)
{
if (!pool)
return NULL;
if (!pool->available) {
block_t *block = malloc(sizeof(block_t) + pool->eltsize * pool->growsize);
void *cur;
u32 i;
if (!block) {
# ifdef DEBUG_POOL
log_f(1, "[%s]: failed block allocation\n", pool->name);
# endif
errno = ENOMEM;
return NULL;
}
/* maintain list of allocated blocks
*/
list_add(&block->list_blocks, &pool->list_blocks);
pool->nblocks++;
# ifdef DEBUG_POOL
log_f(1, "[%s]: growing pool from %u to %u elements. block=%p nblocks=%u\n",
pool->name,
pool->allocated,
pool->allocated + pool->growsize,
block,
pool->nblocks);
# endif
pool->allocated += pool->growsize;
for (i = 0; i < pool->growsize; ++i) {
cur = block->data + i * pool->eltsize;
# ifdef DEBUG_POOL
log_f(7, "alloc=%p cur=%p\n", block, cur);
# endif
_pool_add(pool, (struct list_head *)cur);
}
//pool_stats(pool);
}
/* this is the effective address if the object (and also the
* pool list_head address)
*/
return _pool_get(pool);
}
void pool_destroy(pool_t *pool)
{
block_t *block, *tmp;
if (!pool)
return;
/* release memory blocks */
# ifdef DEBUG_POOL
log_f(1, "[%s]: releasing %d blocks and main structure\n", pool->name, pool->nblocks);
log(5, "blocks:");
# endif
list_for_each_entry_safe(block, tmp, &pool->list_blocks, list_blocks) {
list_del(&block->list_blocks);
free(block);
# ifdef DEBUG_POOL
log(5, " %p", block);
# endif
}
# ifdef DEBUG_POOL
log(5, "\n");
# endif
free(pool);
}
#ifdef BIN_pool
struct d {
u16 data1;
char c;
struct list_head list;
};
static LIST_HEAD (head);
int main(int ac, char**av)
{
pool_t *pool;
int total;
int action=0;
u16 icur=0;
char ccur='z';
struct d *elt;
debug_init(3);
log_f(1, "%s: sizeof(d)=%lu sizeof(*d)=%lu off=%lu\n", *av, sizeof(elt),
sizeof(*elt), offsetof(struct d, list));
if ((pool = pool_create("dummy", 3, sizeof(*elt)))) {
pool_stats(pool);
for (int cur=1; cur<ac; ++cur) {
total = atoi(av[cur]);
if (action == 0) { /* add elt to list */
log_f(2, "adding %d elements\n", total);
for (int i = 0; i < total; ++i) {
elt = pool_get(pool);
elt->data1 = icur++;
elt->c = ccur--;
list_add(&elt->list, &head);
}
pool_stats(pool);
action = 1;
} else { /* remove one elt from list */
log_f(2, "deleting %d elements\n", total);
for (int i = 0; i < total; ++i) {
if (!list_empty(&head)) {
elt = list_last_entry(&head, struct d, list);
printf("elt=[%d, %c]\n", elt->data1, elt->c);
list_del(&elt->list);
pool_add(pool, elt);
}
}
pool_stats(pool);
action = 0;
}
}
}
pool_stats(pool);
}
#endif

View File

@@ -1,77 +0,0 @@
/* pool.h - A simple memory pool manager.
*
* Copyright (C) 2021 Bruno Raoult ("br")
* Licensed under the GNU General Public License v3.0 or later.
* Some rights reserved. See COPYING.
*
* You should have received a copy of the GNU General Public License along with this
* program. If not, see <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
*
* SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
*
*/
#ifndef POOL_H
#define POOL_H
#include <stdint.h>
#include <stddef.h>
#include "list.h"
#include "bits.h"
#define POOL_NAME_LENGTH (16) /* max name length including trailing \0 */
typedef struct {
struct list_head list_blocks; /* list of allocated blocks in pool */
char data[]; /* objects block */
} block_t;
typedef struct {
char name[POOL_NAME_LENGTH]; /* pool name */
u32 available; /* current available elements */
u32 allocated; /* total objects allocated */
u32 growsize; /* number of objects per block allocated */
size_t eltsize; /* object size */
u32 nblocks; /* number of blocks allocated */
struct list_head list_available; /* available nodes */
struct list_head list_blocks; /* allocated blocks */
} pool_t;
/**
* pool_stats - display some pool statistics
* @pool: the pool address.
*/
void pool_stats(pool_t *pool);
/**
* pool_create - create a new memory pool
* @name: the name to give to the pool.
* @grow: the number of elements to add when no more available.
* @size: the size of an element in pool.
*/
pool_t *pool_create(const char *name, u32 grow, size_t size);
/**
* pool_get - get an element from a pool
* @pool: the pool address.
*/
void *pool_get(pool_t *pool);
/**
* pool_add - add (release) an element to a pool
* @pool: the pool address.
* @elt: the address of the object to add to the pool.
*/
u32 pool_add(pool_t *pool, void *elt);
/**
* pool_destroy - destroy a pool.
* @pool: the pool address.
*
* Attention: All memory is freed, but no check is done whether all pool
* elements has been released. Referencing any pool object after this call
* is strongly discouraged.
*/
void pool_destroy(pool_t *pool);
#endif

View File

@@ -1,124 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* adaptation of kernel's <asm-generic/rwonce.h>
* See https://www.kernel.org/doc/Documentation/memory-barriers.txt
*/
/*
* Prevent the compiler from merging or refetching reads or writes. The
* compiler is also forbidden from reordering successive instances of
* READ_ONCE and WRITE_ONCE, but only when the compiler is aware of some
* particular ordering. One way to make the compiler aware of ordering is to
* put the two invocations of READ_ONCE or WRITE_ONCE in different C
* statements.
*
* These two macros will also work on aggregate data types like structs or
* unions.
*
* Their two major use cases are: (1) Mediating communication between
* process-level code and irq/NMI handlers, all running on the same CPU,
* and (2) Ensuring that the compiler does not fold, spindle, or otherwise
* mutilate accesses that either do not require ordering or that interact
* with an explicit memory barrier or atomic instruction that provides the
* required ordering.
*/
#ifndef __BR_RWONCE_H
#define __BR_RWONCE_H
/************ originally in <include/linux/compiler_types.h> */
# define __compiletime_error(message) __attribute__((error(message)))
/************ originally in <include/linux/compiler_types.h> */
/*
* __unqual_scalar_typeof(x) - Declare an unqualified scalar type, leaving
* non-scalar types unchanged.
*/
/*
* Prefer C11 _Generic for better compile-times and simpler code. Note: 'char'
* is not type-compatible with 'signed char', and we define a separate case.
*/
#define __scalar_type_to_expr_cases(type) \
unsigned type: (unsigned type)0, \
signed type: (signed type)0
#define __unqual_scalar_typeof(x) \
typeof(_Generic((x), \
char: (char)0, \
__scalar_type_to_expr_cases(char), \
__scalar_type_to_expr_cases(short), \
__scalar_type_to_expr_cases(int), \
__scalar_type_to_expr_cases(long), \
__scalar_type_to_expr_cases(long long), \
default: (x)))
/* Is this type a native word size -- useful for atomic operations */
#define __native_word(t) \
(sizeof(t) == sizeof(char) || sizeof(t) == sizeof(short) || \
sizeof(t) == sizeof(int) || sizeof(t) == sizeof(long))
#ifdef __OPTIMIZE__
# define __compiletime_assert(condition, msg, prefix, suffix) \
do { \
extern void prefix ## suffix(void) __compiletime_error(msg); \
if (!(condition)) \
prefix ## suffix(); \
} while (0)
#else
# define __compiletime_assert(condition, msg, prefix, suffix) do { } while (0)
#endif
#define _compiletime_assert(condition, msg, prefix, suffix) \
__compiletime_assert(condition, msg, prefix, suffix)
/**
* compiletime_assert - break build and emit msg if condition is false
* @condition: a compile-time constant condition to check
* @msg: a message to emit if condition is false
*
* In tradition of POSIX assert, this macro will break the build if the
* supplied condition is *false*, emitting the supplied error message if the
* compiler has support to do so.
*/
#define compiletime_assert(condition, msg) \
_compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
#define compiletime_assert_atomic_type(t) \
compiletime_assert(__native_word(t), \
"Need native word sized stores/loads for atomicity.")
/************ originally in <asm-generic/rwonce.h> */
/*
* Yes, this permits 64-bit accesses on 32-bit architectures. These will
* actually be atomic in some cases (namely Armv7 + LPAE), but for others we
* rely on the access being split into 2x32-bit accesses for a 32-bit quantity
* (e.g. a virtual address) and a strong prevailing wind.
*/
#define compiletime_assert_rwonce_type(t) \
compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long), \
"Unsupported access size for {READ,WRITE}_ONCE().")
/*
* Use __READ_ONCE() instead of READ_ONCE() if you do not require any
* atomicity. Note that this may result in tears!
*/
#ifndef __READ_ONCE
#define __READ_ONCE(x) (*(const volatile __unqual_scalar_typeof(x) *)&(x))
#endif
#define READ_ONCE(x) \
({ \
compiletime_assert_rwonce_type(x); \
__READ_ONCE(x); \
})
#define __WRITE_ONCE(x, val) \
do { \
*(volatile typeof(x) *)&(x) = (val); \
} while (0)
#define WRITE_ONCE(x, val) \
do { \
compiletime_assert_rwonce_type(x); \
__WRITE_ONCE(x, val); \
} while (0)
#endif /* __BR_RWONCE_H */

18
config/home/bash_profile Normal file
View File

@@ -0,0 +1,18 @@
#!/usr/bin/env bash
#
# ~/.bash_profile - bash login script.
#
# (C) Bruno Raoult ("br"), 2024
# Licensed under the GNU General Public License v3.0 or later.
# Some rights reserved. See COPYING.
#
# You should have received a copy of the GNU General Public License along with this
# program. If not, see <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
#
# For login shells, ~/.profile is executed. Debian default one does:
# 1) source .bashrc if it exists
# 2) add "$HOME"/bin in PATH
# This imply a duplicate "$HOME/bin" in PATH, as we do everything in .bashrc.$user.
# Having this ~/.bash_profile will avoid the execution of ~/.profile
[ -f "$HOME/.bashrc" ] && . "$HOME/.bashrc"

291
config/home/bashrc.br Normal file
View File

@@ -0,0 +1,291 @@
#!/usr/bin/env bash
#
# ~/.bashrc.br - user specific initialization
#
# (C) Bruno Raoult ("br"), 2001-2024
# Licensed under the GNU General Public License v3.0 or later.
# Some rights reserved. See COPYING.
#
# You should have received a copy of the GNU General Public License along with this
# program. If not, see <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
#
# SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
#
# Usage: to be invoked from .bashrc.
# i.e., add at the end of .bashrc:
# [ -f "$HOME/.bashrc.$USER" ] && . "$HOME/.bashrc.$USER"
#
# Debian default ~/.profile does:
# 1) source .bashrc if it exists
# 2) add "$HOME"/bin in PATH
# This imply a duplicate "$HOME/bin" in PATH, as we do everything here.
# Better to have a ~/.bash_profile with the lines above.
# _var_del() - remove an element from a colon-separated list.
# $1: name (reference) of a colon separated list
# $2: element to remove (string)
#
# _var_del() removes every occurrence of $2, if there are more than 1,
# and leaves $1 unchanged if $2 is not present.
#
# Example:
# With VAR's value being "foo:bar:quax:bar". Using "_var_del VAR bar" will
# leave VAR with the value "foo:quax".
_var_del() {
local -n _p_del=$1
local _l=":$_p_del:"
while [[ $_l =~ :$2: ]]; do
_l=${_l//:$2:/:}
done
_l=${_l%:}
_l=${_l#:}
_p_del="$_l"
}
# _var_prepend() - prepend element to colon-separated variable.
# $1: variable name (reference)
# $2: element to add (string)
#
# Any occurrence of $2 in $1 is first removed, then $2 is added at $1 beginning.
#
# Example:
# With VAR's value being "foo:bar:quax:bar". Using "_var_prepend VAR bar"
# will leave VAR with the value "bar:foo:quax".
_var_prepend() {
local -n _p_prepend=$1
_var_del _p_prepend "$2"
[[ -z $_p_prepend ]] && _p_prepend="$2" && return
_p_prepend="$2:$_p_prepend"
}
# _var_append() - append element to colon-separated variable.
# $1: variable name (reference)
# $2: element to add (string)
#
# Any occurrence of $2 in $1 is first removed, then $2 is added at $1 end.
#
# Example:
# With VAR's value being "foo:bar:quax:bar". Using "_var_append VAR bar"
# will leave VAR with the value "foo:quax:bar".
_var_append() {
local -n _p_append=$1
_var_del _p_append "$2"
[[ -z $_p_append ]] && _p_append="$2" && return
_p_append="$_p_append:$2"
}
# adjust PATH. Below paths will be added at beginning.
_lpath=("$HOME/bin/$(uname -s)-$(uname -m)"
"$HOME/bin"
#"$HOME/.cargo/bin"
"/usr/local/bin")
# loop array in reverse order. Note: We do not test for path existence and add it
# unconditionally, to avoid automounter interference.
for (( _i = ${#_lpath[@]} - 1; _i >= 0; --_i )); do
_var_prepend PATH "${_lpath[_i]}"
done
unset _lpath
# why is it in default Ubuntu path ?
_var_del PATH /snap/bin
# enable core file
ulimit -Sc 102400 # in 1024 bytes, 100Mb
# ... and set PAGER to less (for man(1) and others)
if hash less 2>/dev/null; then
export PAGER=less
# do not clear screen after "less", exit immediately if one page only
export LESS="-XFB"
# ... and just alias more... to less ;-)
alias more=less
fi
# no output split for dc and bc / make bc silent
export DC_LINE_LENGTH=0
export BC_LINE_LENGTH=0
export BC_ENV_ARGS=--quiet
# both ubuntu and debian assume we want colors if TERM contains "color"
# this is surely not true, as TERM is often forced by terminal emulator
# shellcheck disable=SC2154
PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
case "$TERM" in
xterm*|rxvt*)
PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"
;;
*)
;;
esac
# trim prompt path
export PROMPT_DIRTRIM=3
# find a suitable editor
e() {
$VISUAL "$@"
}
export -f e
if hash emacs 2>/dev/null; then
# uncomment below to use full emacs
#export EDITOR=emacs
# ... OR: uncomment below to use emacsclient
#export ALTERNATE_EDITOR="/usr/bin/emacs"
#export EDITOR="emacs.sh"
#alias emacs="emacs.sh"
export ALTERNATE_EDITOR=""
export VISUAL="emacsclient -c"
alias emacs="emacsclient -c"
#alias crontab="VISUAL=emacsclient crontab -e"
#alias crontab="emacs-crontab.sh"
else
# emacs clones, then vim/vi, then... whatever left.
_VISUALS=(zile jed mg e3em vim vi nano ed)
for e in "${_VISUALS[@]}"; do
if hash "$e" 2>/dev/null; then
export VISUAL="$e"
break
fi
done
unset _VISUALS
fi
export EDITOR=$VISUAL
# look for a pdf viewer
for _pdfviewer in qpdfview atril; do
if hash "$_pdfviewer" 2>/dev/null; then
# shellcheck disable=SC2139
alias acroread="$_pdfviewer"
break
fi
done
unset _pdfviewer
# append to the history file, don't overwrite it
shopt -s histappend
# write history after each command
export PROMPT_COMMAND="history -a"
# Add timestamp in history
export HISTTIMEFORMAT="%d/%m %H:%M "
# ignore history dups, delete all previous dups
export HISTCONTROL="ignorespace:ignoredups:erasedups"
# ignore these in history
export HISTIGNORE="history *:h:hl:hll:hlll"
# history size
HISTSIZE=5000
HISTFILESIZE=5000
# remove new stupid Debian "ls" quoting, and colors...
# Many complains, one of them:
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=813164#226
export QUOTING_STYLE=literal
[[ -v BASH_ALIASES[ls] ]] && unalias ls
# avoid these stupid systemd defaults (horizontal scroll and pager)
alias systemctl="systemctl --no-pager --full"
# useful aliases/functions
alias l='ls -F'
alias ls='ls -F'
alias l1='ls -1F'
alias la='ls -AF'
alias ll='ls -lF'
alias lla='ls -lAF'
alias ldl='ls -l | grep ^d'
[[ -v BASH_ALIASES[lrt] ]] && unalias lrt
lrt() {
local -i _l=20
if (( $# > 0 )) && [[ $1 =~ [[:digit:]]+ ]]; then
_l="$1"
shift
fi
# shellcheck disable=2012
ls -lrt "${1:-.}" | tail -"$_l"
}
[[ -v BASH_ALIASES[lart] ]] && unalias lart
lart() {
local -i _l=20
if (( $# > 0 )) && [[ $1 =~ [[:digit:]]+ ]]; then
_l="$1"
shift
fi
# shellcheck disable=2012
ls -laFrt "${1:-.}" | tail -"$_l"
}
alias h="history 10" # short
alias hl="history 25" # long
alias hll="history 100" # very long
alias hlll="history" # all history
alias grep='grep --color=auto' # add colors to grep
alias fgrep='fgrep --color=auto'
alias egrep='egrep --color=auto'
# user temp directory
export USERTMP=~/tmp
# misc aliases
alias fuck='sudo $(history -p \!\!)'
alias diff='diff -u'
# fdiff() - compare two files with same name
# parameters:
# $1: first file
# $2: second file directory
#
# fdiff will compare (diff) $1 with a file of basename $1 in $2 directory.
# Examples:
# % fdiff .bashrc ~ # compare .bashrc with ~/.bashrc
# % fdiff /tmp/.bashrc /home/br/ # compare /tmp/.bashrc with /home/br/.bashrc
fdiff () {
local file1="$1" # file to compare
local file2="$2/${file1##*/}" # file2 with path
diff "$file1" "$file2"
}
# I am used to rehash...
# rehash - manage bash's remembered commands paths
# $1...: Only forget those commands
rehash() {
if (($#)); then
hash -d "$@"
else
hash -r
fi
}
# french-> english and english->french translation
alias trans="trans.sh"
alias rtrans="trans.sh -fen -tfr"
# easy directory sync (remove source trailing slash)
syncdir() {
local -a opts=(--archive --hard-links --one-file-system --itemize-changes --delete)
local src="$1" dst="$2"
case "$src" in
*[!/]*/)
src=${src%"${src##*[!/]}"};;
*[/])
src="/";;
esac
rsync "${opts[@]}" "$src" "$dst"
}
# host specific initialization
# shellcheck disable=SC1090
[ -f "$HOME/.bashrc.$USER.$(hostname)" ] && . "$HOME/.bashrc.$USER.$(hostname)"
# Indent style for emacs
# Local Variables:
# mode: shell-script
# sh-basic-offset: 4
# sh-indentation: 4
# indent-tabs-mode: nil
# End:

View File

@@ -0,0 +1,82 @@
#!/usr/bin/env bash
#
# ~/.bashrc.br.eowyn - host specific initialization
#
# (C) Bruno Raoult ("br"), 2001-2024
# Licensed under the GNU General Public License v3.0 or later.
# Some rights reserved. See COPYING.
#
# You should have received a copy of the GNU General Public License along with this
# program. If not, see <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
#
# SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
#
# Usage: to be invoked from .bashrc.$USER
# i.e., add at the end of .bashrc.$USER:
# [ -f "$HOME/.bashrc.$USER.$(hostname)" ] && . "$HOME/.bashrc.$USER.$(hostname)"
# mysql aliases. Will match any "[client-XXX]" lines in ~/.my.cnf
# and generate "myXXX" aliases.
if [[ -r ~/.my.cnf ]]; then
mapfile -t MYSQL_ARRAY < ~/.my.cnf
for line in "${MYSQL_ARRAY[@]}"; do
if [[ $line =~ ^\[client-(.+)\]$ ]]; then
SUFFIX="${BASH_REMATCH[1]}"
# shellcheck disable=SC2139,SC2140
alias my"$SUFFIX"="mysql --defaults-group-suffix=-$SUFFIX"
fi
done
fi
# shortcuts to commonly used directories/projects
# _vardir() - define common dirs vars & aliases
# $1: name variable to set
# $2: name of alias to define
# $3: script to source (relative to $2). '-': no script, '+': './script/env.sh'
# $4: project path
#
# _vardir() sets variable with $1 name to $4, and an alias with $2 name.
# The alias, when invoked, will:
# (1) change working directory to $1
# (2) source $3 when $3 is not '-'. $3 path can be relative (preferred), or
# absolute. If $3 is "+", it will default to "scripts/env.sh".
#
# Examples:
# _vardir MYDIR mydir - ~/foo/mydirprj
_vardir() {
local _v="$1" _a="$2" _s="$3" _p="$4"
if [[ ! -d $_p ]]; then
printf "ignored project: %s\n" "$_p"
return 0
fi
local _x="cd $_p"
export "$_v"="$_p"
case "$_s" in
-) ;;
+) _s="scripts/env.sh" ;&
*) if [[ -r "$_p/$_s" ]]; then
_x+="; . $_s"
else
printf "%s: ignored.\n" "$_p/$_s"
fi
esac
# shellcheck disable=SC2139
alias "$_a"="$_x"
}
#_vardir AOC aoc + ~/dev/advent-of-code # Advent of code
_vardir WCHESS wchess - ~/dev/www/crd/chess # raoult.com chess
_vardir CHESS chess + ~/dev/brchess # brchess
_vardir TOOLS tools - ~/dev/tools # tools
_vardir BRLIB brlib - ~/dev/brlib # brlib
#_vardir EUD eud + ~/dev/eudyptula # eudyptula
_vardir DEV dev - ~/dev # dev
# Indent style for emacs
# Local Variables:
# mode: shell-script
# sh-basic-offset: 4
# sh-indentation: 4
# indent-tabs-mode: nil
# End:

View File

@@ -0,0 +1,82 @@
#!/usr/bin/env bash
#
# ~/.bashrc.br.lorien - host specific initialization
#
# (C) Bruno Raoult ("br"), 2001-2024
# Licensed under the GNU General Public License v3.0 or later.
# Some rights reserved. See COPYING.
#
# You should have received a copy of the GNU General Public License along with this
# program. If not, see <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
#
# SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
#
# Usage: to be invoked from .bashrc.$USER
# i.e., add at the end of .bashrc.$USER:
# [ -f "$HOME/.bashrc.$USER.$(hostname)" ] && . "$HOME/.bashrc.$USER.$(hostname)"
# mysql aliases. Will match any "[client-XXX]" lines in ~/.my.cnf
# and generate "myXXX" aliases.
if [[ -r ~/.my.cnf ]]; then
mapfile -t MYSQL_ARRAY < ~/.my.cnf
for line in "${MYSQL_ARRAY[@]}"; do
if [[ $line =~ ^\[client-(.+)\]$ ]]; then
SUFFIX="${BASH_REMATCH[1]}"
# shellcheck disable=SC2139,SC2140
alias my"$SUFFIX"="mysql --defaults-group-suffix=-$SUFFIX"
fi
done
fi
# shortcuts to commonly used directories/projects
# _vardir() - define common dirs vars & aliases
# $1: name variable to set
# $2: name of alias to define
# $3: script to source (relative to $2). '-': no script, '+': './script/env.sh'
# $4: project path
#
# _vardir() sets variable with $1 name to $4, and an alias with $2 name.
# The alias, when invoked, will:
# (1) change working directory to $1
# (2) source $3 when $3 is not '-'. $3 path can be relative (preferred), or
# absolute. If $3 is "+", it will default to "scripts/env.sh".
#
# Examples:
# _vardir MYDIR mydir - ~/foo/mydirprj
_vardir() {
local _v="$1" _a="$2" _s="$3" _p="$4"
if [[ ! -d $_p ]]; then
printf "ignored project: %s\n" "$_p"
return 0
fi
local _x="cd $_p"
export "$_v"="$_p"
case "$_s" in
-) ;;
+) _s="scripts/env.sh" ;&
*) if [[ -r "$_p/$_s" ]]; then
_x+="; . $_s"
else
printf "%s: ignored.\n" "$_p/$_s"
fi
esac
# shellcheck disable=SC2139
alias "$_a"="$_x"
}
_vardir AOC aoc + ~/dev/advent-of-code # Advent of code
_vardir WCHESS wchess - ~/dev/www/crd/chess # raoult.com chess
_vardir CHESS chess + ~/dev/brchess # brchess
_vardir TOOLS tools - ~/dev/tools # tools
_vardir BRLIB brlib - ~/dev/brlib # brlib
_vardir EUD eud + ~/dev/eudyptula # eudyptula
_vardir DEV dev - ~/dev # dev
# Indent style for emacs
# Local Variables:
# mode: shell-script
# sh-basic-offset: 4
# sh-indentation: 4
# indent-tabs-mode: nil
# End:

View File

@@ -0,0 +1,58 @@
;; ~/.emacs.d/lorien.el
;;
;; emacs configuration - this file will be loaded only when emacs runs on lorien.
;;
;; br, 2010-2019
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; pre-load often-visited files
;; avoids calling this twice
(when (not (boundp 'my/eowyn-loaded))
;; I put mainfile (current project) in variable.
(setq
my/mainfile "~/dev/advent-of-code/2019/RESULTS.txt"
my/eowyn-loaded t)
;; mysql CoC connection
(defun my/connect-coc ()
(interactive)
(my/sql-connect-preset 'coc))
;; shortcuts for tramp
;; (my/add-to-list
;; 'directory-abbrev-alist
;; '(("^/root" . "/su:/")
;; ("^/rebel" . "/ssh:arwen:www/cf.bodi/rebels21/")
;; ("^/strat" . "/ssh:arwen:www/cf.bodi/strat-dom/")))
(defconst my/loaded-files-at-startup
(list
my/mainfile
user-init-file
(concat user-emacs-directory "emacs-cheatsheet.org"))
;; (concat (getenv "HOME") "/dev/g910-gkey-macro-support/lib/data_mappers/char_uinput_mapper.py")
;; (concat (getenv "HOME") "/Documents/org/boot-disk.org"))
"personal files always loaded at startup (no visible window).")
(let ((num 1))
(dolist
(filename my/loaded-files-at-startup)
(if (file-exists-p filename)
(progn
;; set variable "my/buffer-1" to buffer returned by find-file
(set
(intern (concat "my/buffer-" (number-to-string num)))
(find-file-noselect filename nil nil nil))
(message "file: [%s] loaded." filename))
(message "cannot load file: [%s]." filename))
(cl-incf num)))
;; set windows for current work buffers
(when (boundp 'my/graphic-loaded)
(set-window-buffer my/main-window my/buffer-1)
;;(set-window-buffer my/upper-window (get-buffer "*Messages*"))
(set-window-buffer my/upper-window "*Messages*")
(set-window-buffer my/below-window my/buffer-3)))
;; (set-window-buffer current-buffer (get-buffer "*messages*"))))
;; (set-window-buffer "*messages*")

22
config/home/emacs.d/graphic.el Executable file
View File

@@ -0,0 +1,22 @@
;; ~/.emacs.d/graphic.el
;;
;; emacs configuration - this file will be loaded only when emacs runs on graphic
;; system.
;;
;; br, 2010-2019
;; avoids calling this twice
(when (not (boundp 'my/graphic-loaded))
;; disable toolbar
(tool-bar-mode -1)
;; initial frame size
(set-frame-size (selected-frame) 180 50)
(setq
;; split windows and assign them references
my/upper-window (selected-window)
my/main-window (split-window-right)
my/below-window (split-window-below)
my/graphic-loaded t))

2446
config/home/emacs.d/init.el Executable file

File diff suppressed because it is too large Load Diff

142
config/home/emacs.d/lorien.el Executable file
View File

@@ -0,0 +1,142 @@
;; ~/.emacs.d/lorien.el
;;
;; Emacs configuration - this file will be loaded only when run on lorien.
;;
;; br, 2010-2019
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; pre-load often-visited files
;; avoids calling this twice
(when (not (boundp 'my/lorien-loaded))
(setq my/lorien-loaded t)
;; use ESC as C-g
;; (global-set-key [escape] 'keyboard-escape-quit)
;; (global-unset-key [escape])
(define-key key-translation-map (kbd "ESC") (kbd "C-g"))
;; mail
(require 'message)
(setq message-send-mail-function 'smtpmail-send-it
smtpmail-default-smtp-server "localhost"
smtpmail-smtp-server "localhost"
smtpmail-debug-info t
mail-signature "\n\n-- \n2 + 2 = 5, for very large values of 2.\n"
mail-default-headers "CC: \n"
send-mail-function 'smtpmail-send-it
)
;; shortcuts for tramp
;; (my/add-to-list
;; 'directory-abbrev-alist
;; '(("^/root" . "/su:/")
;; ("^/rebel" . "/ssh:arwen:www/cf.bodi/rebels21/")
;; ("^/strat" . "/ssh:arwen:www/cf.bodi/strat-dom/")))
(defconst my/loaded-files-at-startup
(list
"~/dev/brlib/Makefile"
"~/dev/brchess/Makefile"
;;"~/org/boot-disk.org"
;;"~/org/beaglebone-buster-setup.org"
;;"~/dev/www/cf.bodi/sql/coc.sql"
;;"~/dev/www/cf.bodi/sql/coc-sql.org"
user-init-file
"~/dev/tools/bash/Makefile"
"~/org/emacs-cheatsheet.org")
;;"~/dev/g910/g910-gkey-macro-support/lib/data_mappers/char_uinput_mapper.py"
;;"~/dev/advent-of-code/2022/Makefile"
;;"~/dev/www/com.raoult/devs/php/chess/list-pgn-games.php")
;; "~/dev/eudyptula/ID")
"personal files always loaded at startup (no visible window).")
(let ((num 1))
(dolist
(filename my/loaded-files-at-startup)
(if (file-exists-p filename)
(progn
;; set variable "my/buffer-1" to buffer returned by find-file
(set
(intern (concat "my/buffer-" (number-to-string num)))
(find-file-noselect filename nil nil nil))
(message "file: [%s] loaded." filename))
(message "cannot load file: [%s]." filename))
(cl-incf num)))
;; set windows for current work buffers
(when (boundp 'my/graphic-loaded)
(set-window-buffer my/main-window my/buffer-1)
(set-window-buffer my/upper-window "*Messages*")
(set-window-buffer my/below-window my/buffer-2))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Coc sync
;; mysql CoC connection (dev)
;;(defun my/connect-coc ()
;;(interactive)
;;(my/sql-connect-preset 'coc))
(defun my/connect-coc ()
(interactive)
(sql-connect "coc"))
;; sync from/to idril
(defun my/coc-get-db ()
"get last coc db from arwen"
(interactive)
;; force run on local machine when in tramp buffer
(with-current-buffer (get-buffer "*scratch*")
(async-shell-command "sync-coc-db-from-idril.sh")))
(defun my/sync-www ()
"sync www to arwen - dry run"
(interactive)
(with-current-buffer (get-buffer "*scratch*")
(async-shell-command "sync-www-to-idril.sh")))
(defun my/sync-www-doit ()
"sync www to arwen"
(interactive)
(with-current-buffer (get-buffer "*scratch*")
(async-shell-command "sync-www-to-idril.sh -d")))
(setq org-publish-project-alist
'(("org"
:base-directory "~/org"
:base-extension "org"
:publishing-directory "~/dev/www/cf.bodi/org"
:recursive t
:publishing-function org-html-publish-to-html
;;:headline-levels 4
;;:section-numbers nil
;;:html-head nil
:html-head-include-default-style nil
:html-head-include-scripts nil
;; :html-preamble my-blog-header
;;:html-postamble my-blog-footer
)
("static"
:base-directory "~/org/"
:base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf"
:publishing-directory "~/dev/www/cf.bodi/org/"
:recursive t
:publishing-function org-publish-attachment)
;; Define any other projects here...
))
(global-set-key (kbd "s-c c c") 'my/connect-coc)
(global-set-key (kbd "s-c c g") 'my/coc-get-db)
(global-set-key (kbd "s-c c s") 'my/sync-www)
(global-set-key (kbd "s-c c w") 'my/sync-www-doit))
;; (Define-key my/keys-mode-map
;; (kbd "s-c c g") 'my/coc-gewt-db)
;; (define-key my/keys-mode-map
;; (kbd "s-c c s") 'my/coc-sync-www)
;; (set-window-buffer current-buffer (get-buffer "*messages*"))))
;; (set-window-buffer "*messages*")
;; Local Variables:
;; flycheck-disabled-checkers: (emacs-lisp-checkdoc)
;; End:

9
config/home/emacs.d/term.el Executable file
View File

@@ -0,0 +1,9 @@
;; ~/.emacs.d/term.el
;;
;; emacs configuration - this file will be loaded only in terminal mode
;;
;; br, 2010-2018
;;(print "loading term.el")