R2pkg: update to version 0.5. trunk
authorbrook <brook@pkgsrc.org>
Thu, 01 Aug 2019 13:11:08 +0000
branchtrunk
changeset 337237 b5c4951f695d
parent 337236 bc5be5c6bf2d
child 337238 7e0442cc7314
R2pkg: update to version 0.5.
pkgtools/R2pkg/Makefile
pkgtools/R2pkg/files/R2pkg.8
pkgtools/R2pkg/files/R2pkg.sh
pkgtools/R2pkg/files/RELEASE
--- a/pkgtools/R2pkg/Makefile	Thu Aug 01 12:58:31 2019 +0000
+++ b/pkgtools/R2pkg/Makefile	Thu Aug 01 13:11:08 2019 +0000
@@ -1,7 +1,7 @@
-# $NetBSD: Makefile,v 1.3 2019/06/24 13:46:04 brook Exp $
+# $NetBSD: Makefile,v 1.4 2019/08/01 13:11:08 brook Exp $
 #
 
-VERS=		0.4
+VERS=		0.5
 PKGNAME=	R2pkg-${VERS}
 CATEGORIES=	pkgtools
 
--- a/pkgtools/R2pkg/files/R2pkg.8	Thu Aug 01 12:58:31 2019 +0000
+++ b/pkgtools/R2pkg/files/R2pkg.8	Thu Aug 01 13:11:08 2019 +0000
@@ -1,4 +1,4 @@
-.\"	$NetBSD: R2pkg.8,v 1.5 2019/06/24 13:46:04 brook Exp $
+.\"	$NetBSD: R2pkg.8,v 1.6 2019/08/01 13:11:08 brook Exp $
 .\"
 .\" Copyright (c) 2014,2015,2016,2017,2018,2019
 .\"	Brook Milligan.  All rights reserved.
@@ -36,61 +36,97 @@
 .Nd create a pkgsrc package for an R package
 .Sh SYNOPSIS
 .Nm
-.Op Fl DehqruVv
+.Op Fl cDehqruVv
 .Op Fl E Ar editor
 .Op Fl M Ar maintainer_email
-.Op Fl R Ar dependency_file
 .Op Ar package
 .Sh DESCRIPTION
 .Nm
-takes the name of an R
-.Ar package
-that should be turned into a
+creates or updates a
+.Xr pkgsrc 7
+package for the corresponding R
+.Ar package .
+The
 .Xr pkgsrc 7
-package in the current directory.
-The generated package includes a
-.Pa Makefile
-and a mostly complete set of files to get the package going.
-However,
+package will be created in the current directory,
+which by convention should be named
+.Pa R-package .
+If the user does not supply the
+.Ar package
+on the command line, then
+.Nm
+will use the basename of the current directory
+as the name of the R package after stripping the leading
+.Pa R- .
+.Pp
+.Nm
+tries to make as complete a
+.Xr pkgsrc 7
+package as possible, and in the case of updating tries to maintain
+as much of the original as possible.
+Nevertheless,
 the files should be reviewed and perhaps adjusted manually after
 .Nm
 has finished its job.
 To help with this, the original versions of files created by
 .Nm
-are preserved for reference.
+are preserved for reference, but should be removed before commiting the
+.Xr pkgsrc 7
+package.
 .Pp
 .Nm
-is intended to help the process of creating a package but is not
+is intended to help the process of creating or updating a package but is not
 intended to fly on autopilot.
 .Pp
 The
-.Pa Makefile
-contains all of the variables required for an R package, with
-information being taken from the package's
+.Pa DESCR
+file is populated with information from the package's
 .Pa DESCRIPTION
 file on CRAN, which is automatically fetched by
 .Nm .
 Likewise, the
-.Pa DESCR
-file is populated with information from the same file.
+.Pa Makefile
+contains all of the variables required for an R package, with
+information being taken from the same
+.Pa DESCRIPTION
+file.
+In the case of updating, a file
+.Pq Pa DESCR.new
+containing the new description is created, and
+as much as possible of the original
+.Pa Makefile
+is retained.
 After
-.Pa Makefile
+.Pa DESCR
 and
-.Pa DESCR
+.Pa Makefile
 are completed,
 .Nm
-fetches the package and computes its checksum via the
+fetches the distfile and computes its checksum via the
 .Ic makesum
-target.
-.Pp
-If the user does not supply the
-.Ar package
-on the command line, then
-.Nm
-will prompt for it.
+and
+.Ic makepatchsum
+targets.
+Finally, a
+.Pa buildlink3.mk
+file may be created if necessary.
 .Pp
 The following options are available:
 .Bl -tag -width indent
+.It Fl c
+Create the
+.Xr pkgsrc 7
+package
+.Po
+and any dependencies if the
+.Fl r
+option is given
+.Pc .
+This is the default if neither
+.Fl c
+nor
+.Fl u
+is given.
 .It Fl D
 Write the package's description into
 .Pa DESCRIPTION .
@@ -103,55 +139,72 @@
 .Ar editor
 instead of the user's default editor.
 .It Fl e
-Do not edit Makefile and DESCR.
+Do not present the
+.Pa Makefile
+and
+.Pa DESCR
+files for editing.
 .It Fl h
 Produce a short help message.
 .It Fl M Ar maintainer_email
 Set the maintainer email address for any newly created packages.
+By default, the email address will be
+.Aq pkgsrc-users@NetBSD.org .
 .It Fl q
 Do not produce status messages along the way.
-.It Fl R Ar dependency_file
-Process dependency packages recursively using
-.Pa dependency_file
-to record dependency information.
-This option is intended for
-internal use only to implement recursion over dependencies.
-Instead, the
-.Fl r
-option should be used to select recursion.
 .It Fl r
 Process dependency packages recursively.
+Dependency packages that do not exist will be created in the
+.Pa pkgsrc/wip
+directory, and processed by
+.Nm
+recursively.
+Dependency packages that do exist will only be processed when updating.
 A
 .Pa depends
 file is created that lists the dependencies in
 .Xr tsort 1
-order.
-This is likely the best order for testing the resulting
+order, which is likely the best for testing the resulting
 dependency packages.
 In particular, a useful strategy for creating
+.Po
+or updating with the
+.Fl u
+option
+.Pc
 packages recursively is to (i) run
 .Nm
 with the
 .Fl r
-option, (ii) fix all the leaf packages, (iii) rerun
+option,
+(ii) move any newly created packages from
+.Pa pkgsrc/wip
+into appropriate categories,
+(iii) fix each package as needed, and
+(iv) rerun
 .Nm
-with the
+with both the
 .Fl r
-option, and (iv) continue until all packages are created.
-Repeating runs of
-.Nm
-once the leaf dependencies are moved into the correct categories will
-allow the program to capture the correct dependencies in each
-.Pa Makefile .
+and
+.Fl u
+options.
+The second
+.Pq and subsequent
+run(s) will correct each package's
+.Pa Makefile
+with the correct category information and the correct dependency directories.
 .It Fl u
 Update the
 .Xr pkgsrc 7
-package.
-.Nm
-must be run in an R package directory, which is assumed to be named as
-.Pa R-package .
-The name of the package to be updated will be taken from the directory
-name and need not be given on the command line.
+package
+.Po
+and any dependencies if the
+.Fl r
+option is given
+.Pc .
+This has no effect if a package
+.Pq including a dependency
+is being newly created.
 .It Fl V
 Print the version.
 .It Fl v
@@ -178,29 +231,36 @@
 .Xr pkgsrc 7
 package for the R package
 .Ar foo :
-.Dl cd pkgsrc/math
+.Dl cd pkgsrc/category
 .Dl mkdir R-foo
 .Dl cd R-foo
-.Dl R2pkg foo
-Remember to test the resulting package and remove any extra files
-created by
-.Nm .
+.Dl R2pkg -c foo
 Adding
 .Fl r
 to the command line will also recursively create all necessary
 dependencies in the
-.Pa wip
-directory.
+.Pa pkgsrc/wip
+directory if they do not already exist.
 .Pp
 Use the following commands to update a
 .Xr pkgsrc 7
 package (and dependencies) for the R package
 .Ar foo :
-.Dl cd pkgsrc/math/R-foo
-.Dl R2pkg -r -u
-Packages built for any new dependencies will be added to the
-.Pa wip
+.Dl cd pkgsrc/category/R-foo
+.Dl R2pkg -u -r
+Packages created for any new dependencies will be added to the
+.Pa pkgsrc/wip
 directory.
+.Pp
+Remember to test the resulting package(s) and remove any extra files
+created by
+.Nm .
+If new packages are created in the
+.Pa pkgsrc/wip
+directory, they should be moved to appropriate categories
+and
+.Nm
+should be rerun to update categories and paths.
 .Sh SEE ALSO
 .Xr pkgsrc 7
 .Sh AUTHORS
@@ -212,14 +272,16 @@
 does not create a finished package; instead, it facilitates the task.
 However, the resulting files must be reviewed and possibly edited by
 hand.
-Although dependencies are included in the generated
-.Pa Makefile ,
-they are not fully analyzed and likely have incorrect category
-information.
 Packages created recursively for dependencies are placed
-in a newly created, nonstandard
-.Pa pkgsrc/R
-category directory and must be moved to appropriate category
-directories within
+in the
+.Pa pkgsrc/wip
+directory;
+they must be moved to appropriate category directories within
 .Xr pkgsrc 7
 and checked for correctness.
+Therefore, although dependencies are included in the generated
+.Pa Makefile ,
+they will have incorrect category information
+unless moved to an appopriate category and
+.Nm
+rerun.
--- a/pkgtools/R2pkg/files/R2pkg.sh	Thu Aug 01 12:58:31 2019 +0000
+++ b/pkgtools/R2pkg/files/R2pkg.sh	Thu Aug 01 13:11:08 2019 +0000
@@ -1,5 +1,5 @@
 #!/bin/sh
-# $NetBSD: R2pkg.sh,v 1.4 2019/06/24 13:46:04 brook Exp $
+# $NetBSD: R2pkg.sh,v 1.5 2019/08/01 13:11:08 brook Exp $
 #
 # Copyright (c) 2014,2015,2016,2017,2018,2019
 #	Brook Milligan.  All rights reserved.
@@ -36,7 +36,7 @@
 
 R2PKG=${0}
 
-USAGE="${NAME} [-DVehqrv] [-E editor] [-M maintainer] [-R dependency_file] [package] -- create an R package for pkgsrc"
+USAGE="${NAME} [-cDehqruVv] [-E editor] [-M maintainer] [package] -- create an R package for pkgsrc"
 
 : ${CRAN_URL:=ftp://cran.r-project.org}
 : ${PKGEDITOR:=${EDITOR:=vi}}
@@ -44,61 +44,65 @@
 # Substituted by pkgsrc at pre-configure time.
 MAKE=@MAKE@
 EDIT=1
+LEVEL=0
 MAINTAINER_EMAIL=pkgsrc-users@NetBSD.org
+PID=$$
 QUIET=0
 RECURSIVE=0
-TOP_LEVEL=1
 UPDATE=0
 VERBOSE=0
 
 DESCRIPTION_CONNECTION=connection
 
-while getopts DE:M:R:Vehqruv f
+while getopts cDehqruVvE:M:L:P: f
 do
-    case $f in
+    case ${f} in
+	# options without arguments
+	c) UPDATE=0; ARGS="${ARGS} -c";;
 	D) DESCRIPTION=yes; DESCRIPTION_CONNECTION="'DESCRIPTION'"; ARGS="${ARGS} -D";;
-	E) PKGEDITOR=${OPTARG}; ARGS="${ARGS} -E ${PKGEDITOR}";;
-	M) MAINTAINER_EMAIL=${OPTARG}; ARGS="${ARGS} -M ${MAINTAINER_EMAIL}";;
-	R) DEPENDENCY_LIST=${OPTARG}; RECURSIVE=1; TOP_LEVEL=0; ARGS="${ARGS} -R ${DEPENDENCY_LIST}";;
-	V) echo "${NAME} v${VERS}"; exit 0;;
 	e) EDIT=0; ARGS="${ARGS} -e";;
 	h) echo ${USAGE}; exit 0;;
 	q) QUIET=1; ARGS="${ARGS} -q";;
 	r) RECURSIVE=1; RECURSIVE_MESSAGE=1; ARGS="${ARGS} -r";;
 	u) UPDATE=1; ARGS="${ARGS} -u";;
+	V) echo "${NAME} v${VERS}"; exit 0;;
 	v) VERBOSE=$((${VERBOSE}+1)); ARGS="${ARGS} -v";;
+	# options taking arguments
+	E) PKGEDITOR=${OPTARG}; ARGS="${ARGS} -E ${PKGEDITOR}";;
+	M) MAINTAINER_EMAIL=${OPTARG}; ARGS="${ARGS} -M ${MAINTAINER_EMAIL}";;
+	# options for recursion; only for internal use
+	L) LEVEL=${OPTARG};;
+	P) PID=${OPTARG};;
+	# unknown options
         \?) echo ${USAGE}; exit 1;;
     esac
 done
 shift `expr ${OPTIND} - 1`
 
-if [ ${UPDATE} -eq 1 ]; then
+# Update ${ARGS} for recursive call
+ARGS="${ARGS} -L $((${LEVEL}+1)) -P ${PID}"
+
+if [ ${#} -eq 0 ]; then
     RPKG=$(echo $(basename $(pwd)) | sed -e 's/^R-//');
-elif [ ${#} -eq 0 ]; then
-    read -p "package: " RPKG TAIL
-    if [ "X${TAIL}" != "X" ]; then
-	echo "Error: multiple package names given."
-	echo ${USAGE}
-	exit 1
-    fi
 elif [ ${#} -eq 1 ]; then
     RPKG=${1}
 else
-    echo ${USAGE}
+    echo "Error: multiple package names given."
+    echo "${USAGE}"
     exit 1
 fi
 
 R_FILE=${TMPDIR}/R2pkg.$$.R
 
-if [ ${TOP_LEVEL} -eq 1 ]; then
-    BANNER_MSG="===> Creating R package: R-${RPKG}"
+if [ ${UPDATE} -eq 1 -a -r Makefile ]; then
+    BANNER_MSG="[ ${LEVEL} ] ===> Updating R package R-${RPKG} in $(pwd)"
 else
-    BANNER_MSG="===> Creating dependency package: R-${RPKG}"
+    BANNER_MSG="[ ${LEVEL} ] ===> Creating R package R-${RPKG} in $(pwd)"
 fi
-if [ "X${DEPENDENCY_LIST}" = "X" ]; then
-    DEPENDENCY_LIST=${TMPDIR}/R2pkg.depends.$$
-    ARGS="${ARGS} -R ${DEPENDENCY_LIST}"
-fi
+
+PACKAGES_LIST=${TMPDIR}/R2pkg.packages.${PID}
+DEPENDENCY_LIST=${TMPDIR}/R2pkg.depends.${PID}
+
 if [ ${QUIET} -eq 1 ]; then
     STDOUT_MAKESUM=">/dev/null"
     STDOUT_EXTRACT=">/dev/null"
@@ -107,7 +111,7 @@
     ECHO_DONE=":"
     ECHO_FETCH=":"
     ECHO_EXTRACT=":"
-    if [ ${TOP_LEVEL} -eq 0 ]; then
+    if [ ${LEVEL} -ne 0 ]; then
 	ECHO=":"
     fi
 elif [ ${VERBOSE} -eq 0 ]; then
@@ -149,11 +153,28 @@
     fi
 }
 
-preserve_original_files ()
+check_for_no_recursion ()
+{
+    touch ${PACKAGES_LIST}
+    grep -E -q -e "${RPKG}" ${PACKAGES_LIST} \
+	&& echo "ERROR: circular dependency"
+    echo "${RPKG}" >> ${PACKAGES_LIST}
+}
+
+preserve_original_content ()
 {
+    [ -f Makefile ] && grep -e "CATEGORIES=" Makefile > CATEGORIES
+    [ -f Makefile ] && grep -e "COMMENT=" Makefile > COMMENT
+    [ -f Makefile ] && grep -e "LICENSE=" Makefile > LICENSE
+    [ -f Makefile ] && grep -e "MAINTAINER=" Makefile > MAINTAINER
+    [ -f Makefile ] && grep -e "USE_LANGUAGES" Makefile > USE_LANGUAGES
+    [ -f Makefile ] && grep -e "USE_TOOLS" Makefile > USE_TOOLS
+    [ -f Makefile ] && grep -e "DEPENDS" Makefile > DEPENDS
+    [ -f Makefile ] && grep -e "buildlink3.mk" Makefile > BUILDLINK3.MK
+
     [ -f DESCR ]    && mv DESCR DESCR.orig
-    [ -f Makefile ] && grep -e "MAINTAINER=" Makefile > MAINTAINER
     [ -f Makefile ] && mv Makefile Makefile.orig
+    [ -f buildlink3.mk ] && mv buildlink3.mk buildlink3.mk.orig
     [ -f distinfo ] && mv distinfo distinfo.orig
 }
 
@@ -161,6 +182,15 @@
 {
     R_CMD="Rscript --no-save ${R_FILE}"
     cat << EOF > ${R_FILE}
+R_version <- function()
+{
+  info <- R.Version()
+  version <- paste0(info[['major']],'.',info[['minor']])
+  version
+}
+
+set.locale <- function() { invisible(Sys.setlocale('LC_ALL','C')) }
+
 trim.space <- function(s) gsub('[[:space:]]','',s)
 trim.blank <- function(s) gsub('[[:blank:]]','',s)
 one.space <- function(s) gsub('[[:blank:]]+',' ',s)
@@ -168,21 +198,84 @@
 pkg.vers <- function(s) gsub('_','.',s)
 field <- function(key,value) paste(key,'=\t',value,sep='')
 
+base.packages <- c(
+  'MASS',
+  'Matrix',
+  'R',
+  'Rcpp',
+  'boot',
+  'class',
+  'cluster',
+  'codetools',
+  'foreign',
+  'grDevices',
+  'graphics',
+  'grid',
+  'lattice',
+  'methods',
+  'mgcv',
+  'nlme',
+  'nnet',
+  'parallel',
+  'rpart',
+  'splines',
+  'stats',
+  'survival',
+  'tools',
+  'utils')
+
 licenses <- list()
-licenses[['ACM']]           <- 'acm-license'
-licenses[['APACHE']]        <- 'apache-2.0'
-licenses[['ARTISTIC']]      <- 'artistic-2.0'
-licenses[['BSD-2']]         <- '2-clause-bsd'
-licenses[['GPL-2']]         <- 'gnu-gpl-v2'
-licenses[['GPL-3']]         <- 'gnu-gpl-v3'
-licenses[['GPL (>= 2)']]    <- 'gnu-gpl-v2'
-licenses[['GPL-2 | GPL-3']] <- 'gnu-gpl-v2 OR gnu-gpl-v3'
-licenses[['LGPL-2']]        <- 'gnu-lgpl-v2'
-licenses[['LGPL-2.1']]      <- 'gnu-lgpl-v2.1'
-licenses[['LGPL (>= 2)']]   <- 'gnu-lgpl-v2'
-licenses[['LUCENT']]        <- 'lucent'
-licenses[['MIT']]           <- 'mit'
-licenses[['POSTGRESQL']]    <- 'postgresql-license'
+licenses[['ACM']]                                    <- 'acm-license'
+licenses[['ACM | file LICENSE']]                     <- 'acm-license	# OR file LICENSE'
+licenses[['APACHE']]                                 <- 'apache-1.1 OR apache-2.0'
+licenses[['Apache License 2.0']]                     <- 'apache-2.0'
+licenses[['Apache License (== 2.0)']]                <- 'apache-2.0'
+licenses[['Apache License (== 2.0) | file LICENSE']] <- 'apache-2.0	# OR file LICENSE'
+licenses[['ARTISTIC']]                               <- 'artistic OR artistic-2.0'
+licenses[['Artistic-2.0']]                           <- 'artistic-2.0'
+licenses[['BSD']]                                    <- '2-clause-bsd OR modified-bsd OR original-bsd'
+licenses[['BSD-2']]                                  <- '2-clause-bsd'
+licenses[['BSD_2_clause + file LICENSE']]            <- '2-clause-bsd	# + file LICENSE'
+licenses[['BSD 3 clause']]                           <- 'modified-bsd'
+licenses[['BSD 3 clause + file LICENSE']]            <- 'modified-bsd	# + file LICENSE'
+licenses[['BSD_3_clause + file LICENSE']]            <- 'modified-bsd	# + file LICENSE'
+licenses[['BSL-1.0']]                                <- 'boost-license'
+licenses[['CC0']]				     <- 'cc0-1.0-universal'
+licenses[['GPL']]                                    <- 'gnu-gpl-v1 OR gnu-gpl-v2 OR gnu-gpl-v3'
+licenses[['GPL-1']]                                  <- 'gnu-gpl-v1'
+licenses[['GPL-2']]                                  <- 'gnu-gpl-v2'
+licenses[['GPL-2 | file LICENSE']]                   <- 'gnu-gpl-v2	# OR file LICENSE'
+licenses[['GPL-3']]                                  <- 'gnu-gpl-v3'
+licenses[['GPL-2 | GPL-3']]                          <- 'gnu-gpl-v2 OR gnu-gpl-v3'
+licenses[['GPL (>= 2)']]                             <- 'gnu-gpl-v2 OR gnu-gpl-v3'
+licenses[['GPL (>= 2.0)']]                           <- 'gnu-gpl-v2 OR gnu-gpl-v3'
+licenses[['GPL (>= 2) | file LICENSE']]              <- 'gnu-gpl-v2 OR gnu-gpl-v3	# OR file LICENSE'
+licenses[['GPL (>= 3)']]                             <- 'gnu-gpl-v3'
+licenses[['LGPL']]                                   <- 'gnu-lgpl-v2 OR gnu-lgpl-v2.1 OR gnu-lgpl-v3'
+licenses[['LGPL-2']]                                 <- 'gnu-lgpl-v2'
+licenses[['LGPL-2.1']]                               <- 'gnu-lgpl-v2.1'
+licenses[['LGPL-3']]                                 <- 'gnu-lgpl-v3'
+licenses[['LGPL-2 | LGPL-3']]                        <- 'gnu-lgpl-v2 OR gnu-lgpl-v3'
+licenses[['LGPL (>= 2)']]                            <- 'gnu-lgpl-v2 OR gnu-lgpl-v2.1 OR gnu-lgpl-v3'
+licenses[['LUCENT']]                                 <- 'lucent'
+licenses[['Lucent Public License']]                  <- 'lucent'
+licenses[['MIT']]                                    <- 'mit'
+licenses[['MIT + file LICENSE']]                     <- 'mit	# + file LICENSE'
+licenses[['MIT + file LICENSE | Unlimited']]         <- 'mit	# + file LICENSE OR unlimited'
+licenses[['MPL-1.0']]                                <- 'mpl-1.0'
+licenses[['MPL-1.1']]                                <- 'mpl-1.1'
+licenses[['MPL-2.0']]                                <- 'mpl-2.0'
+licenses[['MPL-2.0 | file LICENSE']]                 <- 'mpl-2.0	# OR file LICENSE'
+licenses[['POSTGRESQL']]                             <- 'postgresql-license'
+
+adjacent.duplicates <- function(x)
+{
+  a <- x[-length(x)]
+  b <- x[-1]
+  dups <- a == b
+  dups <- c(FALSE,dups)
+  dups
+}
 
 paste2 <- function(s1,s2)
 {
@@ -192,6 +285,278 @@
   if (!is.na(s1) && !is.na(s2)) return (paste(s1,s2))
 }
 
+end.paragraph <- function(l,l1=l,l2=list())
+{
+  if (length(l1) > 0 || length(l2) > 0)
+    l <- append(l,'')
+  l
+}
+
+as.sorted.list <- function(df)
+{
+  l <- list()
+  df <- df[!duplicated(df),]
+  if (nrow(df) > 0)
+    {
+      key <- as.vector(df[,1])
+      value <- as.vector(df[,2])
+      key <- order(key,value)
+      l <- as.list(value[key])
+    }
+  l
+}
+
+read.file.as.dataframe <- function(filename)
+{
+  # message('===> read.file.as.dataframe(',filename,')')
+  contents <- as.list(readLines(filename))
+  df <- data.frame()
+  for (line in contents)
+    {
+      # str(line)
+      df <- rbind(df,data.frame(line=line,stringsAsFactors=FALSE))
+    }
+  df
+}
+
+categorize.key_value <- function(df,line='line')
+{
+  re.skip_blank <- '[[:blank:]]*'
+  re.blank <- '[[:blank:]]+'
+  re.anything <- '.*'
+
+  re.key <- '[^+=[:blank:]]+'
+  re.operator <- '[+=]+'
+  re.delimiter <- re.skip_blank
+  re.value <- re.anything
+  re.optional_TODO <- '(#[[:blank:]]*TODO[[:blank:]]*:[[:blank:]]*)*'
+
+  re.match_key_value_line <- paste0('^',
+    re.skip_blank,
+    re.optional_TODO,
+    re.key,
+    re.skip_blank,
+    re.operator,
+    re.delimiter,
+    re.value,
+    '\$')
+
+  re.match_key <- paste0('^',
+    re.skip_blank,
+    re.optional_TODO,
+    '(',re.key,')',
+    re.skip_blank,
+    re.operator,
+    re.delimiter,
+    re.value,
+    '\$')
+
+  df\$key_value <- grepl(re.match_key_value_line,df[,line])
+  df\$key <- sub(re.match_key,'\\\2',df[,line])
+  df\$key[!df\$key_value] <- NA
+  df
+}
+
+categorize.depends <- function(df,line='line')
+{
+  df\$depends <- df\$key_value & df\$key == 'DEPENDS'
+  df\$category[df\$depends] <- unlist(sapply(strsplit(df[df\$depends,line],'/',fixed=TRUE),'[',3))
+  df
+}
+
+categorize.buildlink <- function(df,line='line')
+{
+  df\$buildlink3.mk <- grepl('buildlink3.mk',df[,line])
+  df\$category[df\$buildlink3.mk] <- unlist(sapply(strsplit(df[df\$buildlink3.mk,line],'/',fixed=TRUE),'[',3))
+  df
+}
+
+fix.continued.lines <- function(df,line='line')
+{
+  if (nrow(df) > 1)
+    {
+      continued <- grepl('\\\\\\\\$',df[,line])
+      continued_key_value <- df\$key_value & continued
+      if (FALSE %in% df[continued,'key_value'])
+        {
+	  message('[ ${LEVEL} ] WARNING: unhandled continued line(s)')
+	}
+      for (i in 1:(length(continued_key_value)-1))
+        {
+	  next_line <- i + 1
+	  if (continued_key_value[i])
+	    {
+	      df[i,line] <- sub('[[:blank:]]*\\\\\\\\$','',df[i,line])
+	      df\$key_value[next_line] <- TRUE
+	      df\$key[next_line] <- df\$key[i]
+	      df[next_line,line] <- paste0(df\$key[next_line],'+=',df[next_line,line])
+	    }
+	}
+    }
+  df
+}
+
+read.Makefile.as.dataframe <- function()
+{
+  # message('===> read.Makefile.as.dataframe():')
+
+  re.skip_blank <- '[[:blank:]]*'
+  re.blank <- '[[:blank:]]+'
+  re.anything <- '.*'
+
+  re.key <- '[^+=[:blank:]]+'
+  re.operator <- '[+=]+'
+  re.delimiter <- re.skip_blank
+  re.value <- re.anything
+  re.optional_TODO <- '(#[[:blank:]]*TODO[[:blank:]]*:[[:blank:]]*)*'
+
+  re.match_operator <- paste0('^',
+    re.skip_blank,
+    re.optional_TODO,
+    re.key,
+    re.skip_blank,
+    '(',re.operator,')',
+    re.delimiter,
+    re.value,
+    '\$')
+  re.match_delimiter <- paste0('^',
+    re.skip_blank,
+    re.optional_TODO,
+    re.key,
+    re.skip_blank,
+    re.operator,
+    '(',re.delimiter,')',
+    re.value,
+    '\$')
+  re.match_value <- paste0('^',
+    re.skip_blank,
+    re.optional_TODO,
+    re.key,
+    re.skip_blank,
+    re.operator,
+    re.delimiter,
+    '(',re.value,')',
+    '\$')
+  re.match_optional_TODO <- paste0('^',
+    re.skip_blank,
+    '(',re.optional_TODO,')',
+    re.key,
+    re.skip_blank,
+    re.operator,
+    re.delimiter,
+    re.value,
+    '\$')
+
+  df <- read.file.as.dataframe('Makefile.orig')
+
+  df\$order <- 1:nrow(df)
+  df\$category <- NA
+
+  df <- categorize.key_value(df)
+  df <- fix.continued.lines(df)
+  df <- categorize.depends(df)
+  df <- categorize.buildlink(df)
+
+  df\$operator <- sub(re.match_operator,'\\\2',df\$line)
+  df\$delimiter <- sub(re.match_delimiter,'\\\2',df\$line)
+  df\$old_value <- sub(re.match_value,'\\\2',df\$line)
+  df\$old_todo <- sub(re.match_optional_TODO,'\\\1',df\$line)
+
+  df\$operator[!df\$key_value] <- NA
+  df\$delimiter[!df\$key_value] <- NA
+  df\$old_value[!df\$key_value] <- NA
+  df\$old_todo[!df\$key_value] <- NA
+
+  df
+}
+
+read.file.as.list <- function(filename)
+{
+  result <- list()
+  info <- file.info(filename)
+  size <- info[filename,'size']
+  if (!is.na(size) && size > 0)
+    {
+       contents <- readLines(filename)
+       result <- as.list(contents)
+    }
+  result
+}
+
+read.file.as.value <- function(filename)
+{
+  value <- ''
+  l <- read.file.as.list(filename)
+  if (length(l) == 1)
+    {
+      line <- l[[1]]
+      fields <- strsplit(line,'[[:blank:]]+')
+      value <- fields[[1]][2]
+    }
+  value
+}
+
+read.file.as.values <- function(filename)
+{
+  message('===> read.file.as.values(',filename,'):')
+  values <- list()
+  l <- read.file.as.list(filename)
+  print(l)
+  for (line in l)
+    {
+      # fields <- strsplit(line,'[[:blank:]]+')
+      # value <- fields[[1]][2]
+    }
+  print(values)
+  values
+}
+
+simplify.whitespace <- function(s) { gsub('[[:blank:]]+',' ',s) }
+remove.punctuation <- function(s)
+{
+  punctuation <- '[,-]'
+  gsub(punctuation,'',s)
+}
+remove.quotes <- function(s)
+{
+  quotes <- '[\'\`"]'
+  gsub(quotes,'',s)
+}
+remove.articles <- function(s)
+{
+  pattern <- '^([[:blank:]]*)An* |([[:blank:]]+)[Aa]n*[[:blank:]]+'
+  result <- gsub(pattern,'\\\1',s)
+  result
+}
+
+case.insensitive.equals <- function(s1,s2)
+{
+  s1.lower <- tolower(simplify.whitespace(s1))
+  s2.lower <- tolower(simplify.whitespace(s2))
+  result <- s1.lower == s2.lower
+  result
+}
+
+weakly.equals <- function(s1,s2)
+{
+  result <- case.insensitive.equals(remove.articles(remove.quotes(remove.punctuation(s1))),
+                                    remove.articles(remove.quotes(remove.punctuation(s2))))
+  result
+}
+
+new.field.if.different <- function(filename,s)
+{
+  field <- field(filename,one.line(s))
+  field.list <- read.file.as.list(filename)
+  if (length(field.list) == 1)
+    {
+      f <- field.list[[1]]
+      if (case.insensitive.equals(f,field))
+        field <- f
+    }
+  field
+}
+
 todo.license <- function(s)
 {
   if (is.null(licenses[[s]]))
@@ -209,30 +574,108 @@
   license
 }
 
-package <- function(s) field('R_PKGNAME',one.line(s))
-version <- function(s) field('R_PKGVER',one.line(s))
-comment <- function(s) field('COMMENT',one.line(s))
-license <- function(s) field(todo.license(s),pkgsrc.license(s))
+package <- function(s) one.line(s)
+version <- function(s) one.line(s)
+comment <- function(s) one.line(s)
+use.tools <- function(s) read.file.as.list(s)
+
+license <- function(s)
+{
+  license <- pkgsrc.license(s)
+  old.license <- read.file.as.value('LICENSE')
+  if (old.license != '' && old.license != license)
+    license <- paste0(license,'	# previously: ',old.license)
+  license
+}
+
 maintainer <- function(email)
-  {
-     if (file.exists('MAINTAINER'))
-       {
-         x <- scan('MAINTAINER','character',quiet=TRUE)
-         if (length(x) == 2)
-           email = x[2]
-         else
-           message('WARNING: previous MAINTAINER is ignored')
-       }
-     email <- paste0('MAINTAINER=	',email)
-     email
-   }
+{
+  MAINTAINER <- read.file.as.value('MAINTAINER')
+  if (MAINTAINER == '')
+    MAINTAINER <- email
+  MAINTAINER
+}
+
+make.sed.command <- function(key,value)
+{
+  address <- paste0('/^[[:blank:]]*',key,'/')
+  match <- paste0('(',key,'[[:blank:]]*=[[:blank:]]*).*$')
+  replacement <- paste0('\\\1',value)
+  command <- paste0(' -e "',address,'s/',match,'/',replacement,'/"')
+  command
+}
+
+sed.categories <- function(categories) make.sed.command('CATEGORIES',categories)
+sed.comment <- function(comment)
+{
+  old.comment <- read.file.as.value('COMMENT')
+  if (weakly.equals(old.comment,comment))
+    comment <- old.comment
+  make.sed.command('COMMENT',comment)
+}
+sed.maintainer <- function(email)
+{
+  make.sed.command('MAINTAINER',maintainer(email))
+}
+sed.license <- function(license)
+{
+  make.sed.command('LICENSE',license)
+}
+sed.r_pkgver <- function(r_pkgver) make.sed.command('R_PKGVER',r_pkgver)
 
-categories <- function() paste('CATEGORIES=',paste(basename(dirname(getwd())),'R'),sep='	')
+buildlink3.mk <- function(s1,s2)
+{
+  BUILDLINK3.MK <- data.frame()
+  buildlink3.mk.list <- read.file.as.list('BUILDLINK3.MK')
+  for (line in buildlink3.mk.list)
+    {
+      fields <- strsplit(line[[1]],'/',fixed=TRUE)
+      key <- fields[[1]][3]
+      value <- line
+      BUILDLINK3.MK <- rbind(BUILDLINK3.MK,data.frame(key=key,value=value))
+    }
+  if (find.Rcpp(s1,s2))
+    {
+      buildlink3.line <- '.include "../../devel/R-Rcpp/buildlink3.mk"'
+      key <- 'devel'
+      value <- buildlink3.line
+      BUILDLINK3.MK <- rbind(BUILDLINK3.MK,data.frame(key=key,value=value))
+    }
+  BUILDLINK3.MK
+}
+
+makefile.field <- function(key,value)
+{
+  # message('===> makefile.field(',key,',',value,'):')
+  field <- paste0(key,'=	',value)
+  # print(field)
+  field
+}
+
+makefile.fields <- function(key,values)
+{
+  # message('===> makefile.fields():')
+  fields <- list()
+  for (l in values)
+    {
+      value <- unlist(l)
+      # message('===> value=',value,' ',length(value),' ',value == '')
+      # print(value)
+      if (value != '')
+        fields <- append(fields,makefile.field(key,list(value)))
+      else
+        fields <- append(fields,list(''))
+      # print(fields)
+    }
+  # print(fields)
+  fields
+}
+
+categories <- function() paste(basename(dirname(getwd())),'R')
 description <- function(s) strwrap(s,width=71)
 
 filter.imports <- function(s)
 {
-  base.packages <- c('R','MASS','Matrix','Rcpp','cluster','grDevices','graphics','grid','foreign','lattice','nlme','methods','nnet','parallel','rpart','stats','survival','tools','utils')
   for (pkg in base.packages)
     {
       re.pkg <- paste('^',pkg,sep='')
@@ -241,6 +684,13 @@
   s
 }
 
+find.Rcpp <- function(s1,s2)
+{
+  s <- paste(s1,s2)
+  Rcpp <- grepl('Rcpp',s)
+  Rcpp
+}
+
 make.imports <- function(s1,s2)
 {
   s <- paste2(s1,s2)
@@ -254,112 +704,221 @@
 make.dependency <- function(s)
 {
   s <- gsub('\\\\)','',s)
+  s <- gsub('-','.',s)
   s <- unlist(strsplit(s,'\\\\('))
   s
 }
 
-depends <- function(s1,s2)
+depends <- function(dependency) dependency[1]
+
+depends.pkg <- function(dependency)
+{
+  # XXX message('===> ',depends(dependency))
+  result <- Sys.glob(paste0('../../*/R-',depends(dependency)))
+  result
+}
+
+new.depends.pkg <- function(dependency)
+{
+  result <- Sys.glob(paste0('../../wip/R-',depends(dependency)))
+  result
+}
+
+depends.pkg.fullname <- function(dependency,index=1)
+{
+  result <- system(paste('cd',depends.pkg(dependency)[index],'&& bmake show-var VARNAME=PKGNAME'),intern=TRUE)
+  result
+}
+
+depends.pkg.name <- function(dependency,index=1)
+{
+  result <- sub('^(.*)-([^-]*)$','\\\\1',depends.pkg.fullname(dependency,index))
+  result
+}
+
+depends.pkg.vers <- function(dependency,index=1)
+{
+  result <- sub('^(.*)-([^-]*)$','\\\\2',depends.pkg.fullname(dependency,index))
+  result
+}
+
+depends.vers <- function(dependency,index=1)
+{
+  if (length(dependency) == 2)
+    result <- dependency[2]
+  else
+    result <- paste0('>=',depends.pkg.vers(dependency,index))
+  result <- trim.space(result)
+  result
+}
+
+depends.vers.2 <- function(dependency)
+{
+  result <- ifelse(length(dependency) == 2, dependency[2], '>=???')
+  result <- trim.space(result)
+  result
+}
+
+depends.dir <- function(dependency,index=1)
+{
+  fields <- strsplit(depends.pkg(dependency)[index],'/',fixed=TRUE)
+  result <- fields[[1]][3]
+  result
+}
+
+depends.line <- function(dependency,index=1)
+{
+  result <- paste0('DEPENDS+=\tR-',depends(dependency),depends.vers(dependency,index),':',depends.pkg(dependency)[index])
+  result
+}
+
+depends.line.2 <- function(dependency)
+{
+  result <- paste0('DEPENDS+=\tR-',depends,depends.vers.2(dependency),':../../???/R-',depends)
+  result <- paste0(result,'	# XXX - found')
+  for (pkg in depends.pkg(dependency))
+    result <- paste(result,pkg)
+  result
+}
+
+buildlink3.file <- function(dependency,index=1)
+{
+  result <- paste0(depends.pkg(dependency)[index],'/buildlink3.mk')
+  result
+}
+
+buildlink3.line <- function(dependency,index=1)
+{
+  result <- paste0('.include "',buildlink3.file(dependency,index),'"')
+  result
+}
+
+dependency.dir <- function(dependency)
+{
+  result <- paste0('../../wip/R-',depends(dependency))
+  result
+}
+
+message.wip.dependency <- function(dependency,index=1)
+{
+  dir <- depends.dir(dependency,index)
+  dir.in.wip <- grepl('wip',dir)
+  wd.in.wip <- grepl('/wip/',getwd())
+  if (dir.in.wip && !wd.in.wip)
+    message('[ ${LEVEL} ] WARNING: R-${RPKG} should not depend on a wip package: ',depends.pkg(dependency)[index])
+}
+
+message.too.many.dependencies <- function(dependency)
+{
+  msg <- paste0('[ ${LEVEL} ] WARNING: too many dependencies found for ',depends(dependency),':')
+  for (pkg in depends.pkg(dependency))
+    msg <- paste(msg,pkg)
+  msg
+}
+
+update.dependency <- function(dependency,index=1)
+{
+  message('[ ${LEVEL} ] WARNING: updating dependency for ',depends(dependency),': ',depends.pkg(dependency)[index])
+  grep <- paste0('grep -E -q -e "',depends(dependency),'" ${PACKAGES_LIST}')
+  command <- paste0(grep,' || (cd ',depends.pkg(dependency)[index],' && ${R2PKG} ${ARGS} ',depends(dependency),')')
+  error <- system(command)
+  if (error != 0)
+    message('[ ${LEVEL} ] WARNING: error updating dependency for ',depends(dependency))
+}
+
+make.depends <- function(s1,s2)
 {
   imports <- make.imports(s1,s2)
+  # XXX  message('===> imports:')
+  # XXX print(imports)
   DEPENDS <- data.frame()
-  BUILDLINK3.MK <- data.frame()
+  BUILDLINK3.MK <- buildlink3.mk(s1,s2)
   if (length(imports) > 0)
     {
       for (i in 1:length(imports))
         {
           dependency <- make.dependency(imports[i])
-          depends <- dependency[1]
-          depends.pkg <- Sys.glob(paste('../../*/R-',depends,sep=''))
-          if (length(depends.pkg) == 0) # a dependency cannot be found
+	  # XXX message('[ ',${LEVEL},' ] ===> ',i,' / ',length(imports),': ',depends(dependency))
+          if (length(depends.pkg(dependency)) == 0) # a dependency cannot be found
             {
-	      message('WARNING: creating the dependency',depends)
+	      message('[ ${LEVEL} ] 0 dependencies match ',dependency)
               if (${RECURSIVE})
-                {
-                  dependency.dir <- paste('../../wip/R-',depends,sep='')
-                  dir.create(path=dependency.dir,recursive=TRUE)
-                  error <- system(paste('(cd',dependency.dir,'&& ${R2PKG} ${ARGS}',depends,')'))
-                  if (error != 0)
-                    file.remove(dependency.dir)
-                }
+	        {
+                  dir.create(path=dependency.dir(dependency),recursive=TRUE)
+ 		  update.dependency(dependency)
+ 		}
+              else
+                message('[ ${LEVEL} ] WARNING: dependency needed for ',depends(dependency))
             }
-          depends.pkg <- Sys.glob(paste('../../*/R-',depends,sep=''))
-	  if (length(depends.pkg) == 0) # no dependency was created
-	    message('WARNING: the dependency',depends,'does not exist')
-          else if (length(depends.pkg) == 1) # a unique dependency found
+	  else if (length(depends.pkg(dependency)) == 1) # a unique dependency found
             {
-	      fields <- strsplit(depends.pkg,'/',fixed=TRUE)
-	      depends.dir <- fields[[1]][3]
-	      buildlink3.mk <- paste(depends.pkg,'/buildlink3.mk',sep='')
-	      if (file.exists(buildlink3.mk))
-                {
-                  buildlink3.line <- paste('.include "',buildlink3.mk,'"',sep='')
-                  BUILDLINK3.MK <- rbind(BUILDLINK3.MK,data.frame(key=depends.dir,value=buildlink3.line))
-                }
+	      message('[ ${LEVEL} ] 1 dependency matches ',dependency,': ',depends.pkg(dependency))
+	      message.wip.dependency(dependency)
+              if (${RECURSIVE} && ${UPDATE})
+	        update.dependency(dependency)
+	      if (file.exists(buildlink3.file(dependency)))
+                BUILDLINK3.MK <- rbind(BUILDLINK3.MK,data.frame(key=depends.dir(dependency),value=buildlink3.line(dependency)))
+	      else
+                DEPENDS <- rbind(DEPENDS,data.frame(key=depends.dir(dependency),value=depends.line(dependency)))
+            }
+	  else if (length(depends.pkg(dependency)) == 2) # two dependencies found
+	    {
+	      d <- depends.pkg(dependency)
+	      index <- grep('/wip/',d,invert=TRUE)
+	      message('[ ${LEVEL} ] 2 dependencies match ',dependency,':',paste(' ',depends.pkg(dependency)))
+	      # message('===> depends(dependency): ',depends(dependency))
+	      # message('===> depends.pkg(dependency):',paste(' ',d))
+	      # message('===> index: ',index)
+	      # message('===> buildlinke.line(): ',buildlink3.line(dependency,index))
+	      if (length(index) == 1) # a unique, non-wip, dependency found
+	        {
+		  message('[ ${LEVEL} ] choosing unique non-wip dependency for ',dependency,': ',depends.pkg(dependency)[index])
+		  if (${RECURSIVE} && ${UPDATE})
+	            update.dependency(dependency,index)
+	      	  if (file.exists(buildlink3.file(dependency,index)))
+                    BUILDLINK3.MK <- rbind(BUILDLINK3.MK,data.frame(key=depends.dir(dependency,index),value=buildlink3.line(dependency,index)))
+		  else
+                    DEPENDS <- rbind(DEPENDS,data.frame(key=depends.dir(dependency,index),value=depends.line(dependency,index)))
+		}
 	      else
 	        {
-                  depends.pkg.fullname <- system(paste('cd',depends.pkg,'&& bmake show-var VARNAME=PKGNAME'),intern=TRUE)
-                  depends.pkg.name <- sub('^(.*)-([^-]*)$','\\\\1',depends.pkg.fullname)
-                  depends.pkg.vers <- sub('^(.*)-([^-]*)$','\\\\2',depends.pkg.fullname)
-                  if (length(dependency) == 2)
-                    depends.vers <- dependency[2]
-                  else
-                    depends.vers <- paste('>=',depends.pkg.vers,sep='')
-                  depends.line <- paste('DEPENDS+=\tR-',depends,depends.vers,':',depends.pkg,sep='')
-                  depends.line <- paste(depends.line,'	# XXX - found ',depends.pkg.fullname,' (',depends.pkg,')',sep='')
-                  DEPENDS <- rbind(DEPENDS,data.frame(key=depends.dir,value=depends.line))
-                }
-            }
-          else			# more than 1 dependency found
+		  message('[ ${LEVEL} ] no unique non-wip dependency matches')
+		  message(message.too.many.dependencies(dependency))
+              	  DEPENDS <- rbind(DEPENDS,data.frame(key='???',value=depends.line.2(dependency)))
+		}
+	    }
+          else			# more than 2 dependencies found
             {
-	      msg <- paste('WARNING: too many dependencies found for ',depends,':',sep='')
-	      for (pkg in depends.pkg)
-	        msg <- paste(msg,' ',pkg,sep='')
-	      message(msg)
-              depends.vers <- ifelse(length(dependency) == 2, dependency[2], '>=???')
-              depends.vers <- trim.space(depends.vers)
-              depends.line <- paste('DEPENDS+=\tR-',depends,depends.vers,':../../???/R-',depends,sep='')
- 	      depends.line <- paste(depends.line,'	# XXX - found',sep='')
-	      for (pkg in depends.pkg)
- 	        depends.line <- paste(depends.line,' ',pkg,sep='')
-              DEPENDS <- rbind(DEPENDS,data.frame(key='???',value=depends.line))
+	      message('[ ${LEVEL} ] ',length(depends.pkg(dependency)),' dependencies match ',dependency,':',paste(' ',depends.pkg(dependency)))
+ 	      message(message.too.many.dependencies(dependency))
+              DEPENDS <- rbind(DEPENDS,data.frame(key='???',value=depends.line.2(dependency)))
             }
-          new.depends.pkg <- Sys.glob(paste('../../wip/R-',depends,sep=''))
-          if (length(new.depends.pkg) > 0)
-            system(paste('echo',depends,'${RPKG} >> ${DEPENDENCY_LIST}'))
+          if (length(new.depends.pkg(dependency)) > 0)
+            system(paste('echo',depends(dependency),'${RPKG} >> ${DEPENDENCY_LIST}'))
         }
     }
-  if (nrow(DEPENDS) > 0)
-    {
-      key <- as.vector(DEPENDS[,1])
-      value <- as.vector(DEPENDS[,2])
-      key <- order(key,value)
-      DEPENDS <- as.list(value[key])
-      if (length(DEPENDS) > 0)
-        DEPENDS <- append(DEPENDS,'')
-    }
-  else
-    DEPENDS = list()
-  if (nrow(BUILDLINK3.MK) > 0)
-    {
-      key <- as.vector(BUILDLINK3.MK[,1])
-      value <- as.vector(BUILDLINK3.MK[,2])
-      key <- order(key,value)
-      BUILDLINK3.MK <- as.list(value[key])
-    }
-  else
-    BUILDLINK3.MK <- list()
-  list(DEPENDS,BUILDLINK3.MK)
+  DEPENDS <- as.sorted.list(DEPENDS)
+  DEPENDS <- end.paragraph(DEPENDS)
+  BUILDLINK3.MK <- as.sorted.list(BUILDLINK3.MK)
+  result <- list(DEPENDS,BUILDLINK3.MK)
+  result
 }
 
 use.languages <- function(s1,s2)
 {
+#  message('===> use.languages(',s1,',',s2,'):')
+#  USE_LANGUAGES <- read.file.as.values('USE_LANGUAGES')
+#  if (length(USE_LANGUAGES) == 0)
+#    {
+#      if (find.Rcpp(s1,s2))
+#        USE_LANGUAGES <- append(USE_LANGUAGES,list('USE_LANGUAGES+=	c c++'))
+#    }
   USE_LANGUAGES <- list()
-  s <- paste(s1,s2)
-  Rcpp <- grepl('Rcpp',s)
-  if (Rcpp)
-    USE_LANGUAGES <- append(USE_LANGUAGES,list('USE_LANGUAGES+=	c c++'))
-  if (length(USE_LANGUAGES) > 0)
-    USE_LANGUAGES <- append(USE_LANGUAGES,'')
+  if (find.Rcpp(s1,s2))
+    USE_LANGUAGES <- append(USE_LANGUAGES,list('c c++'))
+  if (length(USE_LANGUAGES) == 0)
+    USE_LANGUAGES <- '# none'
+  USE_LANGUAGES <- end.paragraph(USE_LANGUAGES)
   USE_LANGUAGES
 }
 
@@ -369,53 +928,437 @@
   writeLines(description,con='DESCRIPTION')
 }
 
+write.Makefile <- function(metadata)
+{
+  RCSID             <- '# \$NetBSD\$'
+  CATEGORIES        <- makefile.field('CATEGORIES',categories())
+  MAINTAINER        <- makefile.field('MAINTAINER',maintainer('${MAINTAINER_EMAIL}'))
+  HOMEPAGE          <- makefile.field('HOMEPAGE','\${R_HOMEPAGE_BASE}/${RPKG}/')
+  COMMENT           <- makefile.field('COMMENT',comment(metadata[3]))
+  LICENSE           <- makefile.field('LICENSE',license(metadata[5]))
+  R_PKGNAME         <- makefile.field('R_PKGNAME',package(metadata[1]))
+  R_PKGVER          <- makefile.field('R_PKGVER',version(metadata[2]))
+  USE_LANGUAGES     <- makefile.fields('USE_LANGUAGES',use.languages(metadata[6],metadata[7]))
+  DEPENDENCIES      <- make.depends(metadata[6],metadata[7])
+  DEPENDS	    <- DEPENDENCIES[1]
+  BUILDLINK3.MK     <- DEPENDENCIES[2]
+  INCLUDE.R         <- '.include "../../math/R/Makefile.extension"'
+  INCLUDE.PKG       <- '.include "../../mk/bsd.pkg.mk"'
+
+  Makefile <- list()
+  Makefile <- append(Makefile,RCSID)
+  Makefile <- append(Makefile,'')
+  Makefile <- append(Makefile,CATEGORIES)
+  Makefile <- append(Makefile,'')
+  Makefile <- append(Makefile,MAINTAINER)
+  Makefile <- append(Makefile,HOMEPAGE)
+  Makefile <- append(Makefile,COMMENT)
+  Makefile <- append(Makefile,LICENSE)
+  Makefile <- append(Makefile,'')
+  Makefile <- append(Makefile,R_PKGNAME)
+  Makefile <- append(Makefile,R_PKGVER)
+  Makefile <- append(Makefile,'')
+  Makefile <- append(Makefile,DEPENDS)
+  Makefile <- append(Makefile,USE_LANGUAGES)
+  Makefile <- append(Makefile,INCLUDE.R)
+  Makefile <- append(Makefile,BUILDLINK3.MK)
+  Makefile <- append(Makefile,INCLUDE.PKG)
+  Makefile <- paste(unlist(Makefile),collapse='\n')
+
+  write(Makefile,'Makefile')
+}
+
+construct.line <- function(df,key,value)
+{
+  key <- df[df\$key==key,'key']
+  operator <- df[df\$key==key,'operator']
+  delimiter <- df[df\$key==key,'delimiter']
+  value <- df[df\$key==key,value]
+  df\$new_line[df\$key==key] <- paste0(key,operator,delimiter,value)
+  df
+}
+
+element <- function(df,key,value,quiet=FALSE)
+{
+  key.index <- match(key,df\$key,0)
+  if (key.index != 0 && df\$key_value[key.index])
+    result <- df[key.index,value]
+  else
+    {
+      result <- '???'
+      if (!quiet)
+        {
+          if (key.index == 0)
+            message('[ ${LEVEL} ] WARNING: ',key,' not found')
+          else
+            message('[ ${LEVEL} ] WARNING: ',key,' is not a key-value field')
+	}
+    }
+  result
+}
+
+make.categories <- function(df)
+{
+  # message('===> make.categories():')
+  directory <- basename(dirname(getwd()))
+  categories <- unlist(element(df,'CATEGORIES','old_value'))
+  categories <- unlist(strsplit(categories,'[[:blank:]]+'))
+  categories <- c(directory,categories,'R')
+  if (directory != 'wip')
+    categories <- categories[ categories != 'wip' ]
+  categories <- categories[!duplicated(categories)]
+  categories <- paste(categories,collapse=' ')
+  categories
+}
+
+make.maintainer <- function(df)
+{
+  old.maintainer <- element(df,'MAINTAINER','old_value')
+  new.maintainer <- element(df,'MAINTAINER','new_value')
+  maintainer <- ifelse(old.maintainer == '',new.maintainer,old.maintainer)
+  maintainer
+}
+
+make.comment <- function(df)
+{
+  old.comment <- element(df,'COMMENT','old_value')
+  new.comment <- element(df,'COMMENT','new_value')
+  comment <- old.comment
+  if (!weakly.equals(old.comment,new.comment))
+    comment <- paste0(comment,'	# updated to: ',new.comment)
+  comment
+}
+
+make.new_license <- function(df,license)
+{
+  new_license <- licenses[[license]]
+  if (is.null(new_license))
+      new_license <- license
+  df\$new_value[df\$key == 'LICENSE'] <- new_license
+  df
+}
+
+license.marked.todo <- function(todo) { todo != '' }
+license.in.pkgsrc <- function(license) { license %in% sapply(licenses,'[',1) }
+
+make.license <- function(df)
+{
+  # message('===> make.license():')
+  old_license <- element(df,'LICENSE','old_value')
+  old_todo <- element(df,'LICENSE','old_todo')
+  new_license <- element(df,'LICENSE','new_value')
+
+  if (license.in.pkgsrc(old_license) && license.in.pkgsrc(new_license))
+    {
+      if (case.insensitive.equals(old_license,new_license))
+        {
+          license <- old_license
+          todo <- old_todo
+        }
+      else
+        {
+          license <- paste0(new_license,'	# previously: ',old_license)
+          todo <- old_todo
+        }
+    }
+  else if (license.in.pkgsrc(old_license) && !license.in.pkgsrc(new_license))
+    {
+      license <- paste0(old_license,'	# updated to: ',new_license)
+      todo <- '# TODO: '
+    }
+  else if (!license.in.pkgsrc(old_license) && license.in.pkgsrc(new_license))
+    {
+      license <- paste0(new_license,'	# previously: ',old_license)
+      todo <- ''
+    }
+  else if (!license.in.pkgsrc(old_license) && !license.in.pkgsrc(new_license))
+    {
+      license <- paste0(new_license,'	# previously: ',old_license)
+      todo <- '# TODO: '
+    }
+
+  df\$value[df\$key == 'LICENSE'] <- license
+  df\$todo[df\$key == 'LICENSE'] <- todo
+
+  df
+}
+
+make.r_pkgver <- function(df) element(df,'R_PKGVER','new_value')
+
+find.order <- function(df,key,field)
+{
+  x <- df[,key]
+  value <- match(TRUE,x)
+  if (!is.na(value))
+    value <- df[value,field]
+  value
+}
+
+write.makefile <- function(lines) write(lines,'Makefile')
+
+update.Makefile.with.metadata <- function(df,metadata)
+{
+  # message('===> update.Makefile.with.metadata():')
+
+  df\$new_value <- NA
+
+  df <- make.new_license(df,metadata[5])
+
+  df\$new_value[df\$key == 'CATEGORIES'] <- categories()
+  df\$new_value[df\$key == 'MAINTAINER'] <- '${MAINTAINER_EMAIL}'
+  df\$new_value[df\$key == 'COMMENT'] <- one.line(metadata[3])
+  df\$new_value[df\$key == 'R_PKGVER'] <- version(metadata[2])
+
+  # str(df)
+  # print(df)
+  df
+}
+
+update.Makefile.with.new.values <- function(df)
+{
+  # message('===> update.Makefile.with.new.values():')
+  df\$value <- NA
+  df\$todo <- ''
+  df <- make.license(df)
+  df\$value[df\$key == 'CATEGORIES'] <- make.categories(df)
+  df\$value[df\$key == 'MAINTAINER'] <- make.maintainer(df)
+  df\$value[df\$key == 'COMMENT'] <- make.comment(df)
+  df\$value[df\$key == 'R_PKGVER'] <- make.r_pkgver(df)
+
+  # str(df)
+  # print(df)
+  df
+}
+
+update.Makefile.with.new.line <- function(df)
+{
+  # message('===> update.Makefile.with.new.line():')
+  df\$new_line <- NA
+
+  construct_key_value <- df\$key_value & !is.na(df\$value)
+  df\$new_line[construct_key_value] <-
+    paste0(df\$todo[construct_key_value],
+           df\$key[construct_key_value],
+           df\$operator[construct_key_value],
+           df\$delimiter[construct_key_value],
+           df\$value[construct_key_value])
+
+  copy_line <- !df\$key_value | !construct_key_value
+  df\$new_line[copy_line] <- df\$line[copy_line]
+
+  # str(df)
+  # print(df)
+  df
+}
+
+annotate.distname.in.Makefile <- function(df)
+{
+  match <- grepl('^[[:blank:]]*DISTNAME',df\$new_line)
+  line <- df\$new_line[match]
+  value <- sub('^[[:blank:]]*DISTNAME[[:blank:]]*=[[:blank:]]*','',line)
+  pkgname <- sub('_.+$','',value)
+  pkgver <- sub('^.+_','',value)
+  PKGNAME <- paste0('R_PKGNAME=',pkgname)
+  PKGVER <- paste0('R_PKGVER=',pkgver)
+  comment <- paste0('	# XXX -- replace this line with ',PKGNAME,' and ',PKGVER,' as third stanza (and rerun R2pkg)')
+  df\$new_line[match] <- paste0(line,comment)
+  df
+}
+
+annotate.Makefile <- function(df)
+{
+  df <- annotate.distname.in.Makefile(df)
+  df
+}
+
+remove.master.sites.from.Makefile <- function(df)
+{
+  match <- grepl('^[[:blank:]]*MASTER_SITES',df\$new_line)
+  df <- df[!match,]
+  df
+}
+
+remove.buildlink.abi.depends.from.Makefile <- function(df)
+{
+  match <- grepl('^[[:blank:]]*BUILDLINK_ABI_DEPENDS',df\$new_line)
+  df <- df[!match,]
+  df
+}
+
+remove.buildlink.api.depends.from.Makefile <- function(df)
+{
+  match <- grepl('^[[:blank:]]*BUILDLINK_API_DEPENDS',df\$new_line)
+  df <- df[!match,]
+  df
+}
+
+remove.lines.from.Makefile <- function(df)
+{
+  df <- remove.master.sites.from.Makefile(df)
+  df <- remove.buildlink.abi.depends.from.Makefile(df)
+  df <- remove.buildlink.api.depends.from.Makefile(df)
+  df
+}
+
+conflicts <- function(pkg)
+{
+  conflict <- paste0('R>=',R_version())
+  conflicts <- list()
+  if (pkg %in% base.packages)
+    {
+      conflicts <- append(conflicts,makefile.field('CONFLICTS',conflict))
+      conflicts <- end.paragraph(conflicts)
+    }
+  conflicts
+}
+
+conflicts.order <- function(df)
+{
+  order <- element(df,'R_PKGNAME','order')
+  order
+}
+
+make.df.conflicts <- function(df,metadata)
+{
+  df.conflicts <- data.frame()
+  conflicts.exist <- element(df,'CONFLICTS','old_value',quiet=TRUE) != '???'
+  if (!conflicts.exist)
+    {
+      c <- conflicts(metadata[1])
+      order <- conflicts.order(df)
+      order <- order + 2.5
+      i <- 0
+      for (conflict in c)
+        {
+          i <- i + 1
+          category <- as.character(i)
+          depends <- FALSE
+          buildlink3.mk <- FALSE
+          x <- data.frame(new_line=conflict,order=order,category=category,depends=depends,buildlink3.mk=buildlink3.mk)
+          df.conflicts <- rbind(df.conflicts,x)
+        }
+    }
+  df.conflicts
+}
+
+make.df.depends <- function(df,DEPENDS)
+{
+  # message('===> make.df.depends():')
+  # str(df)
+  # print(df)
+  df.depends <- data.frame()
+  if (TRUE %in% df\$depends)
+    df.depends <- data.frame(new_line=df[df\$depends,'line'],stringsAsFactors=FALSE)
+  for (line in DEPENDS)
+    df.depends <- rbind(df.depends,data.frame(new_line=line,stringsAsFactors=FALSE))
+  if (nrow(df.depends) > 0)
+    {
+      df.depends\$category <- NA
+      df.depends\$buildlink3.mk <- FALSE
+      df.depends <- categorize.key_value(df.depends,'new_line')
+      df.depends <- categorize.depends(df.depends,'new_line')
+      df.depends\$key_value <- NULL
+      df.depends\$key <- NULL
+      df.depends <- df.depends[!duplicated(df.depends),]
+      df.depends\$order <- find.order(df,'depends','order')
+    }
+  # message('===> df.depends:')
+  # str(df.depends)
+  # print(df.depends)
+  df.depends
+}
+
+make.df.buildlink3 <- function(df,BUILDLINK3.MK)
+{
+  # message('===> make.df.buildlink3():')
+  df.buildlink3.mk <- data.frame()
+  if (TRUE %in% df\$buildlink3.mk)
+    df.buildlink3.mk <- data.frame(new_line=df[df\$buildlink3.mk,'line'],stringsAsFactors=FALSE)
+  for (line in BUILDLINK3.MK)
+    df.buildlink3.mk <- rbind(df.buildlink3.mk,data.frame(new_line=line,stringsAsFactors=FALSE))
+  if (nrow(df.buildlink3.mk) > 0)
+    {
+      df.buildlink3.mk\$category <- NA
+      df.buildlink3.mk\$depends <- FALSE
+      df.buildlink3.mk <- categorize.buildlink(df.buildlink3.mk,'new_line')
+      df.buildlink3.mk <- df.buildlink3.mk[!duplicated(df.buildlink3.mk),]
+      df.buildlink3.mk\$order <- find.order(df,'buildlink3.mk','order')
+    }
+  # str(df.buildlink3.mk)
+  # print(df.buildlink3.mk)
+  df.buildlink3.mk
+}
+
+make.df.makefile <- function(df,df.conflicts,df.depends,df.buildlink3.mk)
+{
+  # message('===> make.df.makefile():')
+  # message('===> df:')
+  # str(df)
+  # print(df)
+  fields <- c('new_line','order','category','depends','buildlink3.mk')
+  df.makefile <- df[!df\$depends & !df\$buildlink3.mk,fields]
+  df.makefile <- rbind(df.makefile,df.conflicts,df.depends,df.buildlink3.mk)
+  df.makefile <- df.makefile[order(df.makefile\$order,df.makefile\$category,df.makefile\$new_line),]
+  df.makefile <- df.makefile[!adjacent.duplicates(df.makefile\$new_line),]
+  df.makefile
+}
+
+update.Makefile <- function(metadata)
+{
+  DEPENDENCIES  <- make.depends(metadata[6],metadata[7])
+  DEPENDS       <- DEPENDENCIES[[1]]
+  BUILDLINK3.MK <- DEPENDENCIES[[2]]
+  # message('===> DEPENDS:')
+  # str(DEPENDS)
+  # print(DEPENDS)
+  # message('===> BUILDLINK3.MK:')
+  # str(BUILDLINK3.MK)
+  # print(BUILDLINK3.MK)
+
+  # message('===> df:')
+  df <- read.Makefile.as.dataframe()
+  df <- update.Makefile.with.metadata(df,metadata)
+  df <- update.Makefile.with.new.values(df)
+  df <- update.Makefile.with.new.line(df)
+  df <- annotate.Makefile(df)
+  df <- remove.lines.from.Makefile(df)
+
+  df.conflicts <- make.df.conflicts(df,metadata)
+  df.depends <- make.df.depends(df,DEPENDS)
+  df.buildlink3 <- make.df.buildlink3(df,BUILDLINK3.MK)
+  df.makefile <- make.df.makefile(df,df.conflicts,df.depends,df.buildlink3)
+
+  write.makefile(df.makefile[,'new_line'])
+}
+
+create.Makefile <- function(metadata)
+{
+  if (${UPDATE} == 1 && file.exists('Makefile.orig'))
+    update.Makefile(metadata)
+  else
+    write.Makefile(metadata)
+}
+
+create.DESCR <- function(metadata)
+{
+  DESCR <- description(metadata[4])
+  write(DESCR,'DESCR')
+}
+
+set.locale()
+
 error <- download.file(url='${RPKG_DESCRIPTION_URL}',destfile='DESCRIPTION',quiet=${QUIET_CURL},method='curl')
 if (error)
-  quit(status=error)
+  {
+    message('ERROR: Downloading the DESCRIPTION file for ${RPKG} failed;')
+    message('       perhaps the package no longer exists?')
+    quit(save='no',status=error)
+  }
 
 metadata <- read.dcf(file='DESCRIPTION', fields=c('Package','Version','Title','Description','License','Imports','Depends'))
 
-CVS               <- '# \$NetBSD\$'
-CATEGORIES        <- categories()
-MASTER.SITES      <- 'MASTER_SITES=	\${MASTER_SITE_R_CRAN:=contrib/}'
-MAINTAINER        <- maintainer('${MAINTAINER_EMAIL}')
-HOMEPAGE          <- 'HOMEPAGE=	\${R_HOMEPAGE_BASE}/${RPKG}/'
-COMMENT           <- comment(metadata[3])
-LICENSE           <- license(metadata[5])
-R_PKGNAME         <- package(metadata[1])
-R_PKGVER          <- version(metadata[2])
-USE_LANGUAGES     <- use.languages(metadata[6],metadata[7])
-DEPENDENCIES      <- depends(metadata[6],metadata[7])
-DEPENDS		  <- DEPENDENCIES[1]
-BUILDLINK3.MK     <- DEPENDENCIES[2]
-INCLUDE.R         <- '.include "../../math/R/Makefile.extension"'
-INCLUDE.PKG       <- '.include "../../mk/bsd.pkg.mk"'
-
-DESCR        <- description(metadata[4])
-
-Makefile <- list()
-Makefile <- append(Makefile,CVS)
-Makefile <- append(Makefile,'')
-Makefile <- append(Makefile,CATEGORIES)
-Makefile <- append(Makefile,MASTER.SITES)
-Makefile <- append(Makefile,'')
-Makefile <- append(Makefile,MAINTAINER)
-Makefile <- append(Makefile,HOMEPAGE)
-Makefile <- append(Makefile,COMMENT)
-Makefile <- append(Makefile,LICENSE)
-Makefile <- append(Makefile,'')
-Makefile <- append(Makefile,R_PKGNAME)
-Makefile <- append(Makefile,R_PKGVER)
-Makefile <- append(Makefile,'')
-Makefile <- append(Makefile,USE_LANGUAGES)
-Makefile <- append(Makefile,DEPENDS)
-Makefile <- append(Makefile,INCLUDE.R)
-Makefile <- append(Makefile,BUILDLINK3.MK)
-Makefile <- append(Makefile,INCLUDE.PKG)
-Makefile <- paste(unlist(Makefile),collapse='\n')
-
-write(Makefile,'Makefile')
-write(DESCR,'DESCR')
+create.Makefile(metadata)
+create.DESCR(metadata)
 EOF
     eval ${R_CMD}
     retval=${?}
@@ -444,35 +1387,139 @@
     ${ECHO_FETCH} "==> Fetching R-${RPKG} ..."
     MAKE_CMD="${MAKE} makesum ${STDOUT_MAKESUM}"
     eval ${MAKE_CMD}
-    return ${?}
+    error=${?}
+    if [ ${error} -eq 0 ]; then
+	MAKE_CMD="${MAKE} makepatchsum ${STDOUT_MAKESUM}"
+	eval ${MAKE_CMD}
+	error=${?}
+    fi
+    return ${error}
+}
+
+create_buildlink3_mk ()
+{
+    if [ -f buildlink3.mk.orig ]; then
+	PKGVERSION=$(${MAKE} show-var VARNAME=PKGVERSION)
+	sed -E -e "/BUILDLINK_API_DEPENDS\./s/[[:digit:].]+$/${PKGVERSION}/" \
+	    buildlink3.mk.orig > buildlink3.mk
+    fi
 }
 
 extract ()
 {
-    ${ECHO_EXTRACT} "==> Extracting R-${RPKG} ..."
-    MAKE_CMD="${MAKE} extract ${STDOUT_EXTRACT}"
+    ${ECHO_EXTRACT} "[ ${LEVEL} ] Extracting R-${RPKG} ..."
+    MAKE_CMD="env SKIP_DEPENDS=yes ${MAKE} clean extract ${STDOUT_EXTRACT}"
     eval ${MAKE_CMD}
 }
 
+check_license ()
+{
+    rm -f LICENSE
+    # echo '===> LICENSE files:'
+    if [ -f work/*/LICENSE ]; then
+	grep -v "^YEAR: " work/*/LICENSE \
+	    | grep -v "^COPYRIGHT HOLDER: " \
+	    | grep -v "^ORGANIZATION: " \
+	    > LICENSE
+	if [ -s LICENSE ]; then
+	    # ninka -d LICENSE
+	    cp work/*/LICENSE .
+	    /bin/echo -n "[ ${LEVEL} ] Current license: "
+	    grep LICENSE Makefile
+	    echo "[ ${LEVEL} ] Please check it against the following:"
+	    cat LICENSE
+	else
+	    rm LICENSE
+	    sed -E -e 's/[[:blank:]]+#[[:blank:]]+\+ file LICENSE[[:blank:]]+.*$//' Makefile > Makefile.$$ \
+		&& mv Makefile.$$ Makefile
+	    grep -q "file LICENSE" Makefile && echo "[ ${LEVEL} ] 'file LICENSE' in Makefile but no relevant license information"
+	fi
+    fi
+}
+
+check_copying ()
+{
+    if [ -f work/*/COPYING ]; then
+	cp work/*/COPYING .
+    fi
+}
+
+cleanup_DESCR ()
+{
+    if [ -f DESCR -a -f DESCR.orig ]; then
+	if diff --ignore-case --ignore-all-space --ignore-blank-lines DESCR.orig DESCR > /dev/null; then
+	    mv DESCR.orig DESCR
+	else
+	    mv DESCR DESCR.new
+	    mv DESCR.orig DESCR
+	fi
+    elif [ -f DESCR.orig ]; then
+	mv DESCR.orig DESCR
+    fi
+}
+
+cleanup_Makefile ()
+{
+    if [ -f Makefile -a -f Makefile.orig ]; then
+	diff --ignore-case --ignore-all-space --ignore-blank-lines Makefile.orig Makefile > /dev/null \
+	    && mv Makefile.orig Makefile
+    elif [ -f Makefile.orig ]; then
+	mv Makefile.orig Makefile
+    else
+	echo "[ ${LEVEL} ] $(pwd): neither Makefile nor Makefile.orig"
+    fi
+}
+
+cleanup_buildlink3 ()
+{
+    if [ buildlink3.mk -a -f buildlink3.mk.orig ]; then
+	diff --ignore-case --ignore-all-space --ignore-blank-lines buildlink3.mk.orig buildlink3.mk > /dev/null \
+	    && mv buildlink3.mk.orig buildlink3.mk
+    elif [ -f buildlink3.mk.orig ]; then
+	mv buildlink3.mk.orig buildlink3.mk
+    fi
+}
+
+cleanup_distinfo ()
+{
+    if [ -f distinfo -a -f distinfo.orig ]; then
+	tail +2 distinfo.orig > ${TMPDIR}/distinfo.orig.${PID}
+	tail +2 distinfo > ${TMPDIR}/distinfo.${PID}
+	cmp -s ${TMPDIR}/distinfo.orig.${PID} ${TMPDIR}/distinfo.${PID} \
+	    && mv distinfo.orig distinfo
+	rm -f ${TMPDIR}/distinfo.orig.${PID} ${TMPDIR}/distinfo.${PID}
+    elif [ -f distinfo.orig ]; then
+	mv distinfo.orig distinfo
+    fi
+}
+
+cleanup_misc_files ()
+{
+    [ "X${DESCRIPTION}" != "X" ] || rm -f DESCRIPTION
+    rm -f ${R_FILE}
+    rm -f CATEGORIES
+    rm -f COMMENT
+    rm -f MAINTAINER
+    rm -f USE_LANGUAGES
+    rm -f USE_TOOLS
+    rm -f DEPENDS
+    rm -f BUILDLINK3.MK
+    [ ${LEVEL} -eq 0 ] && rm -f ${PACKAGES_LIST}
+    [ ${LEVEL} -eq 0 ] && rm -f ${DEPENDENCY_LIST}
+}
+
 cleanup ()
 {
-    [ "X${DESCRIPTION}" != "X" ] || rm -f DESCRIPTION
-    if [ -f DESCR.orig ] && cmp -s DESCR.orig DESCR; then
-	mv DESCR.orig DESCR
-    fi
-    if [ -f Makefile.orig ] && cmp -s Makefile.orig Makefile; then
-	mv Makefile.orig Makefile
-    fi
-    if [ -f distinfo.orig ] && cmp -s distinfo.orig distinfo; then
-	mv distinfo.orig distinfo
-    fi
-    rm -f ${R_FILE}
-    rm -f MAINTAINER
+    cleanup_DESCR
+    cleanup_Makefile
+    cleanup_buildlink3
+    cleanup_distinfo
+    cleanup_misc_files
 }
 
 messages ()
 {
-    if [ ${QUIET} -eq 0 -a ${TOP_LEVEL} -ne 0 ]; then
+    if [ ${QUIET} -eq 0 -a ${LEVEL} -eq 0 ]; then
 	cat << EOF
 
 Please do not forget the following:
@@ -482,6 +1529,8 @@
   o verify the LICENSE.
   o verify the DEPENDS, especially the categories.
 EOF
+	[ -f buildlink3.mk ] && echo "- check buildlink3.mk"
+
 	[ "X${DESCRIPTION}" != "X" ] && echo "- remove DESCRIPTION."
 	if [ ${RECURSIVE} -ne 0 ]; then
 	    cat << EOF
@@ -497,7 +1546,6 @@
 		tsort ${DEPENDENCY_LIST} > depends
 		echo "- It may be useful to test these packages in the following order:"
 		awk 'BEGIN{printf(" ")} {printf(" R-%s",$0)}' depends && echo
-		[ ${TOP_LEVEL} -eq 0 ] || rm -f ${DEPENDENCY_LIST}
 	    fi
 	fi
     fi
@@ -505,17 +1553,26 @@
 
 ${ECHO_BANNER} "${BANNER_MSG} ..."
 check_for_R
-preserve_original_files
+check_for_no_recursion
+preserve_original_content
 make_package
 error=${?}
 if [ ${error} -eq 0 ]; then
     edit_Makefile
+    error=${?}; [ ${error} -eq 0 ] || exit ${error}
     edit_DESCR
+    error=${?}; [ ${error} -eq 0 ] || exit ${error}
     create_distinfo
-    error=${?}; [ ${error} -eq 0 ] || exit ${error}
-    # extract
+    create_buildlink3_mk
+    extract
+    check_license
+    check_copying
 fi
+messages
 cleanup
-messages
-${ECHO_DONE} "${BANNER_MSG}: done"
+if [ ${error} -eq 0 ]; then
+    ${ECHO_DONE} "${BANNER_MSG}: completed successfully"
+else
+    ${ECHO_DONE} "${BANNER_MSG}: FAILED"
+fi
 exit ${error}
--- a/pkgtools/R2pkg/files/RELEASE	Thu Aug 01 12:58:31 2019 +0000
+++ b/pkgtools/R2pkg/files/RELEASE	Thu Aug 01 13:11:08 2019 +0000
@@ -1,4 +1,4 @@
-$NetBSD: RELEASE,v 1.1 2019/06/24 13:46:04 brook Exp $
+$NetBSD: RELEASE,v 1.2 2019/08/01 13:11:08 brook Exp $
 
 RELEASE
 =======
@@ -28,3 +28,17 @@
 
 * Use buildlink3.mk files for dependencies when available.
 
+R2pkg v.0.5 (2019-07-31)
+------------------------
+
+* Improve support for updating packages.
+
+* Add explicit option to create packages (-c).
+
+* Relax comparisons for COMMENTS to allow case and some punctuation
+  differences.
+
+* Expand the list of licenses and base R packages checked for.
+
+* Improve support for recursion.
+