add option to not alter exit status on modification
[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 comment-delimited 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 -b Keep backup file
35 -e Exit 0 on modified file.
36 -s Silent. Quiet and exit 0 on modified file.
37 -q Quiet
38 -v Verbose
39 -h|--help Help"
40 local s diff name init file_dir exists verbose backup quiet silent
41 file_dir="$(dirname "$file")"
42 exists=true
43 verbose=false
44 backup=false
45 quiet=false
46 silent=false
47 exit_status=true
48
49 case $1 in
50 -b) backup=true; shift ;;
51 -e) exit_status=false; shift ;;
52 -s) quiet=true; silent=true; exit_status=false; shift ;;
53 -q) quiet=true; shift ;;
54 -v) verbose=true; shift ;;
55 -h|--help) echo "$help"; return ;;
56 esac
57
58 if (( $# == 2 )); then
59 name=": $1"
60 shift
61 fi
62
63 local file="$1"
64 local file_name="${file##*/}"
65
66 local comment
67 comment="#_#_#"
68
69 # bind zone files use ; for comments yes, a little hacky detection.
70 if [[ $file_name == db.* ]]; then
71 comment=";;_;_;"
72 fi
73 local begin="$comment start delimiter of cedit section$name. do not modify. $comment"
74 local end="$comment end delimiter of cedit section$name. do not modify. $comment"
75
76 if [[ ! -e $file_dir ]]; then
77 if ! mkdir -p $file_dir; then
78 s=sudo
79 $s mkdir -p $file_dir || return 2
80 fi
81 fi
82 if [[ ! -e $file ]]; then
83 exists=false
84 if ! $s touch $file; then
85 s=sudo
86 $s touch $file || return 2
87 fi
88 fi
89
90 [[ -w $file ]] || s=sudo
91
92
93 local in_section=false
94 if $exists; then
95 local tailn=1
96 local temp="$(mktemp -d)/$file_name"
97 cp "$file" "$temp"
98 cp /dev/null "$file"
99 while IFS= read -r line; do
100 tailn=$(( tailn + 1 ))
101 if [[ $line == "$begin" ]]; then
102 in_section=true;
103 break
104 fi
105 printf '%s\n' "$line" >> $file
106 done < "$temp"
107 fi
108
109 IFS= read -d '' -n 1 -r init
110 if [[ $init ]]; then
111 $s tee -a "$file" >/dev/null <<<"$begin"
112 printf '%s' "$init" | $s tee -a "$file" >/dev/null
113 $s tee -a "$file" >/dev/null
114 $s tee -a "$file" >/dev/null <<<"$end"
115 fi
116
117 if $exists && $in_section; then
118 while IFS= read -r line; do
119 if [[ $line == "$begin" ]]; then
120 in_section=true;
121 fi
122 if ! $in_section; then
123 printf '%s\n' "$line" >> $file
124 fi
125 if [[ $line == $end ]]; then
126 in_section=false;
127 fi
128 done < <(tail -n +$tailn "$temp")
129 fi
130
131
132 if ! $exists; then
133 ret=1
134 if $verbose; then
135 echo "New file $file:"
136 cat "$file"
137 fi
138 elif type -t diff &>/dev/null; then
139 diff=$(diff -u "$temp" "$file")
140 ret=$?
141 if (( $ret )) && ! $quiet; then
142 echo "backup of original at $temp"
143 echo diff -u "$temp" "$file":
144 echo "$diff"
145 #elif $debug; then
146 # echo "No changes made to $file"
147 fi
148 else
149 # for systems like openwrt which don't have diff
150 diff=$(cmp "$temp" "$file")
151 ret=$?
152 if $verbose; then
153 echo "$diff"
154 fi
155 fi
156 if ! $backup && $exists; then
157 rm -r "$temp"
158 fi
159 if ! $exit_status; then
160 case $ret in
161 0|1) return 0 ;;
162 *) return $ret ;;
163 esac
164 else
165 return $ret
166 fi
167 }
168 cedit "$@"