1# Copyright (C) 2009 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14#
15
16# A collection of shell function definitions used by various build scripts
17# in the Android NDK (Native Development Kit)
18#
19
20# Get current script name into PROGNAME
21PROGNAME=`basename $0`
22
23# Find the Android NDK root, assuming we are invoked from a script
24# within its directory structure.
25#
26# $1: Variable name that will receive the path
27# $2: Path of invoking script
28find_ndk_root ()
29{
30    # Try to auto-detect the NDK root by walking up the directory
31    # path to the current script.
32    local PROGDIR="`dirname \"$2\"`"
33    while [ -n "1" ] ; do
34        if [ -d "$PROGDIR/build/core" ] ; then
35            break
36        fi
37        if [ -z "$PROGDIR" -o "$PROGDIR" = '/' ] ; then
38            return 1
39        fi
40        PROGDIR="`cd \"$PROGDIR/..\" && pwd`"
41    done
42    eval $1="$PROGDIR"
43}
44
45# Put location of Android NDK into ANDROID_NDK_ROOT and
46# perform a tiny amount of sanity check
47#
48if [ -z "$ANDROID_NDK_ROOT" ] ; then
49    find_ndk_root ANDROID_NDK_ROOT "$0"
50    if [ $? != 0 ]; then
51        echo "Please define ANDROID_NDK_ROOT to point to the root of your"
52        echo "Android NDK installation."
53        exit 1
54    fi
55fi
56
57echo "$ANDROID_NDK_ROOT" | grep -q -e " "
58if [ $? = 0 ] ; then
59    echo "ERROR: The Android NDK installation path contains a space !"
60    echo "Please install to a different location."
61    exit 1
62fi
63
64if [ ! -d $ANDROID_NDK_ROOT ] ; then
65    echo "ERROR: Your ANDROID_NDK_ROOT variable does not point to a directory."
66    exit 1
67fi
68
69if [ ! -f $ANDROID_NDK_ROOT/build/core/ndk-common.sh ] ; then
70    echo "ERROR: Your ANDROID_NDK_ROOT variable does not point to a valid directory."
71    exit 1
72fi
73
74## Logging support
75##
76VERBOSE=${VERBOSE-yes}
77VERBOSE2=${VERBOSE2-no}
78
79
80# If NDK_LOGFILE is defined in the environment, use this as the log file
81TMPLOG=
82if [ -n "$NDK_LOGFILE" ] ; then
83    mkdir -p `dirname "$NDK_LOGFILE"` && touch "$NDK_LOGFILE"
84    TMPLOG="$NDK_LOGFILE"
85fi
86
87# Setup a log file where all log() and log2() output will be sent
88#
89# $1: log file path  (optional)
90#
91setup_default_log_file ()
92{
93    if [ -n "$NDK_LOGFILE" ] ; then
94        return
95    fi
96    if [ -n "$1" ] ; then
97        NDK_LOGFILE="$1"
98    else
99        NDK_LOGFILE=/tmp/ndk-log-$$.txt
100    fi
101    export NDK_LOGFILE
102    TMPLOG="$NDK_LOGFILE"
103    rm -rf "$TMPLOG" && mkdir -p `dirname "$TMPLOG"` && touch "$TMPLOG"
104    echo "To follow build in another terminal, please use: tail -F $TMPLOG"
105}
106
107dump ()
108{
109    if [ -n "$TMPLOG" ] ; then
110        echo "$@" >> $TMPLOG
111    fi
112    echo "$@"
113}
114
115dump_n ()
116{
117    if [ -n "$TMPLOG" ] ; then
118        printf %s "$@" >> $TMPLOG
119    fi
120    printf %s "$@"
121}
122
123log ()
124{
125    if [ "$VERBOSE" = "yes" ] ; then
126        echo "$@"
127    else
128        if [ -n "$TMPLOG" ] ; then
129            echo "$@" >> $TMPLOG
130        fi
131    fi
132}
133
134log_n ()
135{
136    if [ "$VERBOSE" = "yes" ] ; then
137        printf %s "$@"
138    else
139        if [ -n "$TMPLOG" ] ; then
140            printf %s "$@" >> $TMPLOG
141        fi
142    fi
143}
144
145log2 ()
146{
147    if [ "$VERBOSE2" = "yes" ] ; then
148        echo "$@"
149    else
150        if [ -n "$TMPLOG" ] ; then
151            echo "$@" >> $TMPLOG
152        fi
153    fi
154}
155
156run ()
157{
158    if [ "$VERBOSE" = "yes" ] ; then
159        echo "## COMMAND: $@"
160        "$@" 2>&1
161    else
162        if [ -n "$TMPLOG" ] ; then
163            echo "## COMMAND: $@" >> $TMPLOG
164            "$@" >>$TMPLOG 2>&1
165        else
166            "$@" > /dev/null 2>&1
167        fi
168    fi
169}
170
171run2 ()
172{
173    if [ "$VERBOSE2" = "yes" ] ; then
174        echo "## COMMAND: $@"
175        "$@" 2>&1
176    elif [ "$VERBOSE" = "yes" ]; then
177        echo "## COMMAND: $@"
178        if [ -n "$TMPLOG" ]; then
179            echo "## COMMAND: $@" >> $TMPLOG
180            "$@" >>$TMPLOG 2>&1
181        else
182            "$@" > /dev/null 2>&1
183        fi
184    else
185        if [ -n "$TMPLOG" ]; then
186            "$@" >>$TMPLOG 2>&1
187        else
188            "$@" > /dev/null 2>&1
189        fi
190    fi
191}
192
193panic ()
194{
195    dump "ERROR: $@"
196    exit 1
197}
198
199fail_panic ()
200{
201    if [ $? != 0 ] ; then
202        dump "ERROR: $@"
203        exit 1
204    fi
205}
206
207
208## Utilities
209##
210
211# Return the value of a given named variable
212# $1: variable name
213#
214# example:
215#    FOO=BAR
216#    BAR=ZOO
217#    echo `var_value $FOO`
218#    will print 'ZOO'
219#
220var_value ()
221{
222    # find a better way to do that ?
223    eval echo "$`echo $1`"
224}
225
226# convert to uppercase
227# assumes tr is installed on the platform ?
228#
229to_uppercase ()
230{
231    echo $1 | tr "[:lower:]" "[:upper:]"
232}
233
234## First, we need to detect the HOST CPU, because proper HOST_ARCH detection
235## requires platform-specific tricks.
236##
237HOST_EXE=""
238HOST_OS=`uname -s`
239case "$HOST_OS" in
240    Darwin)
241        HOST_OS=darwin
242        ;;
243    Linux)
244        # note that building  32-bit binaries on x86_64 is handled later
245        HOST_OS=linux
246        ;;
247    FreeBsd)  # note: this is not tested
248        HOST_OS=freebsd
249        ;;
250    CYGWIN*|*_NT-*)
251        HOST_OS=windows
252        HOST_EXE=.exe
253        if [ "x$OSTYPE" = xcygwin ] ; then
254            HOST_OS=cygwin
255        fi
256        ;;
257esac
258
259log2 "HOST_OS=$HOST_OS"
260log2 "HOST_EXE=$HOST_EXE"
261
262## Now find the host architecture. This must correspond to the bitness of
263## the binaries we're going to run with this NDK. Certain platforms allow
264## you to use a 64-bit kernel with a 32-bit userland, and unfortunately
265## commands like 'uname -m' only report the kernel bitness.
266##
267HOST_ARCH=`uname -m`
268case "$HOST_ARCH" in
269    i?86) HOST_ARCH=x86
270    ;;
271    amd64) HOST_ARCH=x86_64
272    ;;
273    powerpc) HOST_ARCH=ppc
274    ;;
275esac
276
277case "$HOST_OS-$HOST_ARCH" in
278  linux-x86_64|darwin-x86_64)
279    ## On Linux or Darwin, a 64-bit kernel doesn't mean that the user-land
280    ## is always 32-bit, so use "file" to determine the bitness of the shell
281    ## that invoked us. The -L option is used to de-reference symlinks.
282    ##
283    ## Note that on Darwin, a single executable can contain both x86 and
284    ## x86_64 machine code, so just look for x86_64 (darwin) or x86-64 (Linux)
285    ## in the output.
286    ##
287    file -L "$SHELL" | grep -q "x86[_-]64"
288    if [ $? != 0 ]; then
289      # $SHELL is not a 64-bit executable, so assume our userland is too.
290      log2 "Detected 32-bit userland on 64-bit kernel system!"
291      HOST_ARCH=x86
292    fi
293    ;;
294esac
295
296log2 "HOST_ARCH=$HOST_ARCH"
297
298# at this point, the supported values for HOST_ARCH are:
299#   x86
300#   x86_64
301#   ppc
302#
303# other values may be possible but haven't been tested
304#
305# at this point, the value of HOST_OS should be one of the following:
306#   linux
307#   darwin
308#    windows (MSys)
309#    cygwin
310#
311# Note that cygwin is treated as a special case because it behaves very differently
312# for a few things. Other values may be possible but have not been tested
313#
314
315# define HOST_TAG as a unique tag used to identify both the host OS and CPU
316# supported values are:
317#
318#   linux-x86
319#   linux-x86_64
320#   darwin-x86
321#   darwin-x86_64
322#   darwin-ppc
323#   windows
324#   windows-x86_64
325#
326# other values are possible but were not tested.
327#
328compute_host_tag ()
329{
330    HOST_TAG=${HOST_OS}-${HOST_ARCH}
331    # Special case for windows-x86 => windows
332    case $HOST_TAG in
333        windows-x86|cygwin-x86)
334            HOST_TAG="windows"
335            ;;
336    esac
337    log2 "HOST_TAG=$HOST_TAG"
338}
339
340compute_host_tag
341
342# Compute the number of host CPU cores an HOST_NUM_CPUS
343#
344case "$HOST_OS" in
345    linux)
346        HOST_NUM_CPUS=`cat /proc/cpuinfo | grep processor | wc -l`
347        ;;
348    darwin|freebsd)
349        HOST_NUM_CPUS=`sysctl -n hw.ncpu`
350        ;;
351    windows|cygwin)
352        HOST_NUM_CPUS=$NUMBER_OF_PROCESSORS
353        ;;
354    *)  # let's play safe here
355        HOST_NUM_CPUS=1
356esac
357
358log2 "HOST_NUM_CPUS=$HOST_NUM_CPUS"
359
360# If BUILD_NUM_CPUS is not already defined in your environment,
361# define it as the double of HOST_NUM_CPUS. This is used to
362# run Make commands in parralles, as in 'make -j$BUILD_NUM_CPUS'
363#
364if [ -z "$BUILD_NUM_CPUS" ] ; then
365    BUILD_NUM_CPUS=`expr $HOST_NUM_CPUS \* 2`
366fi
367
368log2 "BUILD_NUM_CPUS=$BUILD_NUM_CPUS"
369
370
371##  HOST TOOLCHAIN SUPPORT
372##
373
374# force the generation of 32-bit binaries on 64-bit systems
375#
376FORCE_32BIT=no
377force_32bit_binaries ()
378{
379    if [ "$HOST_ARCH" = x86_64 ] ; then
380        log2 "Forcing generation of 32-bit host binaries on $HOST_ARCH"
381        FORCE_32BIT=yes
382        HOST_ARCH=x86
383        log2 "HOST_ARCH=$HOST_ARCH"
384        compute_host_tag
385    fi
386}
387
388# On Windows, cygwin binaries will be generated by default, but
389# you can force mingw ones that do not link to cygwin.dll if you
390# call this function.
391#
392disable_cygwin ()
393{
394    if [ $HOST_OS = cygwin ] ; then
395        log2 "Disabling cygwin binaries generation"
396        CFLAGS="$CFLAGS -mno-cygwin"
397        LDFLAGS="$LDFLAGS -mno-cygwin"
398        HOST_OS=windows
399        compute_host_tag
400    fi
401}
402
403# Various probes are going to need to run a small C program
404mkdir -p /tmp/ndk-$USER/tmp/tests
405
406TMPC=/tmp/ndk-$USER/tmp/tests/test-$$.c
407TMPO=/tmp/ndk-$USER/tmp/tests/test-$$.o
408TMPE=/tmp/ndk-$USER/tmp/tests/test-$$$EXE
409TMPL=/tmp/ndk-$USER/tmp/tests/test-$$.log
410
411# cleanup temporary files
412clean_temp ()
413{
414    rm -f $TMPC $TMPO $TMPL $TMPE
415}
416
417# cleanup temp files then exit with an error
418clean_exit ()
419{
420    clean_temp
421    exit 1
422}
423
424# this function will setup the compiler and linker and check that they work as advertised
425# note that you should call 'force_32bit_binaries' before this one if you want it to
426# generate 32-bit binaries on 64-bit systems (that support it).
427#
428setup_toolchain ()
429{
430    if [ -z "$CC" ] ; then
431        CC=gcc
432    fi
433    if [ -z "$CXX" ] ; then
434        CXX=g++
435    fi
436    if [ -z "$CXXFLAGS" ] ; then
437        CXXFLAGS="$CFLAGS"
438    fi
439    if [ -z "$LD" ] ; then
440        LD="$CC"
441    fi
442
443    log2 "Using '$CC' as the C compiler"
444
445    # check that we can compile a trivial C program with this compiler
446    mkdir -p $(dirname "$TMPC")
447    cat > $TMPC <<EOF
448int main(void) {}
449EOF
450
451    if [ "$FORCE_32BIT" = yes ] ; then
452        CC="$CC -m32"
453        CXX="$CXX -m32"
454        LD="$LD -m32"
455        compile
456        if [ $? != 0 ] ; then
457            # sometimes, we need to also tell the assembler to generate 32-bit binaries
458            # this is highly dependent on your GCC installation (and no, we can't set
459            # this flag all the time)
460            CFLAGS="$CFLAGS -Wa,--32"
461            compile
462        fi
463    fi
464
465    compile
466    if [ $? != 0 ] ; then
467        echo "your C compiler doesn't seem to work:"
468        cat $TMPL
469        clean_exit
470    fi
471    log "CC         : compiler check ok ($CC)"
472
473    # check that we can link the trivial program into an executable
474    link
475    if [ $? != 0 ] ; then
476        OLD_LD="$LD"
477        LD="$CC"
478        compile
479        link
480        if [ $? != 0 ] ; then
481            LD="$OLD_LD"
482            echo "your linker doesn't seem to work:"
483            cat $TMPL
484            clean_exit
485        fi
486    fi
487    log2 "Using '$LD' as the linker"
488    log "LD         : linker check ok ($LD)"
489
490    # check the C++ compiler
491    log2 "Using '$CXX' as the C++ compiler"
492
493    cat > $TMPC <<EOF
494#include <iostream>
495using namespace std;
496int main()
497{
498  cout << "Hello World!" << endl;
499  return 0;
500}
501EOF
502
503    compile_cpp
504    if [ $? != 0 ] ; then
505        echo "your C++ compiler doesn't seem to work"
506        cat $TMPL
507        clean_exit
508    fi
509
510    log "CXX        : C++ compiler check ok ($CXX)"
511
512    # XXX: TODO perform AR checks
513    AR=ar
514    ARFLAGS=
515}
516
517# try to compile the current source file in $TMPC into an object
518# stores the error log into $TMPL
519#
520compile ()
521{
522    log2 "Object     : $CC -o $TMPO -c $CFLAGS $TMPC"
523    $CC -o $TMPO -c $CFLAGS $TMPC 2> $TMPL
524}
525
526compile_cpp ()
527{
528    log2 "Object     : $CXX -o $TMPO -c $CXXFLAGS $TMPC"
529    $CXX -o $TMPO -c $CXXFLAGS $TMPC 2> $TMPL
530}
531
532# try to link the recently built file into an executable. error log in $TMPL
533#
534link()
535{
536    log2 "Link      : $LD -o $TMPE $TMPO $LDFLAGS"
537    $LD -o $TMPE $TMPO $LDFLAGS 2> $TMPL
538}
539
540# run a command
541#
542execute()
543{
544    log2 "Running: $*"
545    $*
546}
547
548# perform a simple compile / link / run of the source file in $TMPC
549compile_exec_run()
550{
551    log2 "RunExec    : $CC -o $TMPE $CFLAGS $TMPC"
552    compile
553    if [ $? != 0 ] ; then
554        echo "Failure to compile test program"
555        cat $TMPC
556        cat $TMPL
557        clean_exit
558    fi
559    link
560    if [ $? != 0 ] ; then
561        echo "Failure to link test program"
562        cat $TMPC
563        echo "------"
564        cat $TMPL
565        clean_exit
566    fi
567    $TMPE
568}
569
570pattern_match ()
571{
572    echo "$2" | grep -q -E -e "$1"
573}
574
575# Let's check that we have a working md5sum here
576check_md5sum ()
577{
578    A_MD5=`echo "A" | md5sum | cut -d' ' -f1`
579    if [ "$A_MD5" != "bf072e9119077b4e76437a93986787ef" ] ; then
580        echo "Please install md5sum on this machine"
581        exit 2
582    fi
583}
584
585# Find if a given shell program is available.
586# We need to take care of the fact that the 'which <foo>' command
587# may return either an empty string (Linux) or something like
588# "no <foo> in ..." (Darwin). Also, we need to redirect stderr
589# to /dev/null for Cygwin
590#
591# $1: variable name
592# $2: program name
593#
594# Result: set $1 to the full path of the corresponding command
595#         or to the empty/undefined string if not available
596#
597find_program ()
598{
599    local PROG RET
600    PROG=`which $2 2>/dev/null`
601    RET=$?
602    if [ $RET != 0 ]; then
603        PROG=
604    fi
605    eval $1=\"$PROG\"
606    return $RET
607}
608
609prepare_download ()
610{
611    find_program CMD_WGET wget
612    find_program CMD_CURL curl
613    find_program CMD_SCRP scp
614}
615
616# Download a file with either 'curl', 'wget' or 'scp'
617#
618# $1: source URL (e.g. http://foo.com, ssh://blah, /some/path)
619# $2: target file
620download_file ()
621{
622    # Is this HTTP, HTTPS or FTP ?
623    if pattern_match "^(http|https|ftp):.*" "$1"; then
624        if [ -n "$CMD_WGET" ] ; then
625            run $CMD_WGET -O $2 $1
626        elif [ -n "$CMD_CURL" ] ; then
627            run $CMD_CURL -o $2 $1
628        else
629            echo "Please install wget or curl on this machine"
630            exit 1
631        fi
632        return
633    fi
634
635    # Is this SSH ?
636    # Accept both ssh://<path> or <machine>:<path>
637    #
638    if pattern_match "^(ssh|[^:]+):.*" "$1"; then
639        if [ -n "$CMD_SCP" ] ; then
640            scp_src=`echo $1 | sed -e s%ssh://%%g`
641            run $CMD_SCP $scp_src $2
642        else
643            echo "Please install scp on this machine"
644            exit 1
645        fi
646        return
647    fi
648
649    # Is this a file copy ?
650    # Accept both file://<path> or /<path>
651    #
652    if pattern_match "^(file://|/).*" "$1"; then
653        cp_src=`echo $1 | sed -e s%^file://%%g`
654        run cp -f $cp_src $2
655        return
656    fi
657}
658
659
660# Unpack a given archive
661#
662# $1: archive file path
663# $2: optional target directory (current one if omitted)
664#
665unpack_archive ()
666{
667    local ARCHIVE="$1"
668    local DIR=${2-.}
669    local RESULT TARFLAGS ZIPFLAGS
670    mkdir -p "$DIR"
671    if [ "$VERBOSE2" = "yes" ] ; then
672        TARFLAGS="vxpf"
673        ZIPFLAGS=""
674    else
675        TARFLAGS="xpf"
676        ZIPFLAGS="q"
677    fi
678    case "$ARCHIVE" in
679        *.zip)
680            (cd $DIR && run unzip $ZIPFLAGS "$ARCHIVE")
681            ;;
682        *.tar)
683            run tar $TARFLAGS "$ARCHIVE" -C $DIR
684            ;;
685        *.tar.gz)
686            run tar z$TARFLAGS "$ARCHIVE" -C $DIR
687            ;;
688        *.tar.bz2)
689            run tar j$TARFLAGS "$ARCHIVE" -C $DIR
690            ;;
691        *)
692            panic "Cannot unpack archive with unknown extension: $ARCHIVE"
693            ;;
694    esac
695}
696
697# Pack a given archive
698#
699# $1: archive file path (including extension)
700# $2: source directory for archive content
701# $3+: list of files (including patterns), all if empty
702pack_archive ()
703{
704    local ARCHIVE="$1"
705    local SRCDIR="$2"
706    local SRCFILES
707    local TARFLAGS ZIPFLAGS
708    shift; shift;
709    if [ -z "$1" ] ; then
710        SRCFILES="*"
711    else
712        SRCFILES="$@"
713    fi
714    if [ "`basename $ARCHIVE`" = "$ARCHIVE" ] ; then
715        ARCHIVE="`pwd`/$ARCHIVE"
716    fi
717    mkdir -p `dirname $ARCHIVE`
718    if [ "$VERBOSE2" = "yes" ] ; then
719        TARFLAGS="vcf"
720        ZIPFLAGS="-9r"
721    else
722        TARFLAGS="cf"
723        ZIPFLAGS="-9qr"
724    fi
725    case "$ARCHIVE" in
726        *.zip)
727            (cd $SRCDIR && run zip $ZIPFLAGS "$ARCHIVE" $SRCFILES)
728            ;;
729        *.tar)
730            (cd $SRCDIR && run tar $TARFLAGS "$ARCHIVE" $SRCFILES)
731            ;;
732        *.tar.gz)
733            (cd $SRCDIR && run tar z$TARFLAGS "$ARCHIVE" $SRCFILES)
734            ;;
735        *.tar.bz2)
736            (cd $SRCDIR && run tar j$TARFLAGS "$ARCHIVE" $SRCFILES)
737            ;;
738        *)
739            panic "Unsupported archive format: $ARCHIVE"
740            ;;
741    esac
742}
743
744# Copy a directory, create target location if needed
745#
746# $1: source directory
747# $2: target directory location
748#
749copy_directory ()
750{
751    local SRCDIR="$1"
752    local DSTDIR="$2"
753    if [ ! -d "$SRCDIR" ] ; then
754        panic "Can't copy from non-directory: $SRCDIR"
755    fi
756    log "Copying directory: "
757    log "  from $SRCDIR"
758    log "  to $DSTDIR"
759    mkdir -p "$DSTDIR" && (cd "$SRCDIR" && 2>/dev/null tar cf - *) | (tar xf - -C "$DSTDIR")
760    fail_panic "Cannot copy to directory: $DSTDIR"
761}
762
763# This is the same than copy_directory(), but symlinks will be replaced
764# by the file they actually point to instead.
765copy_directory_nolinks ()
766{
767    local SRCDIR="$1"
768    local DSTDIR="$2"
769    if [ ! -d "$SRCDIR" ] ; then
770        panic "Can't copy from non-directory: $SRCDIR"
771    fi
772    log "Copying directory (without symlinks): "
773    log "  from $SRCDIR"
774    log "  to $DSTDIR"
775    mkdir -p "$DSTDIR" && (cd "$SRCDIR" && tar chf - *) | (tar xf - -C "$DSTDIR")
776    fail_panic "Cannot copy to directory: $DSTDIR"
777}
778
779# Copy certain files from one directory to another one
780# $1: source directory
781# $2: target directory
782# $3+: file list (including patterns)
783copy_file_list ()
784{
785    local SRCDIR="$1"
786    local DSTDIR="$2"
787    shift; shift;
788    if [ ! -d "$SRCDIR" ] ; then
789        panic "Cant' copy from non-directory: $SRCDIR"
790    fi
791    log "Copying file: $@"
792    log "  from $SRCDIR"
793    log "  to $DSTDIR"
794    mkdir -p "$DSTDIR" && (cd "$SRCDIR" && tar cf - "$@") | (tar xf - -C "$DSTDIR")
795    fail_panic "Cannot copy files to directory: $DSTDIR"
796}
797
798# Rotate a log file
799# If the given log file exist, add a -1 to the end of the file.
800# If older log files exist, rename them to -<n+1>
801# $1: log file
802# $2: maximum version to retain [optional]
803rotate_log ()
804{
805    # Default Maximum versions to retain
806    local MAXVER="5"
807    local LOGFILE="$1"
808    shift;
809    if [ ! -z "$1" ] ; then
810        local tmpmax="$1"
811        shift;
812        tmpmax=`expr $tmpmax + 0`
813        if [ $tmpmax -lt 1 ] ; then
814            panic "Invalid maximum log file versions '$tmpmax' invalid; defaulting to $MAXVER"
815        else
816            MAXVER=$tmpmax;
817        fi
818    fi
819
820    # Do Nothing if the log file does not exist
821    if [ ! -f "${LOGFILE}" ] ; then
822        return
823    fi
824
825    # Rename existing older versions
826    ver=$MAXVER
827    while [ $ver -ge 1 ]
828    do
829        local prev=$(( $ver - 1 ))
830        local old="-$prev"
831
832        # Instead of old version 0; use the original filename
833        if [ $ver -eq 1 ] ; then
834            old=""
835        fi
836
837        if [ -f "${LOGFILE}${old}" ] ; then
838            mv -f "${LOGFILE}${old}" "${LOGFILE}-${ver}"
839        fi
840
841        ver=$prev
842    done
843}
844