#!/bin/bash # I, Ian Kelling, follow the GNU license recommendations at # https://www.gnu.org/licenses/license-recommendations.en.html. They # recommend that small programs, < 300 lines, be licensed under the # Apache License 2.0. This file contains or is part of one or more small # programs. If a small program grows beyond 300 lines, I plan to switch # its license to GPL. # Copyright 2024 Ian Kelling # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. cedit() { local help="Usage: [-h|--help ] [-v] [SECTION_NAME] FILE Create/modify a section in a config file Returns 1 if the file is modified by this command, 2 or higher for other problems. The section is #comment delimited. Reads STDIN for the contents of the section. Without SECTION_NAME, it acts on a global unnamed section. cedit is short for config edit. -q Quiet -s Silent. Quiet and exit 0 on modified file. -v Verbose -b Keep backup file -h|--help Help" local s diff name init file_dir exists verbose backup quiet silent file_dir="$(dirname "$file")" exists=true verbose=false backup=false quiet=false silent=false case $1 in -b) backup=true; shift ;; -v) verbose=true; shift ;; -q) quiet=true; shift ;; -s) quiet=true; silent=true; shift ;; -h|--help) echo "$help"; return ;; esac if (( $# == 2 )); then name=": $1" shift fi local file="$1" local file_name="${file##*/}" local comment comment="#_#_#" # bind zone files use ; for comments yes, a little hacky detection. if [[ $file_name == db.* ]]; then comment=";;_;_;" fi local begin="$comment start delimiter of cedit section$name. do not modify. $comment" local end="$comment end delimiter of cedit section$name. do not modify. $comment" if [[ ! -e $file_dir ]]; then if ! mkdir -p $file_dir; then s=sudo $s mkdir -p $file_dir || return 2 fi fi if [[ ! -e $file ]]; then exists=false if ! $s touch $file; then s=sudo $s touch $file || return 2 fi fi [[ -w $file ]] || s=sudo local in_section=false if $exists; then local tailn=1 local temp="$(mktemp -d)/$file_name" cp "$file" "$temp" cp /dev/null "$file" while IFS= read -r line; do tailn=$(( tailn + 1 )) if [[ $line == "$begin" ]]; then in_section=true; break fi printf '%s\n' "$line" >> $file done < "$temp" fi IFS= read -d '' -n 1 -r init if [[ $init ]]; then $s tee -a "$file" >/dev/null <<<"$begin" printf '%s' "$init" | $s tee -a "$file" >/dev/null $s tee -a "$file" >/dev/null $s tee -a "$file" >/dev/null <<<"$end" fi if $exists && $in_section; then while IFS= read -r line; do if [[ $line == "$begin" ]]; then in_section=true; fi if ! $in_section; then printf '%s\n' "$line" >> $file fi if [[ $line == $end ]]; then in_section=false; fi done < <(tail -n +$tailn "$temp") fi if ! $exists; then ret=1 if $verbose; then echo "New file $file:" cat "$file" fi elif type -t diff &>/dev/null; then diff=$(diff -u "$temp" "$file") ret=$? if (( $ret )) && ! $quiet; then echo "backup of original at $temp" echo diff -u "$temp" "$file": echo "$diff" #elif $debug; then # echo "No changes made to $file" fi else # for systems like openwrt which don't have diff diff=$(cmp "$temp" "$file") ret=$? if $verbose; then echo "$diff" fi fi if ! $backup && $exists; then rm -r "$temp" fi if $silent; then case $ret in 0|1) return 0 ;; *) return $ret ;; esac else return $ret fi } cedit "$@"