What is cpif?


From:     Sven Utcke
Date: 21 Oct 1994
Fernando Mato Mira writes: c. Touching the file with the code is uncool with make et al.

Barry Schwartz writes: The cpif program addresses this problem.

This is now the second time that I hear about cpif. What is it? Can it be used with, say, FWEB (the literate program of my choice)? If so, where can I get it (ask archie, I suppose?)?


From:     Joachim Schrod
Date: 21 Oct 1994
Sven Utcke writes: This is now the second time that I hear about cpif. What is it?

cpif is part of the noweb distribution, it copies standard input to a file only if the contents did not change. (Or did change, as you want.)

#!/bin/sh

# cpif [ -eq -ne ] file...
# copy standard input to each of the named files
# if new * old is true or old doesn't exist;
# * defaults to -ne

PATH=/bin:/usr/bin

# set -x
op=-ne
case "$1" in
-eq|-ne)    op=$1; shift ;;
-*)     echo 'Usage: '`basename $0`' [ -eq -ne ] file...' 1>&2; exit 2
esac
case $# in
0)      echo 'Usage: '`basename $0`' [ -eq -ne ] file...' 1>&2; exit 2
esac

new=/tmp/$$
trap 'rm -f $new; exit 1' 1 2 15    # clean up files

cat >$new
for i
do
    cmp -s $new $i
    case $op$? in
    -eq0|-ne1|*2)   cp $new $i
    esac
done
rm -f $new
Actually, not long ago I needed to extend this to `real' file copy operations, so here is my copyif. (E.g., I use it for parser generators like PCCTS who create the scanner as an intermediate file and would trigger spurious compilation of the scanner as it has changed.) As it's roughly upward compatible (as long as only one file is `cpif'ed), I use it for noweb, too...
#
# copyif -- copy files if comparison succeeds
#
# (history at end)

# SYNOPSIS
#
#   copyif [-eq | -ne] file
#   copyif [-eq | -ne] file1 file2
#   copyif [-eq | -ne] file ... dir
#
#
# copyif copies a file if a comparison succeeds or if the target file
# does not exist. The comparison is either a test if the contents is
# equal (-eq) or if it has changed (-ne). The default is -ne.
#
# The first call form copies standard input to <file>file</>. The second
# one copies <file>file1</> to <file>file2</>. The third copies all
# <file>files</>s into directory <file>dir</>.
#
# The source file(s) and also the target file/dir must be readable,
# and target must be writable.
#
# A typical usage is in Makefiles where a file shall only be copied if
# it has changed.
#
#

cmd=`basename $0`
usage()
{
    cat <<_EOF_ >&2
usage:
    $cmd: [-eq | -ne] file
    $cmd: [-eq | -ne] file1 file2
    $cmd: [-eq | -ne] file ... dir
_EOF_
    exit $1
}

# parse options...

test $# = 0  -o  "$1" = '-?'  &&  usage 1

# default is -ne

case "$1" in
    -ne|-eq) test=$1
    shift
    ;;
    *)  test='-ne'
    ;;
esac

# one parameter => save source from stdin in $tmp_file
if [ $# = 1 ]
   then tmp_file=/tmp/copyif$$
    trap "rm -f $tmp_file" 0 1 2 3 15
    cat >$tmp_file
    src=$tmp_file
    dest_file=$1
    dest_dir=''
fi

# two parameter
if [ $# = 2 ]
   then src=$1
    # Target argument might be a directory, ie, this might be a
    # call of form 3.
    if [ -d "$2" ]
       then dest_file=''
        dest_dir=$2
       else dest_file=$2
        dest_dir=''
    fi
fi

# n parameter
#   $1 .. $n-1 are $src
#   $n is $dest_dir
if [ $# -gt 2 ]
   then # Split list of arguments on last blank and assign thereby the
    # source files to $src.
    src=`expr "$*" : '\(.*\) '`
    dest_file=''
    # Discard all source arguments
    shift `expr $# - 1`
    # Check if target argument is a directory, complain if not
    if [ ! -d "$1" ]
       then echo "$cmd: $1: not a directory." >&2
        usage 2
       else dest_dir=$1
    fi
fi

# pre:
# $src is list of source files
# either $dest_dir is destination directory
#     or $dest_dir is empty and $dest_file is destination file and
#    $src is only one file

result_code=0

for file in $src
   do   test "$dest_dir"  &&  dest_file=$dest_dir/$file
    # now $dest_file holds the target file name
    # check if it exists
    if [ -f $dest_file ]
       then cmp $file $dest_file >/dev/null
        result="$test$?"
        case "$result" in
            *2) # error in cmp command, message was issued
            exit 2
            ;;
            -eq1|-ne0)
            continue
            ;;
        esac
    fi
    cp $file $dest_file
    result_code=`expr $result_code + $?`
   done

# If there was at least one error in cp, add 10 to the result code.
# This way we get a unique exit code that we can distinguish from
# internal errors.

test $result_code != 0  &&  result_code=`expr $result_code + 10`
exit $result_code


From:     Lee Wittenberg
Date: 24 Oct 1994
Sven Utcke writes: This is now the second time that I hear about cpif. What is it? Can it be used with, say, FWEB (the literate program of my choice)? If so, where can I get it (ask archie, I suppose?)?

cpif is a tool that comes with the noweb distribution. It's designed to work with makefiles, and should work with any system. You can find it in the noweb distribution in the CTAN archives (under the shell directory; the man page cpif.1 is in the xdoc directory). Cpif is a shell script, but Barry Schwartz has ported it to DOS, but this version hasn't been officially released yet, because Lee Wittenberg has been to d--- lazy to build a new DOS noweb distribution with it enclosed.