fix bug reading file
[tee-unique] / appendu
diff --git a/appendu b/appendu
index 43e2e8737aaffabf42ecc47005e395094bfe6a61..d03fb45ad9541bc7c32b17d774a3096ac11192cf 100755 (executable)
--- a/appendu
+++ b/appendu
@@ -3,25 +3,23 @@
 # This program is under GPL v. 3 or later, see <http://www.gnu.org/licenses/>
 
 appendu() {
-    local help="Usage: appendu [OPTION]... FILE [LINE]...
+    local help="Usage: appendu [OPTION]... FILE [LINE_SET]...
+
 Append unique.
-Append each line to FILE if it does not exist in FILE.
-Use LINE if specified, else use lines from stdin.
-Appended lines are output to the terminal.
 
-  -s      don't try to use sudo when it would help us read or write the file
-  --      stop processing arguments
-  --help  display this message"
+A LINE_SET is one or more lines. Append LINE_SET to FILE if it does not exist in
+FILE.  If no LINE_SET argument is given, read lines from stdin, and treat each
+as a single LINE_SET. Appended text is output to the terminal. Duplicate
+LINE_SETs are treated the same.
+
+  --           stop processing arguments
+  [-h|--help]  display this message"
 
-    local dosudo=true
     
     while true; do
-        if [[ $1 == --help ]]; then
+        if [[ $1 == --help || $1 == -h ]]; then
             echo "$help"
             return
-        elif [[ $1 == -s ]]; then
-            dosudo=false
-            shift
         elif [[ $1 == -- ]]; then
             shift
             break
@@ -36,36 +34,69 @@ Appended lines are output to the terminal.
         return 1
     fi
 
-    local readsudo writesudo
     local file="$1"
     shift
-    
+
+    local new_file=true
     if [[ -e $file ]]; then
-        [[ -r $file ]] || readsudo=sudo
-        [[ -w $file ]] || writesudo=sudo
-    else 
+        new_file=false
+    else
         local dir="$(dirname "$file")"
-        if [[ -d $dir ]]; then
-            [[ ! -w $dir ]] && writesudo=sudo
-        else
+        if [[ ! -d $dir ]]; then
             echo "appendu error: $dir does not exist"
             return 1
         fi
     fi
-    if ! $dosudo; then
-        readsudo=
-        writesudo=
-    fi
-    if (( $# )); then
-        for x in "$@"; do
-            [[ -e "$file" ]] && $readsudo grep -q "^$x$" "$file" || $writesudo tee -a "$file"<<<"$x"
-        done
-    elif [[ ! -t 0 ]]; then
+
+    local strings line
+    if (( $# == 0 )); then
         unset IFS
-        while read -r x; do
-            # duplicated from above
-            [[ -e "$file" ]] && $readsudo grep -q "^$x$" "$file" || $writesudo tee -a "$file"<<<"$x"
+        while read -r line; do
+            strings+=( "$line" )
         done
+    else
+        strings=( "$@" )
     fi
+
+    if ! $new_file; then
+        if [[ ! -r $file ]]; then
+            echo "appendu error: cannot read or write $file"
+            return 1
+        fi
+        if [[ ! -w $file ]]; then
+            echo "appendu error: cannot read or write $file"
+            return 1
+        fi            
+        # fix files with no newline at the end.
+        # the following command won't work right on them otherwise.
+        # e = run script, $a\ means append following text, but there is none,
+        # so sed only does what it always does when it was supposed to modify a file,
+        # which is append a newline if there was none.
+        sed -ie '$a\' "$file"
+        # command substitution removes any trailing newlines, so we have to add
+        # a non-newline ending, we randomly chose "b", then remove it.
+        local content=$(cat "$file"; echo b)
+        content=${content%b}
+    fi
+    
+    # we aren't using regex because we want to match strings,
+    # but we also want our match to start at the beginning of a line,
+    # or the beginning of the file, and to end at a line ending.
+    # So we do some slick bash to match this.
+    local start="?(*
+)"
+    local end="
+*"
+    local return_code string return_code
+    for string in "${strings[@]}"; do
+        if $new_file || [[ $content != $start"$string"$end ]]; then
+            if ! tee -a "$file"<<<"$string"; then
+                return_code=$?
+                echo "appendu error: error writing to $file"
+                return $return_code
+            fi
+        fi
+    done
+    return 0
 }
 appendu "$@"