#!/bin/bash #linkbox_remove #removes links within a box on the left linkbox_remove() { # replaces "if menu.show_additional_menu("$menuname") then" with "if false and ... then" modsed -r \ "s,(menu[.]show_additional_menu[(]([\"'])$1\2[)]),false and \1," \ "${HTML_LANG_MOD_DIR}/templates/menu_page_head.html" } #quickstart_remove #removes links within quickstart.html (top menu) quickstart_remove() { for file in \ usr/www/all/templates/quickstart.html \ usr/www.myfritz/all/home/fritzinfo.lua \ usr/www.myfritz/all/home/fritzinfo_mobile.lua \ ; do [ ! -e "${FILESYSTEM_MOD_DIR}/$file" ] && continue modsed \ "/^box.out.*m_link_bold.*href.get.*'[\/]*$1[\/]*'.*box.tohtml.*/d" \ "${FILESYSTEM_MOD_DIR}/$file" done } #menulua_remove #removes links within menu_data.lua (left menu) menulua_remove() { local file="${HTML_LANG_MOD_DIR}/menus/menu_data.lua" if [ "$FREETZ_AVM_HAS_LUA_SCALABLE" == "y" ]; then # find ["lua"] line local pattern='\["lua"\] = "[^"]*'"$1"'.lua"' local matchLine=$(cat "$file" | get_line_number_of_1st_match "${pattern}") [ -z "${matchLine}" ] && error 1 "menulua_remove: no matches for '${pattern}' found" # the ["show"] line is either one or two lines before modsed -r \ "$((${matchLine}-2)),$((${matchLine}-1))"' { s!^(\["show"\] = ).+,$!\1false,! }' \ "$file" else modsed \ "/{* *page = \".*\/$1.lua\",/d" \ "$file" fi } #menu2html_remove #removes links within menu2_home.html (left menu, pre lua) menu2html_remove() { modsed \ "/LMenuitemTop/{N;N;/^
  • \n.*<.a>\n<.li>$/d}" \ "${HTML_SPEC_MOD_DIR}/menus/menu2_home.html" } #homelua_disable [optional return value] #easily disable functions within home.lua by returning $2, if $2 is omitted the function is assumed to return void homelua_disable() { local return_statement="${2:+return }${2}" modsed -r \ "s/^(((local )?function $1)([(][^)]*[)]))$/\1\n${return_statement}\nend\n\2_\4/" \ "${HTML_LANG_MOD_DIR}/home/home.lua" \ " $1_(" } homelua_disable_wrapper() { if [ "$FREETZ_AVM_HAS_LUA_SCALABLE" == "y" ]; then homelua_disable "add_$1" "false" else homelua_disable "tr_$1" "''" fi } # ------------------------------------------------------------------- # isFreetzType # ------------------------------------------------------------------- # Author: Alexander Kriegisch (http://scrum-master.de) # ------------------------------------------------------------------- # Simplify logical OR comparisons with Freetz box types or types of # lab firmwares etc. # # Example: The following construct... # # if [ "$FREETZ_TYPE_5010" == "y" ] || \ # [ "$FREETZ_TYPE_5050" == "y" ] || \ # [ "$FREETZ_TYPE_7050" == "y" ] || \ # [ "$FREETZ_TYPE_7140" == "y" ] || \ # [ "$FREETZ_TYPE_7141" == "y" ] || \ # [ "$FREETZ_TYPE_7150" == "y" ] || \ # [ "$FREETZ_TYPE_7170" == "y" ] # then # # ... can now be abbreviated by: # # if isFreetzType 5010 5050 7050 7140 7141 7150 7170; then # # This is also possible: # # if isFreetzType 7170 7240 7270 && isFreetzType LABOR_AIO; then # ------------------------------------------------------------------- isFreetzType() { for i; do eval [ \"\$FREETZ_TYPE_$i\" == y ] && return 0 done return 1 } # little helper function used by many patches (maybe this should be sourced out) rm_files() { for file in "$@"; do echo2 "rm -rf $file" rm -rf $file done } #modsed [grep-expression-to-verify-sed] #should be used instead of sed -i -e in patch scripts modsed() { local extended_regexp="" if [ "$1" == "-r" ]; then extended_regexp="$1" shift fi [ $# -le 1 ] && error 1 "modsed failed, not enough parameters." case $2 in #since firmware 05.55 HTML_SPEC_MOD_DIR does not exist anymore */doesnotexist/*) return ;; esac if [ -f "$2" ]; then echo2 "patching $2" sed -i $extended_regexp -e "$1" "$2" || error 1 "modsed failed editing $2 (bad sed syntax?)" if [ $# -ge 3 ]; then grep -q "$3" "$2" || error 1 "modsed failed editing $2" fi else if [ $# -lt 3 ]; then warn3 "$2 not found, skipping." else error 1 "$2 not found, aborting." fi fi } # Input: # stdin - file content # $1 - regexp pattern # $2 - optional range start line (useful for something like "find first occurrence of the pattern starting from the line $2") # Output: # returns line number of the 1st pattern match or empty if the pattern doesn't match any line get_line_number_of_1st_match() { sed -n -e "${2:+${2},$}"'{ /'"$1"'/ {=;q} }' } # Usage: # mod_del_area [end-line-pattern] # # Removes all lines of the file starting from the 1st line matching the start-line-pattern # until the 1st line following the start one and matching the end-line-pattern. # # If the end-line-pattern is omitted the end line is equal to the start one. # # Both start and end lines could be fine adjusted by a positive or a negative offset. mod_del_area() { # read parameters [ $# -ne 4 -a $# -ne 5 ] && error 1 "mod_del_area: wrong parameter count" for i in 1 2 3 4 5; do case $i in 1) local startPattern="$1" ;; 2) local startOffset="$1" ;; 3) [ $# -gt 2 ] && local endPattern="$1" || continue ;; 4) local endOffset="$1" ;; 5) local file="$1" ;; esac shift done # check file exists if [ ! -f "$file" ]; then warn3 "mod_del_area: file '$file' does not exist" return fi # determine start-pattern line local startLine=$(cat "$file" | get_line_number_of_1st_match "${startPattern}") if [ -z "$startLine" ]; then warn3 "mod_del_area: start row not found in '$file'" return fi # determine end-pattern line if [ -z "$endPattern" ]; then local endLine=$startLine else local endLine=$(cat "$file" | get_line_number_of_1st_match "${endPattern}" "$(($startLine + 1))") if [ -z "$endLine" ]; then warn3 "mod_del_area: end row not found in '$file'" return fi fi # add offsets startLine="$((startLine + startOffset))" endLine="$((endLine + endOffset))" # remove lines echo1 "patching ${file##*/}: removing lines ${startLine} - ${endLine}" sed -i -e "${startLine},${endLine}d" "$file" [ $? -ne 0 ] && error 1 "mod_del_area: error in sed expression" } # General printing (info messages, warnings and errors) echoX() { # If an error occurs because a parameter starting with "-" (but none of the # predefined letters below) is detected, we heuristically stop option parsing, # assuming the caller wants to print a string like "----" or "-blah", # accidentally starting with a dash/hyphen. In most cases this will work, but # there are a few exceptions: Parameters with additional arguments (here "-i" # and "-p", see below) will lead to conflicts. E.g. if the caller wants to # print "-index" it will be interpreted as "-i ndex" ("ndex" will be used as # an indentation string). If you want to print something like this you have # to play by getopts's rules, i.e. you can use an additional separator "--" # in between real paramaters and function arguments, like this: # echoX -bc -- "-index" # echo2 -c -p "NOTE: " -- "-print this" local indent no_indent prefix no_newline bold unbold colour uncolour OPTIND=0 # Leading colon means: silent error handling, no getopts errors on console while getopts :i:p:lnbc opt; do case $opt in i) indent="$OPTARG" ;; l) no_indent=1 ;; p) prefix="$OPTARG" ;; n) no_newline=-n ;; b) bold='\033[1m'; unbold='\033[0m' ;; c) colour='\033[33m'; uncolour='\033[m' ;; # ANSI brown/yellow ?) break ;; # unknown parameter -> stop option parsing esac done shift $((OPTIND-1)) [ $no_indent ] && indent= echo -e $no_newline "${indent}${bold}${colour}${prefix}$*${uncolour}${unbold}" } # If the verbosity level is undefined, probably this script has been called # directly by a professional user, so let's give him all the details. : ${FREETZ_VERBOSITY_LEVEL:=3} # Two spaces per indentation level seems to be a sensible default [ -z "$L1" ] && L1=" " L0="" L1="$L0$L1" L2="$L1$L1" L3="$L2$L1" # Verbosity-level-dependent, indented printing echo0() { [ "$FREETZ_VERBOSITY_LEVEL" -ge 0 ] && echoX -i "$L0" "$@"; } echo1() { [ "$FREETZ_VERBOSITY_LEVEL" -ge 1 ] && echoX -i "$L1" "$@"; } echo2() { [ "$FREETZ_VERBOSITY_LEVEL" -ge 2 ] && echoX -i "$L2" "$@"; } echo3() { [ "$FREETZ_VERBOSITY_LEVEL" -ge 3 ] && echoX -i "$L3" "$@"; } # Warning message warn() { echoX -c -p "WARNING: " "$@" >&2; } warn0() { [ "$FREETZ_VERBOSITY_LEVEL" -ge 0 ] && warn "$@"; } warn1() { [ "$FREETZ_VERBOSITY_LEVEL" -ge 1 ] && warn "$@"; } warn2() { [ "$FREETZ_VERBOSITY_LEVEL" -ge 2 ] && warn "$@"; } warn3() { [ "$FREETZ_VERBOSITY_LEVEL" -ge 3 ] && warn "$@"; } # Error message + exit with specified error code error() { local err_no=$1 [ $err_no -eq 0 ] && return shift # Coloured (can be combined with bold) echoX -c -p "ERROR: " "$@" >&2 exit $err_no } modunext2() { dd if="$2" of="$2.ext2" bs=256 skip=1 conv=sync >/dev/null 2>&1 \ && { mkdir -p "$1"; echo "rdump / $1" | "$DEBUGFS" "$2.ext2" >"$1.debugfs-rdump.log" 2>&1; } if [ $? -gt 0 ]; then error 1 "modunext2: Error in $2" fi } modunsqfs() { local STATUS if [ "$FREETZ_VERBOSITY_LEVEL" -ge 2 ]; then "$UNSQUASHFS" $UNSQUASHFS_OPTIONS -dest "$1" "$2" 2>&1 | grep -v "^$" | sed -e "s/^/${L2}/g" STATUS=${PIPESTATUS[0]} else "$UNSQUASHFS" $UNSQUASHFS_OPTIONS -dest "$1" "$2" > /dev/null STATUS=$? fi if [ $STATUS -gt 0 ]; then error 1 "modunsqfs: Error in $2" fi } modunpack_autodetect_fs() { if $BLKID -O 256 "$2" 2>/dev/null | grep -q 'TYPE="ext2"'; then modunext2 "$@" elif $BLKID -O 0 "$2" 2>/dev/null | grep -q 'TYPE="squashfs"'; then modunsqfs "$@" else error 1 "modunpack_autodetect_fs: failed to detect file system type of '$2'" fi } modlangsubst() { # modlangsubst # Substitutes all $(lang de:"Deutscher Text" en:"English text" ...) occurrences # in with of the corresponding :"" section. s='[\t\r\n ]' val='\(\([^"\\]*\(\\.\)*\)*\)' entry="\w\+:\"${val}\"" LC_ALL="" LC_CTYPE=C sed -i -e " :a s/\$(lang\(${s}\+${entry}\)*${s}\+${1}:\"${val}\"\(${s}\+${entry}\)*${s}*)/\$(lang:\"\5\")/g s/\$(lang\(${s}\+${entry}\)*${s}*)/*** error: language[${1}] not available ***/g :n s/\$(lang:\"\(\([^\"\\]\+\)\|[\\]\(.\)\)${val}\")/\2\3\$(lang:\"\4\")/g tn s/\$(lang:\"\")//g /\$(lang\(${s}\|\$\)/ {N; ba} " "$2" } modlangconf() { # modlangconf # Get of a { } section in s='[\t\r\n ]' sed -n -e ":n;N;\$!bn;s/^.*${1}${s}\+{${s}*\([^}]*\)${s}*}.*$/\1/p" "$2" } modlang() { # modlang case $1 in /*) conffile=$1 ;; *) conffile="$PWD/$1" ;; esac avail="$(modlangconf "languages" "$conffile")" default="$FREETZ_LANG_STRING $(modlangconf "default" "$conffile") en de $avail" lang="" for i in $default; do for j in $avail; do if [ "$i" == "$j" ]; then lang="$j" break 2 fi done done if [ -z "$lang" ]; then error 1 "no language available" fi [ "$lang" == "$FREETZ_LANG_STRING" ] || \ echo "NOTICE: language $FREETZ_LANG_STRING not available; $lang chosen." 1>&2 ( if ! cd "${2}"; then warn "modlang: $2 does not exist" return fi local dot_exclude="$(echo $FREETZ_BASE_DIR/$1 | sed 's/language$/exclude/')" for i in $(modlangconf "files" "$conffile"); do if [ -e "${i}" ]; then modlangsubst "$lang" "${i}" else do_excluded=n while read l; do echo $i | grep -q $l && do_excluded=y done 2>/dev/null < "$dot_exclude" [ "$do_excluded" != "y" ] && warn "modlang: ${2}/${i} not found" fi done ) } # list of static packages, sorted by start level static_packages() { if [ -r "$STATIC_PACKAGES_FILE" ]; then sort "$STATIC_PACKAGES_FILE" \ | sed -e 's/^[\ \t]*//' -e 's/[\ \t]*$//' -e 's/^S[0-9]*//' \ | grep -v '^#' | grep -v '^$' fi } static_addons() { if [ -r "$STATIC_ADDON_FILE" ]; then cat "$STATIC_ADDON_FILE" \ | sed -e 's/^[\ \t]*//' -e 's/[\ \t]*$//' \ | grep -v '^#' | grep -v '^$' fi } # # returns package name with the version number stripped # real_pkg_name() { echo "$1" | sed -r -e 's/-(alpha|beta|rc|(un)?stable|.)[0-9]*$//i' -e 's/-[0-9a-f]{10}$//' -e 's/-v?[0-9][^-]*$//' } # # returns package name the with version number and special suffixes (e.g. -cgi) stripped # pkg_name() { real_pkg_name "$1" | sed -r -e 's/(-v2)?-cgi$//' } # # $1 .. * - pkgname1 pkgname2 ... # pkg_menuconfig_symbol_name() { echo "$@" | sed -r -e 's,-,_,g' -e 's,([^ \t]*),FREETZ_PACKAGE_\U\1,g' } # # $1 .. * - pkgname1 pkgname-pkgver2 ... # # Each parameter is expected to be either just # - the package name (pkgname), or # - the package name followed by the package version (pkgname-pkgversion) # # requires GNU tar because of the '--null' option (not supported by busybox tar) # collect_pkg_files() { local p=, pkg=, pkg_dir=, pkg_menuconfig_symbol= for p in "$@"; do pkg=$(real_pkg_name "${p}") pkg_menuconfig_symbol=$(pkg_menuconfig_symbol_name ${pkg}) [ "$(eval echo \$${pkg_menuconfig_symbol})" != y ] && continue if [ "${p}" == "${pkg}" ]; then # p is pkgname [ ! -f "${PACKAGES_DIR}/.${pkg}" ] && error 1 "Package ${pkg} is selected but no ${PACKAGES_DIR}/.${pkg} file is available." pkg_dir="${pkg}-$(cat "${PACKAGES_DIR}/.${pkg}")" else # p is (assumed to be) pkgname-pkgver pkg_dir="${p}" fi ( cd "${PACKAGES_DIR}/${pkg_dir}/root/" find . -type f -print0 | "$TAR_GNU" -cv $(find "../" -maxdepth 1 -type f -name ".exclude*" -printf '--exclude-from=%p ') --null -T - -f /dev/null ) done | sed -r -e 's,^[.],,' -e 's,^([^/]),/\1,' } # # checks if fwmod_custom script exists, has correct syntax, and invokes it # with exactly the same parameters as those passed to invoke_fwmod_custom itself # invoke_fwmod_custom() { if [ -x "${BASE_DIR}/fwmod_custom" ]; then echo "invoking custom script" # syntax check $SHELL -n "${BASE_DIR}/fwmod_custom" if [ $? -ne 0 ]; then error 1 "syntax error in fwmod_custom script" fi ( cd "$MOD_DIR" && \ TOOLS_DIR=../../tools \ FILESYSTEM_MOD_DIR=./filesystem \ PATCHES_COND_DIR=../../patches/cond \ ../../fwmod_custom "$@" ) || exit 1 if [ $? -ne 0 ]; then error 1 "fwmod_custom script returned error" fi fi } # # Returns "(NEEDED)"-entries for all binaries/libraries supplied # getNeededEntries() { "${FREETZ_BASE_DIR}"/toolchain/target/bin/${FREETZ_TARGET_CROSS}readelf -d "$@" 2>/dev/null \ | grep "(NEEDED)" | sed -r -e 's,^[^[]* [[]([^]]*)[]].*$,\1,' | sort -u } # # Returns if $1 is a "(NEEDED)"-entry of one of the binaries/libraries supplied after $1 # $1 might also be an extended grep-pattern, e.g. (libcapi|libfax) # isNeededEntry() { [ $# -lt 2 ] && error 1 "isNeededEntry failed, not enough parameters." local neededPattern="$1"; shift local neededEntries=$(getNeededEntries "$@") grep -E -q "$neededPattern" 2>/dev/null <<< "$neededEntries" } # # Strips trailing slash if provided # function stripTrailingSlash() { local path="$1" [ -n "${path}" -a "${path: -1: 1}" == "/" ] && path="${path: 0: -1}" echo -n "$path" } # # Adds trailing slash if missing # function addTrailingSlash() { local path="$1" [ -n "${path}" -a "${path: -1: 1}" != "/" ] && path="$path/" echo -n "$path" } # # Converts absolute symlinks to relative ones. See USAGE below for details. # # Dependencies: readlink, realpath, bash (uses bash specific functions). # function symlinks-abs2rel() { local USAGE="$( echo -e "Usage:\t${FUNCNAME} [-v|-verbose] [-r|-recursive] TARGET_FS_ROOT DIR [MASK], where" echo -e "\t\tTARGET_FS_ROOT is the directory pointing to the root directory of the target filesystem" echo -e "\t\tDIR is the directory within the target filesystem symlinks to be corrected within (absolute path is expected)" echo -e "\t\tMASK optional filename mask, default *" )" local VERBOSE="echo2" local DEPTH="-maxdepth 1" while [ -n "$1" -a "${1:0:1}" == "-" ]; do case "$1" in -v|-verbose|--verbose) VERBOSE="echo0" shift ;; -r|-recursive|--recursive) DEPTH="" shift ;; --) shift break ;; *) error 1 "$USAGE" ;; esac done local TARGET_FS_ROOT=$(realpath "$1") TARGET_FS_ROOT=$(stripTrailingSlash "$TARGET_FS_ROOT") local DIR=$(addTrailingSlash "$2") local MASK="$3"; [ -z "$MASK" ] && MASK="*" if [ -z "$TARGET_FS_ROOT" -o ! -d "$TARGET_FS_ROOT" -o -z "$DIR" -o "${DIR:0:1}" != "/" ]; then error 1 "$USAGE" fi local LINK LINK_DIR TARGET TARGET_CANONICAL TARGET_RELATIVE for LINK in $(find "${TARGET_FS_ROOT}${DIR}" ${DEPTH} -type l -lname "/*" -name "$MASK"); do LINK_DIR=$(dirname "${LINK#${TARGET_FS_ROOT}}") LINK_DIR=$(addTrailingSlash "$LINK_DIR") TARGET=$(readlink --no-newline "$LINK") if [ $? -ne 0 ]; then warn0 "${FUNCNAME}: failed to read symlink from '${LINK#${TARGET_FS_ROOT}}'" continue fi TARGET_CANONICAL=$(readlink --canonicalize-missing --no-newline "$LINK") if [ $? -ne 0 ]; then warn0 "${FUNCNAME}: failed to determine canonical name of '${LINK#${TARGET_FS_ROOT}}' -> '$TARGET'" continue fi # First try to create the shortest symlink possible. This is possible # if TARGET_CANONICAL points to some target located under LINK_DIR, # i.e. TARGET_CANONICAL starts with LINK_DIR. TARGET_RELATIVE="${TARGET_CANONICAL#${LINK_DIR}}" if [ "$TARGET_CANONICAL" == "$TARGET_RELATIVE" ]; then # TARGET_CANONICAL does NOT point to some target under LINK_DIR. # Create relative symlink by prepending target with mutiple "../" local temp="${LINK#${TARGET_FS_ROOT}/}" TARGET_RELATIVE="${TARGET_CANONICAL:1}" while [ "${temp#*/}" != "$temp" ]; do temp="${temp#*/}" TARGET_RELATIVE="../${TARGET_RELATIVE}" done fi $VERBOSE "Converting symlink '${LINK#${TARGET_FS_ROOT}}' from '$TARGET' to '$TARGET_RELATIVE'" if ! ln -sf "$TARGET_RELATIVE" "$LINK"; then error 1 "${FUNCNAME}: failed to convert symlink '${LINK#${TARGET_FS_ROOT}}' from '$TARGET' to '$TARGET_RELATIVE'" fi done } # joins all arguments starting from the 2nd one together using the 1st character of $1 as delimiter # i.e. converts $1 $2 $3 ... to $2${DELIMITER}$3${DELIMITER}$4${DELIMITER}... whereas DELIMITER=${1:0:1} join() { (IFS="$1"; shift; echo -n "$*") } # $1 - file to wrap # $2 $3 ... - libraries to preload function create_LD_PRELOAD_wrapper() { local file_to_wrap="$1"; shift [ -f "${FILESYSTEM_MOD_DIR}${file_to_wrap}" -a -x "${FILESYSTEM_MOD_DIR}${file_to_wrap}" ] || error 1 "${FUNCNAME}: ${file_to_wrap} is expected to exist and be executable" local libraries_to_preload=$(join ':' "$@") [ -n "${libraries_to_preload}" ] || error 1 "${FUNCNAME}(${file_to_wrap}): no LD_PRELOAD library specified" local bname=$(basename "$file_to_wrap") local dname=$(dirname "$file_to_wrap")/avm mkdir -p "${FILESYSTEM_MOD_DIR}${dname}" mv "${FILESYSTEM_MOD_DIR}${file_to_wrap}" "${FILESYSTEM_MOD_DIR}${dname}/" cat <<- EOF >> "${FILESYSTEM_MOD_DIR}${file_to_wrap}" #!/bin/sh export LD_PRELOAD="${libraries_to_preload}\${LD_PRELOAD:+:}\${LD_PRELOAD}" exec ${dname}/${bname} "\$@" EOF chmod 755 "${FILESYSTEM_MOD_DIR}${file_to_wrap}" } # $1 - number (or mathematical expression) in bytes function byte_to_mb() { echo $(($1)) | awk '{ printf "%.1f MB", $1 /1024/1024 }' } # $1 - tarball filename function is_valid_tarball() { # integrity check by listing the content [ -s "$1" ] && "$TAR" -tf "$1" >/dev/null 2>&1 # integrity check by decompressing to stdout # [ -s "$1" ] && "$TAR" -xf "$1" -O >/dev/null 2>&1 } # # get supported brandings by listing directories under /etc/default.${CONFIG_PRODUKT} # # shamelessly stolen from modfs project # function supported_brandings() { local etc_default_PRODUKT_path brandings etc_default_PRODUKT_path=$(find "${FILESYSTEM_MOD_DIR}/etc" -type d -name "default.*" | sed -e "\|${FILESYSTEM_MOD_DIR}/etc/default.[0-9]*$|d") brandings=$(find "${etc_default_PRODUKT_path}" -maxdepth 1 -type d | sed -n -e "s|${etc_default_PRODUKT_path}[/]*||p" | sed -e "/^$/d" | sort -u) echo $brandings }