1cfa5803c08cdb13945b8ae81fa882f747bcfc89
[cedit] / cedit
1 #!/bin/bash
2 # I, Ian Kelling, follow the GNU license recommendations at
3 # https://www.gnu.org/licenses/license-recommendations.en.html. They
4 # recommend that small programs, < 300 lines, be licensed under the
5 # Apache License 2.0. This file contains or is part of one or more small
6 # programs. If a small program grows beyond 300 lines, I plan to switch
7 # its license to GPL.
8
9 # Copyright 2024 Ian Kelling
10
11 # Licensed under the Apache License, Version 2.0 (the "License");
12 # you may not use this file except in compliance with the License.
13 # You may obtain a copy of the License at
14
15 # http://www.apache.org/licenses/LICENSE-2.0
16
17 # Unless required by applicable law or agreed to in writing, software
18 # distributed under the License is distributed on an "AS IS" BASIS,
19 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 # See the License for the specific language governing permissions and
21 # limitations under the License.
22
23 cedit() {
24 local help="Usage: [-h|--help ] [-v] [SECTION_NAME] FILE
25 Create/modify a section in a config file
26
27 Returns 1 if the file is modified by this command, 2 or higher
28 for other problems.
29
30 The section is #comment delimited. Reads STDIN for the contents of the
31 section. Without SECTION_NAME, it acts on a global unnamed
32 section. cedit is short for config edit.
33
34 -q Quiet
35 -s Silent. Quiet and exit 0 on modified file.
36 -v Verbose
37 -b Keep backup file
38 -h|--help Help"
39 local s diff name init file_dir exists verbose backup quiet silent
40 file_dir="$(dirname "$file")"
41 exists=true
42 verbose=false
43 backup=false
44 quiet=false
45 silent=false
46
47 case $1 in
48 -b) backup=true; shift ;;
49 -v) verbose=true; shift ;;
50 -q) quiet=true; shift ;;
51 -s) quiet=true; silent=true; shift ;;
52 -h|--help) echo "$help"; return ;;
53 esac
54
55 if (( $# == 2 )); then
56 name=": $1"
57 shift
58 fi
59
60 local file="$1"
61 local file_name="${file##*/}"
62
63 local comment
64 comment="#_#_#"
65
66 # bind zone files use ; for comments yes, a little hacky detection.
67 if [[ $file_name == db.* ]]; then
68 comment=";;_;_;"
69 fi
70 local begin="$comment start delimiter of cedit section$name. do not modify. $comment"
71 local end="$comment end delimiter of cedit section$name. do not modify. $comment"
72
73 if [[ ! -e $file_dir ]]; then
74 if ! mkdir -p $file_dir; then
75 s=sudo
76 $s mkdir -p $file_dir || return 2
77 fi
78 fi
79 if [[ ! -e $file ]]; then
80 exists=false
81 if ! $s touch $file; then
82 s=sudo
83 $s touch $file || return 2
84 fi
85 fi
86
87 [[ -w $file ]] || s=sudo
88
89
90 local in_section=false
91 if $exists; then
92 local tailn=1
93 local temp="$(mktemp -d)/$file_name"
94 cp "$file" "$temp"
95 cp /dev/null "$file"
96 while IFS= read -r line; do
97 tailn=$(( tailn + 1 ))
98 if [[ $line == "$begin" ]]; then
99 in_section=true;
100 break
101 fi
102 printf '%s\n' "$line" >> $file
103 done < "$temp"
104 fi
105
106 IFS= read -d '' -n 1 -r init
107 if [[ $init ]]; then
108 $s tee -a "$file" >/dev/null <<<"$begin"
109 printf '%s' "$init" | $s tee -a "$file" >/dev/null
110 $s tee -a "$file" >/dev/null
111 $s tee -a "$file" >/dev/null <<<"$end"
112 fi
113
114 if $exists && $in_section; then
115 while IFS= read -r line; do
116 if [[ $line == "$begin" ]]; then
117 in_section=true;
118 fi
119 if ! $in_section; then
120 printf '%s\n' "$line" >> $file
121 fi
122 if [[ $line == $end ]]; then
123 in_section=false;
124 fi
125 done < <(tail -n +$tailn "$temp")
126 fi
127
128
129 if ! $exists; then
130 ret=1
131 if $verbose; then
132 echo "New file $file:"
133 cat "$file"
134 fi
135 elif type -t diff &>/dev/null; then
136 diff=$(diff -u "$temp" "$file")
137 ret=$?
138 if (( $ret )) && ! $quiet; then
139 echo "backup of original at $temp"
140 echo diff -u "$temp" "$file":
141 echo "$diff"
142 #elif $debug; then
143 # echo "No changes made to $file"
144 fi
145 else
146 # for systems like openwrt which don't have diff
147 diff=$(cmp "$temp" "$file")
148 ret=$?
149 if $verbose; then
150 echo "$diff"
151 fi
152 fi
153 if ! $backup && $exists; then
154 rm -r "$temp"
155 fi
156 if $silent; then
157 case $ret in
158 0|1) return 0 ;;
159 *) return $ret ;;
160 esac
161 else
162 return $ret
163 fi
164 }
165 cedit "$@"