source: trunk/coin-functions @ 1818

Last change on this file since 1818 was 1818, checked in by lou, 10 years ago

CoinBazaar? follows the pattern of Data, etc.; not a `normal' project.

File size: 17.3 KB
Line 
1
2# Utility function definitions for the various COIN scripts.
3
4# Copyright (c) 2010 Lou Hafer  Simon Fraser University
5# All Rights Reserved.
6# This file is distributed under the Common Public License.
7# It is part of the BuildTools project in COIN-OR (www.coin-or.org)
8#
9# $Id$
10
11# Functions to disassemble svn URLs and extract relevant bits, and utilities
12# to find the best (most recent) stable and release URLs, calculate libtool
13# version numbers, and compare URLs for compatibility based on major and minor
14# version. Complicated because we have to account for trunk/stable/release and
15# for the variant syntax of Data and ThirdParty URLs. For your normal project,
16# it's
17#   https://projects.coin-or.org/svn/ProjName/trunk
18#   https://projects.coin-or.org/svn/ProjName/stable/M.m
19#   https://projects.coin-or.org/svn/ProjName/releases/M.m.r
20# with the proviso that sometimes it's
21#   https://projects.coin-or.org/svn/ProjName/SubProj/trunk
22# etc.
23# For ThirdParty, it's just like a normal project, except the prefix string
24# is longer:
25#   https://projects.coin-or.org/svn/BuildTools/ThirdParty/ProjName
26# Data is the real pain. Prior to 20101103 (1.0.x series), it had this form:
27#  https://projects.coin-or.org/svn/Data/trunk/ProjName
28#  https://projects.coin-or.org/svn/Data/stable/M.m/ProjName
29#  https://projects.coin-or.org/svn/Data/releases/M.m.r/ProjName
30# After 20101103 (1.1 and subsequent), it uses the same layout as ThirdParty,
31# namely <svn prefix>/Data/Projname/<trunk, stable, releases>. Macros below
32# will cope, but really you should be using pre-1.1 Data anyway.
33
34# Extract the COIN base from the URL, up to svn. Just in case it ever changes.
35# usage: baseURL=`extractBaseURL $svnURL`
36
37extractBaseURL ()
38{
39  exbu_baseURL=`echo $1 | sed -n -e 's|\(.*/svn\)/.*$|\1|p'`
40  echo "$exbu_baseURL"
41}
42
43
44# Extract branch type (trunk/stable/release) from the URL.
45# usage: branchType=`extractTypeFromURL $svnURL`
46
47extractTypeFromURL ()
48{
49  etfu_type="invalid"
50  case "$1" in
51    */trunk* ) etfu_type=trunk ;;
52    */stable/* ) etfu_type=stable ;;
53    */releases/* ) etfu_type=releases ;;
54  esac
55  echo $etfu_type
56}
57
58# Determine if this is a `normal' URL, defined as not Data, ThirdParty, or
59# BuildTools itself. ThirdParty lives in BuildTools as of 100628, so the
60# condition is redundant. Returns yes or no.
61# usage: isNormal=`isNormalURL $svnURL`
62
63isNormalURL ()
64{ case $1 in
65    */BuildTools/* | */ThirdParty/* | */Data/* )
66       echo "no"
67       ;;
68    *) echo "yes"
69       ;;
70  esac
71}
72
73# Extract the project from a svn URL. The trick, for `normal' projects, is to
74# handle CHiPPS, which has CHiPPS/Alps, Bcps, Blis. We can't assume the
75# project name does not contain '/'. The heuristic is to look for everything
76# between svn and one of trunk, stable, releases, or end of line.
77# usage: projName=`extractProjFromURL $svnURL`
78
79extractProjFromURL ()
80{
81  epfu_URL="$1"
82  epfu_projPat='\([^/][^/]*\)'
83  epfu_sfxPat='.*$'
84  case "$epfu_URL" in
85    */Data/trunk/* )
86        epfu_pfxPat=svn/Data/trunk/
87        ;;
88    */Data/stable/* | */Data/releases/* )
89        epfu_pfxPat='svn/Data/[^/][^/]*/[0-9][0-9.]*/'
90        ;;
91    */Data/* )
92        epfu_pfxPat='svn/Data/'
93        ;;
94    */ThirdParty/* )
95        epfu_pfxPat=svn/BuildTools/ThirdParty/
96        ;;
97    *)  epfu_type=`extractTypeFromURL $epfu_URL`
98        epfu_pfxPat='svn/'
99        epfu_projPat='\(.*\)'
100        case $epfu_type in
101          trunk | stable | releases )
102              epfu_sfxPat=$epfu_type'.*$'
103              ;;
104          * )
105              epfu_sfxPat='$'
106              ;;
107        esac
108        ;;
109  esac
110  epfu_projName=`echo $epfu_URL | sed -n -e 's|.*/'$epfu_pfxPat$epfu_projPat$epfu_sfxPat'|\1|p'`
111  epfu_projName=`echo $epfu_projName | sed -e 's|/$||'`
112  echo "$epfu_projName"
113}
114
115# Extract the tail (directory) from a URL that specifies an external. Relevant
116# only for normal projects, where the external will be a subdirectory
117# of the project. Returns null for Data / ThirdParty / BuildTools.
118# usage: projName=`extractTailFromExt $extURL`
119
120extractTailFromExt ()
121{
122  etfe_tail=
123  case "$1" in
124    */Data/* )
125        ;;
126    */BuildTools/ThirdParty/* )
127        ;;
128    */BuildTools/* )
129        ;;
130    */CoinBazaar/* )
131        ;;
132    *)
133        etfe_pfxPat=
134        case "$1" in
135          */trunk/* )
136              etfe_pfxPat='.*/trunk/'
137              ;;
138          */stable/* )
139              etfe_pfxPat='.*/stable/[0-9][0-9.]*/'
140              ;;
141          */releases/* )
142              etfe_pfxPat='.*/releases/[0-9][0-9.]*/'
143              ;;
144        esac
145        etfe_tail=`echo $1 | sed -n -e 's|'$etfe_pfxPat'\(.*\)|\1|p'`
146        ;;
147  esac
148  echo "$etfe_tail"
149}
150
151
152# CppAD uses a version string of the form YYYMMDD.rr. What a pain in the ass.
153# Hence the scattered exceptions below.
154# Extract the entire version string from a stable or release URL. Returns a
155# null string if handed a trunk URL or if there's no version in the URL. The
156# version number must have at least major.minor to match.
157# usage: version=`extractVersionFromURL $svnURL`
158
159extractVersionFromURL ()
160{
161  evfu_URL="$1"
162  if expr "$evfu_URL" : '.*/stable/.*' >/dev/null 2>&1 ||
163     expr "$evfu_URL" : '.*/releases/.*' >/dev/null 2>&1 ; then
164    if expr "$evfu_URL" : '.*/CppAD/.*' >/dev/null 2>&1 ; then
165      evfu_verPat='\([0-9][0-9.]*\)'
166    else
167      evfu_verPat='\([0-9][0-9]*\.[0-9][0-9.]*\)'
168    fi
169    evfu_sfxPat='.*$'
170    evfu_proj=`extractProjFromURL $evfu_URL`
171    case "$evfu_URL" in
172      */Data/stable/* | */Data/releases/* )
173          evfu_pfxPat='svn/Data/[^/][^/]*/'
174          ;;
175      */ThirdParty/* )
176          evfu_pfxPat='svn/BuildTools/ThirdParty/[^/][^/]*/[^/][^/]*/'
177          ;;
178      */Data/* )
179          evfu_pfxPat='svn/Data/'$evfu_proj'/[^/][^/]*/'
180          ;;
181      *)
182          evfu_pfxPat='svn/'$evfu_proj'/[^/][^/]*/'
183          ;;
184    esac
185    evfu_verVer=`echo $1 | sed -n -e 's|.*/'$evfu_pfxPat$evfu_verPat$evfu_sfxPat'|\1|p'`
186    echo "$evfu_verVer"
187  else
188    echo ""
189  fi
190}
191
192# Replace the entire version string from a stable or release URL. A trunk
193# URL will be unmodified. newRev will not be accessed unless the URL is a
194# release. The version must have at least major.minor to match.
195# usage: newURL=`replaceVersionInURL $oldURL $newMajor $newMinor $newRev`
196# and just for CppAD,
197# usage: newURL=`replaceVersionInURL $oldURL $newMajor $newRev`
198
199replaceVersionInURL ()
200{
201  if expr "$1" : '.*/CppAD/.*' >/dev/null 2>&1 ; then
202    isCppAD=yes
203  else
204    isCppAD=no
205  fi
206  if test $isCppAD = no ; then
207    rviu_verPat='[0-9][0-9]*\.[0-9][0-9.]*'
208  else
209    rviu_verPat='[0-9][0-9.]*'
210  fi
211
212  if expr "$1" : '.*/stable/.*' >/dev/null 2>&1 ; then
213    if test $isCppAD = no ; then
214      rviu_newVersion=$2.$3
215    else
216      rviu_newVersion=$2
217    fi
218  elif expr "$1" : '.*/releases/.*' >/dev/null 2>&1 ; then
219    if test $isCppAD = no ; then
220      rviu_newVersion=$2.$3.$4
221    else
222      rviu_newVersion=$2.$3
223    fi
224  else
225    rviu_newVersion=
226    rviu_newURL=$1
227  fi
228  if test -n "$rviu_newVersion" ; then
229    rviu_newURL=`echo $1 | sed -n -e 's|^\(.*\)/'$rviu_verPat'\(.*\)$|\1/'$rviu_newVersion'\2|p'`
230  fi
231  echo $rviu_newURL
232}
233
234# Extract the major version number from a stable or release URL. Returns -1 if
235# handed a trunk URL or if there's no version in the URL
236# usage: majVer=`extractMajorFromURL $svnURL`
237
238extractMajorFromURL ()
239{
240  ejfu_URL="$1"
241  if expr "$ejfu_URL" : '.*/stable/.*' >/dev/null 2>&1 ||
242     expr "$ejfu_URL" : '.*/releases/.*' >/dev/null 2>&1 ; then
243    ejfu_majPat='\([0-9][0-9]*\)'
244    if expr "$ejfu_URL" : '.*/CppAD/.*' >/dev/null 2>&1 ; then
245      ejfu_sfxPat='.*$'
246    else
247      ejfu_sfxPat='\.[0-9][0-9]*.*$'
248    fi
249    ejfu_proj=`extractProjFromURL $ejfu_URL`
250    case "$ejfu_URL" in
251      */Data/stable/* | */Data/releases/* )
252          ejfu_pfxPat='svn/Data/[^/][^/]*/'
253          ;;
254      */ThirdParty/* )
255          ejfu_pfxPat='svn/BuildTools/ThirdParty/[^/][^/]*/[^/][^/]*/'
256          ;;
257      */Data/* )
258          ejfu_pfxPat='svn/Data/'$ejfu_proj'/[^/][^/]*/'
259          ;;
260      *)
261          ejfu_pfxPat='svn/'$ejfu_proj'/[^/][^/]*/'
262          ;;
263    esac
264    ejfu_majVer=`echo $ejfu_URL | sed -n -e 's|.*/'$ejfu_pfxPat$ejfu_majPat$ejfu_sfxPat'|\1|p'`
265    echo "$ejfu_majVer"
266  else
267    echo "-1"
268  fi
269}
270
271# Extract the minor version number from a stable or release URL. Returns -1 if
272# handed a trunk URL or if there's no version in the URL.
273# The CppAD form (YYYYMMDD.rr) doesn't have a minor version number.
274# usage: minVer=`extractMinorFromURL $svnURL`
275
276extractMinorFromURL ()
277{
278  emfu_URL="$1"
279  if expr "$emfu_URL" : '.*/CppAD/.*' >/dev/null 2>&1 ; then
280    echo "-1"
281  elif expr "$emfu_URL" : '.*/stable/.*' >/dev/null 2>&1 ||
282       expr "$emfu_URL" : '.*/releases/.*' >/dev/null 2>&1 ; then
283    emfu_minPat='[0-9][0-9]*\.\([0-9][0-9]*\)'
284    emfu_sfxPat='.*$'
285    emfu_proj=`extractProjFromURL $emfu_URL`
286    case "$emfu_URL" in
287      */Data/stable/* | */Data/releases/* )
288          emfu_pfxPat='svn/Data/[^/][^/]*/'
289          ;;
290      */ThirdParty/* )
291          emfu_pfxPat='svn/BuildTools/ThirdParty/[^/][^/]*/[^/][^/]*/'
292          ;;
293      */Data/* )
294          emfu_pfxPat='svn/Data/'$emfu_proj'/[^/][^/]*/'
295          ;;
296      *)
297          emfu_pfxPat='svn/'$emfu_proj'/[^/][^/]*/'
298          ;;
299    esac
300    emfu_minVer=`echo $emfu_URL | sed -n -e 's|.*/'$emfu_pfxPat$emfu_minPat$emfu_sfxPat'|\1|p'`
301    echo "$emfu_minVer"
302  else
303    echo "-1"
304  fi
305}
306
307# Extract the release (revision) number from a release URL. Returns -1 if
308# handed a trunk or stable URL
309# usage: minVer=`extractReleaseFromURL $svnURL`
310
311extractReleaseFromURL ()
312{
313  erfu_URL="$1"
314  if expr "$erfu_URL" : '.*/releases/.*' >/dev/null 2>&1 ; then
315    if expr "$erfu_URL" : '.*/CppAD/.*' >/dev/null 2>&1 ; then
316      erfu_relPat='[0-9][0-9]*\.\([0-9][0-9]*\)'
317    else
318      erfu_relPat='[0-9][0-9]*\.[0-9][0-9]*\.\([0-9][0-9]*\)'
319    fi
320    erfu_sfxPat='.*$'
321    erfu_proj=`extractProjFromURL $erfu_URL`
322    case "$erfu_URL" in
323      */Data/releases/* )
324          erfu_pfxPat='svn/Data/[^/][^/]*/'
325          ;;
326      */ThirdParty/* )
327          erfu_pfxPat='svn/BuildTools/ThirdParty/[^/][^/]*/[^/][^/]*/'
328          ;;
329      */Data/*)
330          erfu_pfxPat='svn/Data/'$erfu_proj'/[^/][^/]*/'
331          ;;
332      *)
333          erfu_pfxPat='svn/'$erfu_proj'/[^/][^/]*/'
334          ;;
335    esac
336    erfu_relVer=`echo $erfu_URL | sed -n -e 's|.*/'$erfu_pfxPat$erfu_relPat$erfu_sfxPat'|\1|p'`
337    echo "$erfu_relVer"
338  else
339    echo "-1"
340  fi
341}
342
343# Now some functions to locate the highest-numbered stable or release.
344
345# Return the URL of the most recent stable branch matching the parameters.
346# A value of -1 for major version is interpreted as `highest number'
347# Returns an empty string if no suitable stable branch exists.
348# usage: stableURL=`bestStable <URL> <major>`
349
350bestStable ()
351{ bstb_URL=$1
352  bstb_majVer=$2
353
354  # Construct a URL to send to the repository to list the stable branches.
355
356  bstb_baseURL=`extractBaseURL $bstb_URL`
357  bstb_proj=`extractProjFromURL $bstb_URL`
358
359  case "$bstb_URL" in
360    */Data/trunk/* )
361        bstb_listURL=$bstb_baseURL/Data/stable
362        ;;
363    */Data/* )
364        bstb_listURL=$bstb_baseURL/Data/$bstb_proj/stable
365        ;;
366    */ThirdParty/* )
367        bstb_listURL=$bstb_baseURL/BuildTools/ThirdParty/$bstb_proj/stable
368        ;;
369    * )
370        bstb_listURL=$bstb_baseURL/$bstb_proj/stable
371        ;;
372  esac
373
374  bstb_rawls=`svn list $bstb_listURL | sort -nr -t. -k1,1 -k2,2`
375
376  # echo "Raw ls: $bstb_rawls"
377
378  # Filter the list of stable branches to remove any that do not match the
379  # requested major version. Note that there's a / at the end of each URL.
380
381  if test "$bstb_majVer" = "-1" ; then
382    bstb_verPat='[0-9][0-9.]*/'
383  elif expr "$bstb_URL" : '.*/CppAD/.*' >/dev/null 2>&1 ; then
384    bstb_verPat=$bstb_majVer'/'
385  else
386    bstb_verPat=$bstb_majVer'\.[0-9][0-9]*'
387  fi
388  bstb_filter=
389  for bstb_ver in $bstb_rawls ; do
390    if  expr "$bstb_ver" : $bstb_verPat >/dev/null 2>&1 ; then
391      bstb_filter="$bstb_filter $bstb_ver"
392    fi
393  done
394
395  # echo "Filtered ls: $bstb_filter"
396
397  # If there are any candidates left ...
398
399  bstb_bestURL=
400  if test -n "$bstb_filter" ; then
401
402    # If we're dealing with old-style Data, we have to work a bit harder
403    # to find our project. See if any of the filtered candidates contain
404    # the correct project.
405
406    case "$bstb_URL" in
407      */Data/trunk/* | */Data/stable/* | */Data/releases/* )
408        bstb_projPat='.*'$bstb_proj'/.*'
409        # echo "Pattern: $bstb_projPat"
410        for bstb_ver in $bstb_filter ; do
411          if  expr "$bstb_ver" : $bstb_verPat >/dev/null 2>&1 ; then
412            # echo "  url: $bstb_listURL/$bstb_ver"
413            bstb_svnls2="`svn list $bstb_listURL/$bstb_ver`"
414            # echo "  result: $bstb_svnls2"
415            if expr "$bstb_svnls2" : $bstb_projPat >/dev/null 2>&1 ; then
416              bstb_best=$bstb_ver
417              # echo "  best: $bstb_best"
418              break
419            fi
420          fi
421        done
422        bstb_bestURL=$bstb_listURL/$bstb_best$bstb_proj
423      ;;
424      *)
425        bstb_bestURL=`echo $bstb_filter | sed -n -e 's|\([^/]*/\).*$|\1|p'`
426        bstb_bestURL=$bstb_listURL/$bstb_bestURL
427      ;;
428    esac
429  fi
430
431  echo $bstb_bestURL
432}
433
434# Return the URL of the most recent release matching the parameters. Returns
435# null if no suitable release exists.
436# A value of -1 for major or minor version is interpreted as `highest number'
437# A value of -1 for major version means that minor version is ignored.
438# usage: releaseURL=`bestRelease <URL> <major> <minor>`
439
440bestRelease ()
441{ bstb_URL=$1
442  bstb_majVer=$2
443  bstb_minVer=$3
444
445  # Construct a URL to send to the repository to list the releases.
446
447  bstb_baseURL=`extractBaseURL $bstb_URL`
448  bstb_proj=`extractProjFromURL $bstb_URL`
449
450  case "$bstb_URL" in
451    */Data/* )
452        bstb_listURL=$bstb_baseURL/Data/$bstb_proj/releases
453        ;;
454    */ThirdParty/* )
455        bstb_listURL=$bstb_baseURL/BuildTools/ThirdParty/$bstb_proj/releases
456        ;;
457    * )
458        bstb_listURL=$bstb_baseURL/$bstb_proj/releases
459        ;;
460  esac
461
462  bstb_rawls=`svn list $bstb_listURL | sort -nr -t. -k1,1 -k2,2 -k3,3`
463
464  # echo "Raw ls: $bstb_rawls"
465
466  # Filter the list of releases to remove any that do not match the
467  # requested major and minor version.
468
469  if expr "$bstb_URL" : '.*/CppAD/.*' >/dev/null 2>&1 ; then
470    isCppAD=yes
471  else
472    isCppAD=no
473  fi
474
475  if test "$bstb_majVer" = "-1" ; then
476    bstb_verPat='[0-9][0-9.]*/'
477  else
478    bstb_verPat=$bstb_majVer
479    if test $isCppAD = no &&
480       test "$bstb_minVer" != "-1" ; then
481      bstb_verPat="${bstb_verPat}\.$bstb_minVer"
482    fi
483    bstb_verPat="${bstb_verPat}"'\.[0-9][0-9]*/'
484  fi
485
486  # echo "Version pattern: $bstb_verPat"
487  bstb_filter=
488  for bstb_ver in $bstb_rawls ; do
489    if  expr "$bstb_ver" : $bstb_verPat >/dev/null 2>&1 ; then
490      bstb_filter="$bstb_filter $bstb_ver"
491    fi
492  done
493
494  # echo "Filtered ls: $bstb_filter"
495
496  # If there are any candidates left ...
497
498  bstb_bestURL=
499  if test -n "$bstb_filter" ; then
500
501    # If we're dealing with old-style Data, we have to work a bit harder to find our
502    # project. See if any of the filtered candidates contain the correct
503    # project.
504
505    case "$bstb_URL" in
506      */Data/trunk/* | */Data/stable/* | */Data/releases/* )
507        bstb_projPat='.*'$bstb_proj'/.*'
508        # echo "Pattern: $bstb_projPat"
509        for bstb_ver in $bstb_filter ; do
510          if  expr "$bstb_ver" : $bstb_verPat >/dev/null 2>&1 ; then
511            # echo "  url: $bstb_listURL/$bstb_ver"
512            bstb_svnls2="`svn list $bstb_listURL/$bstb_ver`"
513            # echo "  result: $bstb_svnls2"
514            if expr "$bstb_svnls2" : $bstb_projPat >/dev/null 2>&1 ; then
515              bstb_best=$bstb_ver
516              # echo "  best: $bstb_best"
517              break
518            fi
519          fi
520        done
521        bstb_bestURL=$bstb_listURL/$bstb_best$bstb_proj
522      ;;
523      *)
524        bstb_bestURL=`echo $bstb_filter | sed -n -e 's|\([^/]*/\).*$|\1|p'`
525        bstb_bestURL=$bstb_listURL/$bstb_bestURL
526      ;;
527    esac
528
529  fi
530
531  echo $bstb_bestURL
532}
533
534# Count the total number of stable branches for the project for the specified
535# major version number (libtool age). A major version number of -1 means all
536# stable branches (libtool current; see next function). Returns 0 if handed
537# a trunk url, or if the url is Data or BuildTools itself.
538# usage: calcLibtoolAge <svnURL> <major>
539
540calcLibtoolAge ()
541{ cltc_URL=$1
542  cltc_majVer=$2
543
544  # Construct a URL to send to the repository to list the stable branches.
545
546  cltc_baseURL=`extractBaseURL $cltc_URL`
547  cltc_proj=`extractProjFromURL $cltc_URL`
548
549  case "$cltc_URL" in
550    */Data/* )
551        cltc_listURL=
552        ;;
553    */ThirdParty/* )
554        cltc_listURL=$cltc_baseURL/BuildTools/ThirdParty/$cltc_proj/stable
555        ;;
556    */BuildTools/* )
557        cltc_listURL=
558        ;;
559    * )
560        cltc_listURL=$cltc_baseURL/$cltc_proj/stable
561        ;;
562  esac
563
564  # Run an ls and filter for standard stable branches (M.m or YYYYMMDD)
565
566  if test -n "$cltc_listURL" ; then
567    cltc_rawls=`svn list $cltc_listURL | sed -n -e '/[0-9][0-9.]/p'`
568
569    # echo "Raw ls: $cltc_rawls"
570
571    # Filter the list of stable branches to remove any that do not match the
572    # requested major version. -1 means `any major version', hence no filter.
573
574    if test "$cltc_majVer" != "-1" ; then
575      if expr "$cltc_URL" : '.*/CppAD/.*' >/dev/null 2>&1 ; then
576        cltc_verPat=$cltc_majVer
577      else
578        cltc_verPat=$cltc_majVer'\.[0-9][0-9]*'
579      fi
580    else
581      cltc_verPat='[0-9][0-9.]*'
582    fi
583    cltc_filter=
584    for cltc_ver in $cltc_rawls ; do
585      if  expr "$cltc_ver" : $cltc_verPat >/dev/null 2>&1 ; then
586        cltc_filter="$cltc_filter $cltc_ver"
587      fi
588    done
589
590    # echo "Filtered ls: $cltc_filter"
591
592    cltc_cnt=`echo $cltc_filter | wc -w | sed -e 's/ //g'`
593    cltc_cnt=`expr $cltc_cnt - 1`
594  else
595    cltc_cnt=0
596  fi
597
598  echo $cltc_cnt
599}
600
601# Function to compare the versions in a pair of URLs for equality. The criteria
602# is that the major and minor versions match. In theory, the release should
603# not matter. Probably should be a parameter.
604# usage: compareURLVersions <url1> <url2>
605
606compareURLVersions ()
607{
608  url1_major=`extractMajorFromURL $1`
609  url1_minor=`extractMinorFromURL $1`
610  url2_major=`extractMajorFromURL $2`
611  url2_minor=`extractMinorFromURL $2`
612
613  if test $url1_major = $url2_major &&
614     test $url1_minor = $url2_minor ; then
615    echo "yes"
616  else
617    echo "no"
618  fi
619}
620
Note: See TracBrowser for help on using the repository browser.