home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume22 / cvs-berliner / part03 < prev    next >
Encoding:
Internet Message Format  |  1990-06-07  |  53.6 KB

  1. Subject:  v22i017:  Brian Berliner's concurrent RCS system, Part03/07
  2. Newsgroups: comp.sources.unix
  3. Approved: rsalz@uunet.UU.NET
  4. X-Checksum-Snefru: 733d95c3 9eff69b1 aaa84987 3f924963
  5.  
  6. Submitted-by: Brian Berliner <berliner@prisma.com>
  7. Posting-number: Volume 22, Issue 17
  8. Archive-name: cvs-berliner/part03
  9.  
  10. #! /bin/sh
  11. # This is a shell archive.  Remove anything before this line, then unpack
  12. # it by saving it into a file and typing "sh file".  To overwrite existing
  13. # files, type "sh file -c".  You can also feed this as standard input via
  14. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  15. # will see the following message at the end:
  16. #        "End of archive 3 (of 7)."
  17. # Contents:  src/checkin.csh src/collect_sets.c src/main.c
  18. #   src/modules.c src/subr.c src/version_number.c
  19. # Wrapped by rsalz@litchi.bbn.com on Thu May  3 16:59:02 1990
  20. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  21. if test -f 'src/checkin.csh' -a "${1}" != "-c" ; then 
  22.   echo shar: Will not clobber existing file \"'src/checkin.csh'\"
  23. else
  24. echo shar: Extracting \"'src/checkin.csh'\" \(7570 characters\)
  25. sed "s/^X//" >'src/checkin.csh' <<'END_OF_FILE'
  26. X#!/bin/csh
  27. X#
  28. X# $Id: checkin.csh,v 1.8 89/11/20 13:37:33 berliner Exp $
  29. X#
  30. X#   Copyright (c) 1989, Brian Berliner
  31. X#
  32. X#   You may distribute under the terms of the GNU General Public License
  33. X#   as specified in the README file that comes with the CVS 1.0 kit.
  34. X#
  35. X#############################################################################
  36. X#                                        #
  37. X# This script is used to check in sources from vendors.              #
  38. X#                                        #
  39. X#    Usage: checkin repository Vendor_Tag Vendor_Release_Tag            #
  40. X#                                        #
  41. X# The repository is the directory where the sources should            #
  42. X# be deposited, the Vendor_Tag is the symbolic tag for the             #
  43. X# vendor branch of the RCS release tree, and the Vendor_Release_Tag        #
  44. X# is the symbolic tag for this release.                      #
  45. X#                                        #
  46. X# checkin traverses the current directory, ensuring that an             #
  47. X# identical directory structure exists in the repository directory.  It        #
  48. X# then checks the files in in the following manner:                #
  49. X#                                        #
  50. X#        1) If the file doesn't yet exist, check it in             #
  51. X#            as revision 1.1                        #
  52. X#        2) Tag branch 1.1.1 with the vendor tag                #
  53. X#        3) Check the file into the vendor branch,             #
  54. X#            labeling it with the Vendor_Release_Tag            #
  55. X#        4) If the file didn't previously exist,             #
  56. X#            make the vendor branch the default branch        #
  57. X#                                        #
  58. X# The script also is somewhat verbose in letting the user know what is        #
  59. X# going on.  It prints a diagnostic when it creates a new file, or updates  #
  60. X# a file that has been modified on the trunk.                       #
  61. X#                                        #
  62. X#############################################################################
  63. X
  64. Xset vbose = 0
  65. Xset message = ""
  66. Xset cvsbin = /usr/local/bin
  67. Xset rcsbin = /usr/local/bin
  68. Xset grep = /bin/grep
  69. Xset message_file = /tmp/checkin.$$
  70. Xset got_one = 0
  71. X
  72. Xif ( $#argv < 3 ) then
  73. X    echo "Usage: checkin [-v] [-m message] [-f message_file] repository"
  74. X    echo "    Vendor_Tag Vendor_Release_Tag [Vendor_Release_tag...]"
  75. X    exit 1
  76. Xendif
  77. Xwhile ( $#argv )
  78. X    switch ( $argv[1] )
  79. X        case -v:
  80. X            set vbose = 1
  81. X        breaksw
  82. X    case -m:
  83. X        shift
  84. X        echo $argv[1] > $message_file
  85. X        set got_one = 1
  86. X        breaksw
  87. X    case -f:
  88. X        shift
  89. X        set message_file = $argv[1]
  90. X        set got_one = 2
  91. X        breaksw
  92. X    default:
  93. X        break
  94. X    endsw
  95. X    shift
  96. Xend
  97. Xif ( $#argv < 3 ) then
  98. X    echo "Usage: checkin [-v] [-m message] [-f message_file] repository"
  99. X    echo "    Vendor_Tag Vendor_Release_Tag [Vendor_Release_tag...]"
  100. X    exit 1
  101. Xendif
  102. Xset repository = $argv[1]
  103. Xshift
  104. Xset vendor = $argv[1]
  105. Xshift
  106. Xset release = $argv[1]
  107. Xshift
  108. Xset extra_release = ( $argv )
  109. X
  110. Xif ( ! $?CVSROOT ) then
  111. X    echo "Please set the environmental variable CVSROOT to the root"
  112. X    echo "    of the tree you wish to update"
  113. X    exit 1
  114. Xendif
  115. X
  116. Xif ( $got_one == 0 ) then
  117. X    echo "Please Edit this file to contain the RCS log information" >$message_file
  118. X    echo "to be associated with this file (please remove these lines)">>$message_file
  119. X    if ( $?EDITOR ) then
  120. X    $EDITOR $message_file > /dev/tty
  121. X    else
  122. X    /usr/ucb/vi $message_file > /dev/tty
  123. X    endif
  124. X    set got_one = 1
  125. Xendif
  126. X
  127. Xumask 2
  128. X
  129. Xset update_dir = ${CVSROOT}/${repository}
  130. Xif ( -d SCCS ) then
  131. X    sccs get SCCS/* >& /dev/null
  132. Xendif
  133. Xif ( -d RCS ) then
  134. X    $rcsbin/co RCS/* >& /dev/null
  135. Xendif
  136. Xforeach name ( * .[a-zA-Z0-9]* )
  137. X    if ( $name == SCCS ) then 
  138. X    continue
  139. X    endif
  140. X    if ( $name == RCS ) then 
  141. X    continue
  142. X    endif
  143. X    if ( $vbose ) then 
  144. X    echo "Updating ${repository}/${name}"
  145. X    endif
  146. X    if ( -d $name ) then
  147. X    if ( ! -d ${update_dir}/${name} ) then
  148. X        echo "WARNING: Creating new directory ${repository}/${name}"
  149. X        mkdir ${update_dir}/${name}
  150. X        if ( $status ) then
  151. X        echo "ERROR: mkdir failed - aborting"
  152. X        exit 1
  153. X        endif
  154. X    endif
  155. X    chdir $name
  156. X    if ( $status ) then
  157. X        echo "ERROR: Couldn\'t chdir to $name - aborting" 
  158. X        exit 1
  159. X    endif
  160. X    if ( $vbose ) then
  161. X        $cvsbin/checkin -v -f $message_file ${repository}/${name} $vendor $release $extra_release
  162. X    else
  163. X        $cvsbin/checkin -f $message_file ${repository}/${name} $vendor $release $extra_release
  164. X    endif
  165. X    if ( $status ) then 
  166. X        exit 1
  167. X    endif
  168. X    chdir ..
  169. X    else
  170. X    if ( ! -f $name ) then
  171. X        echo "WARNING: $name is neither a regular file" 
  172. X        echo "       nor a directory - ignored"
  173. X        continue
  174. X    endif
  175. X    set file = ${update_dir}/${name},v
  176. X    set new = 0
  177. X    set comment = ""
  178. X    grep -s '\$Log.*\$' ${name}
  179. X    if ( $status == 0 ) then
  180. X        set myext = ${name:e}
  181. X        set knownext = 0
  182. X        foreach xx ( "c" "csh" "e" "f" "h" "l" "mac" "me" "mm" "ms" "p" "r" "red" "s" "sh" "sl" "cl" "ml" "el" "tex" "y" "ye" "yr" "" )
  183. X        if ( "${myext}" == "${xx}" ) then
  184. X            set knownext = 1
  185. X            break
  186. X        endif
  187. X        end
  188. X        if ( $knownext == 0 ) then
  189. X        echo For file ${file}:
  190. X        grep '\$Log.*\$' ${name}
  191. X        echo -n "Please insert a comment leader for file ${name} > "
  192. X        set comment = $<
  193. X        endif
  194. X    endif
  195. X    if ( ! -f $file ) then
  196. X        if ( ! -f ${update_dir}/Attic/${name},v ) then
  197. X            echo "WARNING: Creating new file ${repository}/${name}"
  198. X        if ( "${comment}" != "" ) then
  199. X            $rcsbin/rcs -q -i -c"${comment}" -t/dev/null $file
  200. X        endif
  201. X            $rcsbin/ci -q -u1.1 -t/dev/null $file 
  202. X            if ( $status ) then
  203. X            echo "ERROR: Initial check-in of $file failed - aborting"
  204. X            exit 1
  205. X            endif
  206. X            set new = 1
  207. X        else 
  208. X        set file = ${update_dir}/Attic/${name},v
  209. X        echo "WARNING: Updating ${repository}/Attic/${name}"
  210. X            set head = `head -1 $file`
  211. X            set branch = `head -2 $file | ${grep} -w branch`
  212. X            if ( $#head != 2 || $#branch != 2 ) then
  213. X            echo "ERROR: corrupted RCS file $file - aborting"
  214. X            endif
  215. X            if ( "$head[2]" == "1.1;" && "$branch[2]" != "1.1.1;" ) then
  216. X            ${rcsbin}/rcsdiff -q -r1.1 $file > /dev/null
  217. X            if ( ! $status ) then
  218. X                set new = 1
  219. X            endif
  220. X            else
  221. X                if ( "$branch[2]" != "1.1.1;" ) then
  222. X                echo -n "WARNING: Updating locally modified file "
  223. X            echo    "${repository}/Attic/${name}"
  224. X                endif
  225. X            endif
  226. X        endif
  227. X    else
  228. X        set head = `head -1 $file`
  229. X        set branch = `head -2 $file | ${grep} -w branch`
  230. X        if ( $#head != 2 || $#branch != 2 ) then
  231. X        echo "ERROR: corrupted RCS file $file - aborting"
  232. X        endif
  233. X        if ( "$head[2]" == "1.1;" && "$branch[2]" != "1.1.1;" ) then
  234. X        ${rcsbin}/rcsdiff -q -r1.1 $file > /dev/null
  235. X        if ( ! $status ) then
  236. X            set new = 1
  237. X        endif
  238. X        else
  239. X            if ( "$branch[2]" != "1.1.1;" ) then
  240. X            echo -n "WARNING: Updating locally modified file "
  241. X            echo    "${repository}/${name}"
  242. X            endif
  243. X        endif
  244. X    endif
  245. X    $rcsbin/rcs -q -N${vendor}:1.1.1 $file
  246. X    if ( $status ) then
  247. X        echo "ERROR: Attempt to set Vendor_Tag in $file failed - aborting"
  248. X        exit 1
  249. X    endif
  250. X    set lock_failed = 0
  251. X    $rcsbin/rcs -q -l${vendor} $file >& /dev/null
  252. X    if ( $status ) then
  253. X        set lock_failed = 1
  254. X    endif
  255. X    if ( "${comment}" != "" ) then
  256. X        $rcsbin/rcs -q -c"${comment}" $file
  257. X    endif
  258. X    $rcsbin/ci -q -f -u${vendor} -N${release} $file < $message_file 
  259. X    if ( $status ) then
  260. X        echo "ERROR: Check-in of $file failed - aborting"
  261. X        if ( ! $lock_failed ) then
  262. X            $rcsbin/rcs -q -u${vendor} $file
  263. X        endif
  264. X        exit 1
  265. X    endif
  266. X    foreach tag ( $extra_release )
  267. X        $rcsbin/rcs -q -N${tag}:${release} $file
  268. X        if ( $status ) then
  269. X        echo "ERROR: Couldn't add tag $tag to file $file"
  270. X        continue
  271. X        endif
  272. X    end
  273. X    if ( $new ) then
  274. X        $rcsbin/rcs -q -b${vendor} $file
  275. X        if ( $status ) then
  276. X        echo "ERROR: Attempt to change default branch failed - aborting"
  277. X        exit 1
  278. X        endif
  279. X    endif
  280. X    endif
  281. Xend
  282. Xif ( $got_one == 1 ) rm $message_file
  283. END_OF_FILE
  284. if test 7570 -ne `wc -c <'src/checkin.csh'`; then
  285.     echo shar: \"'src/checkin.csh'\" unpacked with wrong size!
  286. fi
  287. # end of 'src/checkin.csh'
  288. fi
  289. if test -f 'src/collect_sets.c' -a "${1}" != "-c" ; then 
  290.   echo shar: Will not clobber existing file \"'src/collect_sets.c'\"
  291. else
  292. echo shar: Extracting \"'src/collect_sets.c'\" \(7811 characters\)
  293. sed "s/^X//" >'src/collect_sets.c' <<'END_OF_FILE'
  294. X#ifndef lint
  295. Xstatic char rcsid[] = "$Id: collect_sets.c,v 1.15 89/11/19 23:19:51 berliner Exp $";
  296. X#endif !lint
  297. X
  298. X/*
  299. X *    Copyright (c) 1989, Brian Berliner
  300. X *
  301. X *    You may distribute under the terms of the GNU General Public License
  302. X *    as specified in the README file that comes with the CVS 1.0 kit.
  303. X *
  304. X * Collect Sets
  305. X *
  306. X *    Collects the interesting file names from the administration and
  307. X *    the repository in a number of variables:
  308. X *                            solved by:
  309. X *        Clist    conflict-ridden            (user)
  310. X *        Glist    modified, needs merging        (update)
  311. X *        Mlist    modified, needs checking in    (commit)
  312. X *        Olist    needs checking out        (update)
  313. X *        Alist    to be added            (commit)
  314. X *        Rlist    to be removed            (commit)
  315. X *        Wlist    remove entry            (update)
  316. X *        Llist    locked list            (commit)
  317. X *        Blist    branch list            (commit)
  318. X *        Dlist    directory list            (update)
  319. X *
  320. X *    Returns non-zero on error.
  321. X */
  322. X
  323. X#include <sys/param.h>
  324. X#include "cvs.h"
  325. X
  326. Xextern char update_dir[];
  327. X
  328. XCollect_Sets(argc, argv)
  329. X    int argc;
  330. X    char *argv[];
  331. X{
  332. X    register int i;
  333. X    char tmp[MAXPATHLEN], update_user[MAXPATHLEN];
  334. X    int ret = 0;
  335. X
  336. X    /*
  337. X     * By default, a call here must wipe the slate clean
  338. X     */
  339. X    Clist[0] = Glist[0] = Mlist[0] = Olist[0] = Dlist[0] = '\0';
  340. X    Alist[0] = Rlist[0] = Wlist[0] = Llist[0] = Blist[0] = '\0';
  341. X    for (i = 0; i < argc; i++) {
  342. X    (void) strcpy(User, argv[i]);
  343. X    if (update_dir[0] != '\0')
  344. X        (void) sprintf(update_user, "%s/%s", update_dir, User);
  345. X    else
  346. X        (void) strcpy(update_user, User);
  347. X    if (force_tag_match && (Tag[0] != '\0' || Date[0] != '\0'))
  348. X        Locate_RCS();
  349. X    else
  350. X        (void) sprintf(Rcs, "%s/%s%s", Repository, User, RCSEXT);
  351. X    if (isdir(User)) {        /* just a directory -- add to Dlist */
  352. X        (void) strcat(Dlist, " ");
  353. X        (void) strcat(Dlist, User);
  354. X        continue;
  355. X    }
  356. X    Version_TS(Rcs, Tag, User);
  357. X    if (VN_User[0] == '\0') {
  358. X        /*
  359. X         * No entry available, TS_Rcs is invalid
  360. X         */
  361. X        if (VN_Rcs[0] == '\0') {
  362. X        /*
  363. X         * There is no RCS file either
  364. X         */
  365. X        if (TS_User[0] == '\0')    { /* there is no user file */
  366. X            if (!force_tag_match || !isfile(Rcs)) {
  367. X            warn(0, "nothing known about %s", update_user);
  368. X            ret++;
  369. X            }
  370. X        } else {        /* there is a user file */
  371. X            warn(0, "use `cvs add' to create entry for %s",
  372. X             update_user);
  373. X            ret++;
  374. X        }
  375. X        } else {
  376. X        /*
  377. X         * There is an RCS file
  378. X         */
  379. X        if (TS_User[0] == '\0') {
  380. X            /*
  381. X             * There is no user file; ad it to the Olist
  382. X             */
  383. X            (void) strcat(Olist, " ");
  384. X            (void) strcat(Olist, User);
  385. X        } else {
  386. X            /*
  387. X             * There is a user file; print a warning and add it
  388. X             * to the conflict list, Clist
  389. X             */
  390. X            warn(0, "move away %s; it is in the way", update_user);
  391. X            (void) strcat(Clist, " ");
  392. X            (void) strcat(Clist, User);
  393. X            ret++;
  394. X        }
  395. X        }
  396. X    } else if (VN_User[0] == '0' && VN_User[1] == '\0') {
  397. X        /*
  398. X         * An entry for a new-born file; TS_Rcs is dummy
  399. X         */
  400. X        if (TS_User[0] == '\0') {
  401. X        /*
  402. X         * There is no user file, but there should be one;
  403. X         * add it to the remove entry list.
  404. X         */
  405. X        warn(0, "warning: new-born %s has disappeared", update_user);
  406. X        (void) strcat(Wlist, " ");
  407. X        (void) strcat(Wlist, User);
  408. X        } else {
  409. X        /*
  410. X         * There is a user file
  411. X         */
  412. X        if (VN_Rcs[0] == '\0') {
  413. X            /*
  414. X             * There is no RCS file, so add it to the add entry list
  415. X             */
  416. X            (void) strcat(Alist, " ");
  417. X            (void) strcat(Alist, User);
  418. X        } else {
  419. X            /*
  420. X             * There is an RCS file, so someone else must have
  421. X             * checked one in behind our back; added to the conflict
  422. X             * list
  423. X             */
  424. X            warn(0, "conflict: %s created independently by second party",
  425. X             update_user);
  426. X            (void) strcat(Clist, " ");
  427. X            (void) strcat(Clist, User);
  428. X            ret++;
  429. X        }
  430. X        }
  431. X    } else if (VN_User[0] == '-') {
  432. X        /*
  433. X         * An entry for a removed file, TS_Rcs is invalid
  434. X         */
  435. X        if (TS_User[0] == '\0') {
  436. X        /*
  437. X         * There is no user file (as it should be)
  438. X         */
  439. X        (void) sprintf(tmp, "-%s", VN_Rcs);
  440. X        if (strcmp(tmp, "-") == 0) {
  441. X            /*
  442. X             * There is no RCS file; this is all-right, but it
  443. X             * has been removed independently by a second party;
  444. X             * added to the remove entry list.
  445. X             */
  446. X            (void) strcat(Wlist, " ");
  447. X            (void) strcat(Wlist, User);
  448. X        } else if (strcmp(tmp, VN_User) == 0) {
  449. X            /*
  450. X             * The RCS file is the same version as the user file,
  451. X             * and that's OK; added to the to be removed list
  452. X             */
  453. X            (void) strcat(Rlist, " ");
  454. X            (void) strcat(Rlist, User);
  455. X        } else {
  456. X            /*
  457. X             * The RCS file is a newer version than the user file;
  458. X             * and this is defintely not OK; make it a conflict.
  459. X             */
  460. X            warn(0, "conflict: removed %s was modified by second party",
  461. X             update_user);
  462. X            (void) strcat(Clist, " ");
  463. X            (void) strcat(Clist, User);
  464. X            ret++;
  465. X        }
  466. X        } else {
  467. X        /*
  468. X         * The user file shouldn't be there
  469. X         */
  470. X        warn(0, "%s should be removed and is still there", update_user);
  471. X        ret++;
  472. X        }
  473. X    } else {
  474. X        /*
  475. X         * A normal entry, TS_Rcs is valid
  476. X         */
  477. X        if (VN_Rcs[0] == '\0') {
  478. X        /*
  479. X         * There is no RCS file
  480. X         */
  481. X        if (TS_User[0] == '\0') {
  482. X            /*
  483. X             * There is no user file, so just remove the entry
  484. X             */
  485. X            warn(0, "warning: %s is not (any longer) pertinent",
  486. X             update_user);
  487. X            (void) strcat(Wlist, " ");
  488. X            (void) strcat(Wlist, User);
  489. X        } else if (strcmp(TS_User, TS_Rcs) == 0) {
  490. X            /*
  491. X             * The user file is still unmodified, so just remove it
  492. X             * from the entry list
  493. X             */
  494. X            if (!force_tag_match || !isfile(Rcs)) {
  495. X            warn(0, "%s is no longer in the repository",
  496. X                 update_user);
  497. X            (void) strcat(Wlist, " ");
  498. X            (void) strcat(Wlist, User);
  499. X            }
  500. X        } else {
  501. X            /*
  502. X             * The user file has been modified and since it is no
  503. X             * longer in the repository, a conflict is raised
  504. X             */
  505. X            if (!force_tag_match) {
  506. X            warn(0, "conflict: %s is modified but no longer in the repository",
  507. X                 update_user);
  508. X            (void) strcat(Clist, " ");
  509. X            (void) strcat(Clist, User);
  510. X            ret++;
  511. X            }
  512. X        }
  513. X        } else if (strcmp(VN_Rcs, VN_User) == 0) {
  514. X        /*
  515. X         * The RCS file is the same version as the user file
  516. X         */
  517. X        if (TS_User[0] == '\0') {
  518. X            /*
  519. X             * There is no user file, so note that it was lost
  520. X             * and extract a new version
  521. X             */
  522. X            if (strcmp(command, "checkout") != 0 &&
  523. X            strcmp(command, "co") != 0 &&
  524. X            strcmp(command, "get") != 0)
  525. X            warn(0, "warning: %s was lost", update_user);
  526. X            (void) strcat(Olist, " ");
  527. X            (void) strcat(Olist, User);
  528. X        } else if (strcmp(TS_User, TS_Rcs) == 0) {
  529. X            /*
  530. X             * The user file is still unmodified, so nothing
  531. X             * special at all to do -- no lists updated
  532. X             */
  533. X        } else {
  534. X            /*
  535. X             * The user file appears to have been modified, but
  536. X             * we call No_Difference to verify that it really
  537. X             * has been modified -- it updates the Mlist,
  538. X             * if necessary.
  539. X             */
  540. X            (void) No_Difference(0);
  541. X        }
  542. X        } else {
  543. X        /*
  544. X         * The RCS file is a newer version than the user file
  545. X         */
  546. X        if (TS_User[0] == '\0') {
  547. X            /*
  548. X             * There is no user file, so just get it
  549. X             */
  550. X            if (strcmp(command, "checkout") != 0 &&
  551. X            strcmp(command, "co") != 0 &&
  552. X            strcmp(command, "get") != 0)
  553. X            warn(0, "warning: %s was lost", update_user);
  554. X            (void) strcat(Olist, " ");
  555. X            (void) strcat(Olist, User);
  556. X        } else if (strcmp(TS_User, TS_Rcs) == 0) {
  557. X            /*
  558. X             * The user file is still unmodified, so just get it
  559. X             * as well
  560. X             */
  561. X            (void) strcat(Olist, " ");
  562. X            (void) strcat(Olist, User);
  563. X        } else {
  564. X            /*
  565. X             * The user file appears to have been modified; we call
  566. X             * No_Difference to verify this for us, and it updates
  567. X             * Glist if it has really been modified, and Olist if
  568. X             * it hasn't
  569. X             */
  570. X            (void) No_Difference(1);
  571. X        }
  572. X        }
  573. X    }
  574. X    }
  575. X    return (ret);
  576. X}
  577. END_OF_FILE
  578. if test 7811 -ne `wc -c <'src/collect_sets.c'`; then
  579.     echo shar: \"'src/collect_sets.c'\" unpacked with wrong size!
  580. fi
  581. # end of 'src/collect_sets.c'
  582. fi
  583. if test -f 'src/main.c' -a "${1}" != "-c" ; then 
  584.   echo shar: Will not clobber existing file \"'src/main.c'\"
  585. else
  586. echo shar: Extracting \"'src/main.c'\" \(6993 characters\)
  587. sed "s/^X//" >'src/main.c' <<'END_OF_FILE'
  588. Xchar rcsid[] = "$Id: main.c,v 1.23 89/11/20 00:06:34 berliner Exp $\nPatch level ###\n";
  589. X
  590. X/*
  591. X *    Copyright (c) 1989, Brian Berliner
  592. X *
  593. X *    You may distribute under the terms of the GNU General Public License
  594. X *    as specified in the README file that comes with the CVS 1.0 kit.
  595. X *
  596. X * This is the main C driver for the CVS system.
  597. X *
  598. X * Credit to Dick Grune, Vrije Universiteit, Amsterdam, for writing
  599. X * the shell-script CVS system that this is based on.
  600. X *
  601. X * Usage:
  602. X *    cvs [options] command [options] [files/modules...]
  603. X *
  604. X * Where "command" is composed of:
  605. X *        checkout    Check out a module/dir/file
  606. X *        update        Brings work tree in sync with repository
  607. X *        commit        Checks files into the repository
  608. X *        diff        Runs diffs between revisions
  609. X *        log        Prints to "rlog" information for files
  610. X *        add        Adds an entry to the repository
  611. X *        remove        Removes an entry from the repository
  612. X *        status        Status info on the revisions
  613. X *        join        Merge two RCS revisions
  614. X *        patch        "patch" format diff listing between releases
  615. X *        tag        Add/delete a symbolic tag to the RCS file
  616. X *
  617. X * Future:
  618. X *        checkin        Adds new *directories* to the repository
  619. X *                Currently being done by an external shell
  620. X *                script.
  621. X *
  622. X * Brian Berliner
  623. X * 4/20/89
  624. X */
  625. X
  626. X#include <sys/param.h>
  627. X#include "cvs.h"
  628. X#include "patchlevel.h"
  629. X
  630. Xchar *progname, *command;
  631. X
  632. Xchar *fileargv[MAXFILEPERDIR];
  633. Xint fileargc;
  634. X
  635. Xint use_editor = TRUE;
  636. Xint cvswrite = !CVSREAD_DFLT;
  637. Xint really_quiet = FALSE;
  638. Xint quiet = FALSE;
  639. Xint force_tag_match = FALSE;
  640. X
  641. X/*
  642. X * Globals for the lists created in Collect_Sets()
  643. X */
  644. Xchar Clist[MAXLISTLEN], Glist[MAXLISTLEN], Mlist[MAXLISTLEN];
  645. Xchar Olist[MAXLISTLEN], Alist[MAXLISTLEN], Rlist[MAXLISTLEN];
  646. Xchar Wlist[MAXLISTLEN], Llist[MAXLISTLEN], Blist[MAXLISTLEN];
  647. Xchar Dlist[MAXLISTLEN];
  648. X
  649. X/*
  650. X * Globals which refer to a particular file
  651. X */
  652. Xchar Repository[MAXPATHLEN];
  653. Xchar User[MAXPATHLEN], Rcs[MAXPATHLEN];
  654. Xchar VN_User[MAXPATHLEN], TS_User[MAXPATHLEN];
  655. Xchar VN_Rcs[MAXPATHLEN], TS_Rcs[MAXPATHLEN];
  656. Xchar Tag[MAXPATHLEN], Date[MAXPATHLEN];
  657. X
  658. X/*
  659. X * Options is used to hold options passed on to system(), while prog
  660. X * is alwys used in the calls to system().
  661. X */
  662. Xchar Options[MAXPATHLEN];
  663. Xchar prog[MAXPROGLEN];
  664. X
  665. X/*
  666. X * Defaults, for the environment variables that are not set
  667. X */
  668. Xchar *Rcsbin = RCSBIN_DFLT;
  669. Xchar *Editor = EDITOR_DFLT;
  670. Xchar *CVSroot = CVSROOT_DFLT;
  671. X
  672. Xmain(argc, argv)
  673. X    int argc;
  674. X    char *argv[];
  675. X{
  676. X    extern char *getenv();
  677. X    register char *cp;
  678. X    register int c;
  679. X    int help = FALSE, err = 0;
  680. X
  681. X    /*
  682. X     * Just save the last component of the path for error messages
  683. X     */
  684. X    if ((progname = rindex(argv[0], '/')) == NULL)
  685. X    progname = argv[0];
  686. X    else
  687. X    progname++;
  688. X
  689. X    /*
  690. X     * Query the environment variables up-front, so that
  691. X     * they can be overridden by command line arguments
  692. X     */
  693. X    if ((cp = getenv(RCSBIN_ENV)) != NULL)
  694. X    Rcsbin = cp;
  695. X    if ((cp = getenv(EDITOR_ENV)) != NULL)
  696. X    Editor = cp;
  697. X    if ((cp = getenv(CVSROOT_ENV)) != NULL)
  698. X    CVSroot = cp;
  699. X    if (getenv(CVSREAD_ENV) != NULL)
  700. X    cvswrite = FALSE;
  701. X
  702. X    optind = 1;
  703. X    while ((c = getopt(argc, argv, "rwvb:e:d:H")) != -1) {
  704. X    switch (c) {
  705. X    case 'r':
  706. X        cvswrite = FALSE;
  707. X        break;
  708. X    case 'w':
  709. X        cvswrite = TRUE;
  710. X        break;
  711. X    case 'v':
  712. X        (void) sprintf(index(rcsid, '#'), "%d\n", PATCHLEVEL);
  713. X        (void) fputs(rcsid, stdout);
  714. X        (void) fputs("\nCopyright (c) 1989, Brian Berliner\n\n\
  715. XCVS may be copied only under the terms of the GNU General Public License,\n\
  716. Xa copy of which can be found with the CVS 1.0 distribution kit.\n", stdout);
  717. X        exit(0);
  718. X        break;
  719. X    case 'b':
  720. X        Rcsbin = optarg;
  721. X        break;
  722. X    case 'e':
  723. X        Editor = optarg;
  724. X        break;
  725. X    case 'd':
  726. X        CVSroot = optarg;
  727. X        break;
  728. X    case 'H':
  729. X        help = TRUE;
  730. X        break;
  731. X    case '?':
  732. X    default:
  733. X        usage();
  734. X    }
  735. X    }
  736. X    argc -= optind;
  737. X    argv += optind;
  738. X    if (argc < 1)
  739. X    usage();
  740. X
  741. X    /*
  742. X     * Specifying just the '-H' flag to the sub-command causes a Usage
  743. X     * message to be displayed.
  744. X     */
  745. X    command = cp = argv[0];
  746. X    if (help == TRUE || (argc > 1 && strcmp(argv[1], "-H") == 0))
  747. X    argc = -1;
  748. X
  749. X    /*
  750. X     * Make standard output line buffered, so that progress can be
  751. X     * monitored when redirected to a file, but only when we're not
  752. X     * running the "patch" command, as it generates lots of output
  753. X     * to stdout -- just leave it block buffered.
  754. X     */
  755. X    if (strcmp(cp, "patch") != 0)
  756. X    setlinebuf(stdout);
  757. X
  758. X    if (strcmp(cp, "update") == 0)
  759. X    err += update(argc, argv);
  760. X    else if (strcmp(cp, "commit") == 0 || strcmp(cp, "ci") == 0)
  761. X    commit(argc, argv);
  762. X    else if (strcmp(cp, "diff") == 0)
  763. X    diff(argc, argv);
  764. X    else if (strcmp(cp, "checkout") == 0 || strcmp(cp, "co") == 0 ||
  765. X        strcmp(cp, "get") == 0)
  766. X    checkout(argc, argv);
  767. X    else if (strcmp(cp, "log") == 0)
  768. X    log(argc, argv);
  769. X    else if (strcmp(cp, "status") == 0)
  770. X    status(argc, argv);
  771. X    else if (strcmp(cp, "add") == 0)
  772. X    add(argc, argv);
  773. X    else if (strcmp(cp, "remove") == 0)
  774. X    remove(argc, argv);
  775. X    else if (strcmp(cp, "join") == 0)
  776. X    join(argc, argv);
  777. X    else if (strcmp(cp, "patch") == 0)
  778. X    patch(argc, argv);
  779. X    else if (strcmp(cp, "tag") == 0)
  780. X    tag(argc, argv);
  781. X    else
  782. X    usage();            /* oops.. no match */
  783. X    Lock_Cleanup(0);
  784. X    exit(err);
  785. X}
  786. X
  787. Xstatic
  788. Xusage()
  789. X{
  790. X    (void) fprintf(stderr,
  791. X    "Usage: %s [cvs-options] command [command-options] [files...]\n",
  792. X           progname);
  793. X    (void) fprintf(stderr, "\tWhere 'cvs-options' are:\n");
  794. X    (void) fprintf(stderr, "\t\t-r\t\tMake checked-out files read-only\n");
  795. X    (void) fprintf(stderr,
  796. X    "\t\t-w\t\tMake checked-out files read-write (default)\n");
  797. X    (void) fprintf(stderr, "\t\t-v\t\tCVS version and copyright\n");
  798. X    (void) fprintf(stderr, "\t\t-b bindir\tFind RCS programs in 'bindir'\n");
  799. X    (void) fprintf(stderr,
  800. X    "\t\t-e editor\tUse 'editor' for editing log information\n");
  801. X    (void) fprintf(stderr, "\t\t-d CVS_root_directory\n");
  802. X    (void) fprintf(stderr, "\t\t\t\t\Points to the root of the CVS tree\n");
  803. X    (void) fprintf(stderr, "\tand 'command' is:\n");
  804. X    (void) fprintf(stderr, "\t\tcheckout\tCreates a new CVS directory\n");
  805. X    (void) fprintf(stderr,
  806. X    "\t\tupdate\t\tBrings work tree in sync with repository\n");
  807. X    (void) fprintf(stderr,
  808. X    "\t\tcommit\t\tChecks files into the repository\n");
  809. X    (void) fprintf(stderr, "\t\tdiff\t\tRuns diffs between revisions\n");
  810. X    (void) fprintf(stderr,
  811. X    "\t\tlog\t\tPrints out 'rlog' information for files\n");
  812. X    (void) fprintf(stderr, "\t\tstatus\t\tStatus info on the revisions\n");
  813. X    (void) fprintf(stderr, "\t\tadd\t\tAdds an entry to the repository\n");
  814. X    (void) fprintf(stderr,
  815. X    "\t\tremove\t\tRemoves an entry from the repository\n");
  816. X    (void) fprintf(stderr,
  817. X    "\t\tjoin\t\tJoins an RCS revision with the head on checkout\n");
  818. X    (void) fprintf(stderr,
  819. X    "\t\tpatch\t\t\"patch\" format diffs between releases\n");
  820. X    (void) fprintf(stderr, "\t\ttag\t\tAdd a symbolic tag to the RCS file\n");
  821. X    (void) fprintf(stderr,
  822. X    "\tSpecify the '-H' option to each command for Usage information\n");
  823. X    exit(1);
  824. X}
  825. END_OF_FILE
  826. if test 6993 -ne `wc -c <'src/main.c'`; then
  827.     echo shar: \"'src/main.c'\" unpacked with wrong size!
  828. fi
  829. # end of 'src/main.c'
  830. fi
  831. if test -f 'src/modules.c' -a "${1}" != "-c" ; then 
  832.   echo shar: Will not clobber existing file \"'src/modules.c'\"
  833. else
  834. echo shar: Extracting \"'src/modules.c'\" \(8254 characters\)
  835. sed "s/^X//" >'src/modules.c' <<'END_OF_FILE'
  836. X#ifndef lint
  837. Xstatic char rcsid[] = "$Id: modules.c,v 1.14 89/11/19 23:20:12 berliner Exp $";
  838. X#endif !lint
  839. X
  840. X/*
  841. X *    Copyright (c) 1989, Brian Berliner
  842. X *
  843. X *    You may distribute under the terms of the GNU General Public License
  844. X *    as specified in the README file that comes with the CVS 1.0 kit.
  845. X *
  846. X * Modules
  847. X *
  848. X *    Functions for accessing the modules file.
  849. X *
  850. X *    The modules file supports basically three formats of lines:
  851. X *        key [options] directory files...
  852. X *        key [options] directory
  853. X *        key -a aliases...
  854. X *
  855. X *    The -a option allows an aliasing step in the parsing of the modules
  856. X *    file.  The "aliases" listed on a line following the -a are
  857. X *    processed one-by-one, as if they were specified as arguments on the
  858. X *    command line.
  859. X */
  860. X
  861. X#include <sys/param.h>
  862. X#include <sys/file.h>
  863. X#include <ndbm.h>
  864. X#include "cvs.h"
  865. X
  866. Xextern int update_build_dirs;
  867. X
  868. Xint run_module_prog = 1;        /* run -i/-o/-t prog by default */
  869. X
  870. X/*
  871. X * Open the modules file, and die if the CVSROOT environment variable
  872. X * was not set.  If the modules file does not exist, that's fine, and
  873. X * a warning message is displayed and a NULL is returned.
  874. X */
  875. XDBM *
  876. Xopen_module()
  877. X{
  878. X    char mfile[MAXPATHLEN];
  879. X    DBM *db;
  880. X
  881. X    if (CVSroot == NULL) {
  882. X    (void) fprintf(stderr, "%s: must set the CVSROOT environment variable\n",
  883. X               progname);
  884. X    error(0, "or specify the '-d' option to %s", progname);
  885. X    }
  886. X    (void) sprintf(mfile, "%s/%s", CVSroot, CVSROOTADM_MODULES);
  887. X    if ((db = dbm_open(mfile, O_RDONLY, 0666)) == NULL)
  888. X    warn(0, "warning: cannot open modules file %s", mfile);
  889. X    return (db);
  890. X}
  891. X
  892. X/*
  893. X * Close the modules file, if the open succeeded, that is
  894. X */
  895. Xclose_module(db)
  896. X    DBM *db;
  897. X{
  898. X    if (db != NULL) {
  899. X    dbm_close(db);
  900. X    }
  901. X}
  902. X
  903. X/*
  904. X * This is the recursive function that processes a module name.
  905. X * If tag is set, just tag files, otherwise, we were called to check files
  906. X * out.
  907. X */
  908. Xdo_module(db, mname, m_type, msg)
  909. X    DBM *db;
  910. X    char *mname;
  911. X    enum mtype m_type;
  912. X    char *msg;
  913. X{
  914. X    char *checkin_prog = NULL, *checkout_prog = NULL, *tag_prog = NULL;
  915. X    char cwd[MAXPATHLEN], file[MAXPATHLEN];
  916. X    char *moduleargv[MAXFILEPERDIR];
  917. X    int moduleargc, just_file;
  918. X    datum key, val;
  919. X    char *cp, **argv;
  920. X    int c, alias = 0, argc, err = 0;
  921. X
  922. X    just_file = FALSE;
  923. X    update_build_dirs = FALSE;
  924. X    /*
  925. X     * Look the argument module name up in the database; if we found it, use
  926. X     * it, otherwise, see if the file or directory exists relative to the
  927. X     * root directory (CVSroot).  If there is a directory there, it is
  928. X     * extracted or tagged recursively; if there is a file there, it is
  929. X     * extracted or tagged individually; if there is no file or directory
  930. X     * there, we are done.
  931. X     */
  932. X    key.dptr = mname;
  933. X    key.dsize = strlen(key.dptr);
  934. X    if (db != NULL)
  935. X    val = dbm_fetch(db, key);
  936. X    else
  937. X    val.dptr = NULL;
  938. X    if (val.dptr != NULL) {
  939. X    val.dptr[val.dsize] = '\0';
  940. X    } else {
  941. X    /*
  942. X     * Need to determine if the argument module name is a directory
  943. X     * or a file relative to the CVS root and set update_build_dirs
  944. X     * and just_file accordingly
  945. X     */
  946. X    update_build_dirs = TRUE;
  947. X    (void) sprintf(file, "%s/%s", CVSroot, key.dptr);
  948. X    if (!isdir(file)) {
  949. X        (void) strcat(file, RCSEXT);
  950. X        if (!isfile(file)) {
  951. X        warn(0, "cannot find '%s' - ignored", key.dptr);
  952. X        err++;
  953. X        return (err);
  954. X        } else {
  955. X        update_build_dirs = FALSE;
  956. X        just_file = TRUE;
  957. X        }
  958. X    }
  959. X    val = key;
  960. X    }
  961. X    /*
  962. X     * If just extracting or tagging files, need to munge the
  963. X     * passed in module name to look like an actual module entry.
  964. X     */
  965. X    if (just_file == TRUE) {
  966. X    if ((cp = rindex(key.dptr, '/')) != NULL) {
  967. X        *cp++ = '\0';
  968. X    } else {
  969. X        cp = key.dptr;
  970. X        key.dptr = ".";
  971. X    }
  972. X    (void) sprintf(file, "%s %s %s", key.dptr, key.dptr, cp);
  973. X    } else {
  974. X    (void) sprintf(file, "%s %s", key.dptr, val.dptr);
  975. X    }
  976. X    line2argv(&moduleargc, moduleargv, file);
  977. X    argc = moduleargc;
  978. X    argv = moduleargv;
  979. X    if (getwd(cwd) == NULL)
  980. X    error(0, "cannot get current working directory: %s", cwd);
  981. X    optind = 1;
  982. X    while ((c = getopt(argc, argv, CVSMODULE_OPTS)) != -1) {
  983. X    switch (c) {
  984. X    case 'a':
  985. X        alias = 1;
  986. X        break;
  987. X    case 'i':
  988. X        checkin_prog = optarg;
  989. X        break;
  990. X    case 'o':
  991. X        checkout_prog = optarg;
  992. X        break;
  993. X    case 't':
  994. X        tag_prog = optarg;
  995. X        break;
  996. X    case '?':
  997. X        warn(0, "modules file has invalid option for key %s value %s",
  998. X         key.dptr, val.dptr);
  999. X        err++;
  1000. X        return (err);
  1001. X        break;
  1002. X    }
  1003. X    }
  1004. X    argc -= optind;
  1005. X    argv += optind;
  1006. X    if (argc == 0) {
  1007. X    warn(0, "modules file missing directory for key %s value %s",
  1008. X         key.dptr, val.dptr);
  1009. X    err++;
  1010. X    return (err);
  1011. X    }
  1012. X    if (alias) {
  1013. X    register int i;
  1014. X
  1015. X    for (i = 0; i < argc; i++) {
  1016. X        if (!quiet)
  1017. X        printf("%s %s: %s %s\n", progname, command, msg, argv[i]);
  1018. X        err += do_module(db, argv[i], m_type, msg);
  1019. X    }
  1020. X    return (err);
  1021. X    }
  1022. X    err += process_module(argc, argv, m_type, &key);
  1023. X    if (err == 0 && run_module_prog) {
  1024. X    if (m_type == CHECKOUT && checkin_prog !=  NULL) {
  1025. X        FILE *fp = open_file(CVSADM_CIPROG, "w+");
  1026. X        (void) fprintf(fp, "%s\n", checkin_prog);
  1027. X        (void) fclose(fp);
  1028. X    }
  1029. X    }
  1030. X    if (chdir(cwd) < 0)
  1031. X    error(1, "failed chdir to %s!", cwd);
  1032. X    if (err == 0 && run_module_prog) {
  1033. X    if ((m_type == TAG && tag_prog != NULL) ||
  1034. X        (m_type == CHECKOUT && checkout_prog != NULL)) {
  1035. X        (void) sprintf(prog, "%s %s",
  1036. X               m_type == TAG ? tag_prog : checkout_prog, key.dptr);
  1037. X        if (!quiet)
  1038. X        printf("%s %s: Executing '%s'\n", progname, command, prog);
  1039. X        err += system(prog);
  1040. X    }
  1041. X    }
  1042. X    free_names(&moduleargc, moduleargv);
  1043. X    return (err);
  1044. X}
  1045. X
  1046. Xstatic
  1047. Xprocess_module(argc, argv, m_type, keyp)
  1048. X    int argc;
  1049. X    char *argv[];
  1050. X    enum mtype m_type;
  1051. X    datum *keyp;
  1052. X{
  1053. X    register int i, just_file;
  1054. X    int err = 0;
  1055. X
  1056. X    just_file = argc > 1;
  1057. X    if (!just_file && update_build_dirs == FALSE && argc == 1)
  1058. X    update_build_dirs = TRUE;
  1059. X    if (m_type == TAG || m_type == PATCH) {
  1060. X    (void) sprintf(Repository, "%s/%s", CVSroot, argv[0]);
  1061. X    if (chdir(Repository) < 0) {
  1062. X        warn(1, "cannot chdir to %s", Repository);
  1063. X        err++;
  1064. X        return (err);
  1065. X    }
  1066. X    } else {
  1067. X    if (Build_Dirs_and_chdir(keyp->dptr) != 0) {
  1068. X        warn(0, "ignoring module %s", keyp->dptr);
  1069. X        err++;
  1070. X        return (err);
  1071. X    }
  1072. X    (void) sprintf(Repository, "%s/%s", CVSroot, argv[0]);
  1073. X    if (!isdir(CVSADM)) {
  1074. X        FILE *fp;
  1075. X
  1076. X        Create_Admin(Repository, DFLT_RECORD);
  1077. X        if (just_file == TRUE) {
  1078. X        fp = open_file(CVSADM_ENTSTAT, "w+");
  1079. X        (void) fclose(fp);
  1080. X        }
  1081. X    } else {
  1082. X        char file[MAXPATHLEN];
  1083. X
  1084. X        (void) strcpy(file, Repository);
  1085. X        Name_Repository();
  1086. X        if (strcmp(Repository, file) != 0) {
  1087. X        warn(0, "existing repository %s does not match %s",
  1088. X             Repository, file);
  1089. X        err++;
  1090. X        return (err);
  1091. X        }
  1092. X    }
  1093. X    }
  1094. X    if (update_build_dirs == TRUE) {
  1095. X    extern char update_dir[];
  1096. X
  1097. X    (void) strcpy(update_dir, keyp->dptr);
  1098. X    if (m_type == CHECKOUT)
  1099. X        err += update(0, (char **)0);
  1100. X    else if (m_type == TAG)
  1101. X        err += tagit((char *)0);
  1102. X    else if (m_type == PATCH)
  1103. X        err += patched((char *)0);
  1104. X    else
  1105. X        error(0, "impossible module type %d", (int)m_type);
  1106. X    return (err);
  1107. X    }
  1108. X    argc--;
  1109. X    argv++;
  1110. X    for (i = 0; i < argc; i++) {
  1111. X    char line[MAXLINELEN];
  1112. X
  1113. X    (void) strcpy(User, argv[i]);
  1114. X    (void) sprintf(Rcs, "%s/%s%s", Repository, User, RCSEXT);
  1115. X    if (m_type == CHECKOUT) {
  1116. X        Version_TS(Rcs, Tag, User);
  1117. X        if (TS_User[0] == '\0') {
  1118. X        (void) sprintf(line, "Initial %s", User);
  1119. X        Register(User, VN_Rcs, line);
  1120. X        }
  1121. X    } else if (m_type == TAG) {
  1122. X        err += tagit(Rcs);
  1123. X    } else if (m_type == PATCH) {
  1124. X        err += patched(Rcs);
  1125. X    } else {
  1126. X        error(0, "impossible module type %d", (int)m_type);
  1127. X    }
  1128. X    }
  1129. X    if (m_type == CHECKOUT)
  1130. X    err += update(++argc, --argv);
  1131. X    return (err);
  1132. X}
  1133. X
  1134. Xcat_module()
  1135. X{
  1136. X    FILE *fp;
  1137. X    DBM *db;
  1138. X    datum key, val;
  1139. X
  1140. X    if ((db = open_module()) == NULL)
  1141. X    error(0, "failed to cat the modules file");
  1142. X    if ((fp = popen(SORT, "w")) == NULL)
  1143. X    fp = stdout;
  1144. X    for (key = dbm_firstkey(db); key.dptr != NULL; key = dbm_nextkey(db)) {
  1145. X    key.dptr[key.dsize] = '\0';
  1146. X    (void) fprintf(fp, "%-20s", key.dptr);
  1147. X    val = dbm_fetch(db, key);
  1148. X    if (val.dptr != NULL) {
  1149. X        val.dptr[val.dsize] = '\0';
  1150. X        (void) fprintf(fp, " %s\n", val.dptr);
  1151. X    } else {
  1152. X        (void) fprintf(fp, "\n");
  1153. X    }
  1154. X    }
  1155. X    if (fp != stdout)
  1156. X    (void) pclose(fp);
  1157. X}
  1158. END_OF_FILE
  1159. if test 8254 -ne `wc -c <'src/modules.c'`; then
  1160.     echo shar: \"'src/modules.c'\" unpacked with wrong size!
  1161. fi
  1162. # end of 'src/modules.c'
  1163. fi
  1164. if test -f 'src/subr.c' -a "${1}" != "-c" ; then 
  1165.   echo shar: Will not clobber existing file \"'src/subr.c'\"
  1166. else
  1167. echo shar: Extracting \"'src/subr.c'\" \(8199 characters\)
  1168. sed "s/^X//" >'src/subr.c' <<'END_OF_FILE'
  1169. X#ifndef lint
  1170. Xstatic char rcsid[] = "$Id: subr.c,v 1.14 89/11/20 09:51:10 berliner Exp $";
  1171. X#endif !lint
  1172. X
  1173. X/*
  1174. X *    Copyright (c) 1989, Brian Berliner
  1175. X *
  1176. X *    You may distribute under the terms of the GNU General Public License
  1177. X *    as specified in the README file that comes with the CVS 1.0 kit.
  1178. X *
  1179. X * Various useful functions for the CVS support code.
  1180. X */
  1181. X
  1182. X#include <sys/types.h>
  1183. X#include <sys/stat.h>
  1184. X#include <sys/file.h>
  1185. X#include <varargs.h>
  1186. X#include "cvs.h"
  1187. X
  1188. X/*
  1189. X * Send a "printf" format string to stderr and die, calling the
  1190. X * defined exit function first, if necessary
  1191. X */
  1192. Xerror(doperror, fmt, va_alist)
  1193. X    int doperror;
  1194. X    char *fmt;
  1195. X    va_dcl
  1196. X{
  1197. X    extern int errno;
  1198. X    va_list x1;
  1199. X    int err = errno;
  1200. X
  1201. X    va_start(x1);
  1202. X    (void) fprintf(stderr, "%s: ", progname);
  1203. X    (void) vfprintf(stderr, fmt, x1);
  1204. X    if (doperror) {
  1205. X    (void) fprintf(stderr, ": ");
  1206. X    errno = err;
  1207. X    perror("");
  1208. X    errno = 0;
  1209. X    } else
  1210. X    (void) fprintf(stderr, "\n");
  1211. X    va_end(x1);
  1212. X    Lock_Cleanup(0);
  1213. X    exit(1);
  1214. X}
  1215. X
  1216. X/*
  1217. X * Like error() above, but just display the message to stderr,
  1218. X * without dying or running the exit function.
  1219. X */
  1220. Xwarn(doperror, fmt, va_alist)
  1221. X    int doperror;
  1222. X    char *fmt;
  1223. X    va_dcl
  1224. X{
  1225. X    extern int errno;
  1226. X    va_list x1;
  1227. X    int err = errno;
  1228. X
  1229. X    va_start(x1);
  1230. X    (void) fprintf(stderr, "%s: ", progname);
  1231. X    (void) vfprintf(stderr, fmt, x1);
  1232. X    if (doperror) {
  1233. X    (void) fprintf(stderr, ": ");
  1234. X    errno = err;
  1235. X    perror("");
  1236. X    errno = 0;
  1237. X    } else
  1238. X    (void) fprintf(stderr, "\n");
  1239. X    va_end(x1);
  1240. X}
  1241. X
  1242. X/*
  1243. X * Copies "from" to "to".
  1244. X * mallocs a buffer large enough to hold the entire file and
  1245. X * does one read/one write to do the copy.  This is reasonable,
  1246. X * since source files are typically not too large.
  1247. X */
  1248. Xcopy_file(from, to)
  1249. X    char *from;
  1250. X    char *to;
  1251. X{
  1252. X    struct stat sb;
  1253. X    int fdin, fdout;
  1254. X    char *buf;
  1255. X
  1256. X    if ((fdin = open(from, O_RDONLY)) < 0)
  1257. X    error(1, "cannot open %s for copying", from);
  1258. X    if (fstat(fdin, &sb) < 0)
  1259. X    error(1, "cannot fstat %s", from);
  1260. X    if ((fdout = creat(to, (int) sb.st_mode & 07777)) < 0)
  1261. X    error(1, "cannot create %s for copying", to);
  1262. X    if (sb.st_size > 0) {
  1263. X    buf = xmalloc((int)sb.st_size);
  1264. X    if (read(fdin, buf, (int)sb.st_size) != (int)sb.st_size)
  1265. X        error(1, "cannot read file %s for copying", from);
  1266. X    if (write(fdout, buf, (int)sb.st_size) != (int)sb.st_size)
  1267. X        error(1, "cannot write file %s for copying", to);
  1268. X    free(buf);
  1269. X    }
  1270. X    (void) close(fdin);
  1271. X    (void) close(fdout);
  1272. X}
  1273. X
  1274. X/*
  1275. X * Returns non-zero if the argument file is a directory, or
  1276. X * is a symbolic link which points to a directory.
  1277. X */
  1278. Xisdir(file)
  1279. X    char *file;
  1280. X{
  1281. X    struct stat sb;
  1282. X
  1283. X    if (stat(file, &sb) < 0)
  1284. X    return (0);
  1285. X    return ((sb.st_mode & S_IFMT) & S_IFDIR);
  1286. X}
  1287. X
  1288. X/*
  1289. X * Returns non-zero if the argument file is a symbolic link.
  1290. X */
  1291. Xislink(file)
  1292. X    char *file;
  1293. X{
  1294. X    struct stat sb;
  1295. X
  1296. X    if (lstat(file, &sb) < 0)
  1297. X    return (0);
  1298. X    return ((sb.st_mode & S_IFMT) & S_IFLNK);
  1299. X}
  1300. X
  1301. X/*
  1302. X * Returns non-zero if the argument file exists.
  1303. X */
  1304. Xisfile(file)
  1305. X    char *file;
  1306. X{
  1307. X    struct stat sb;
  1308. X
  1309. X    if (stat(file, &sb) < 0)
  1310. X    return (0);
  1311. X    return (1);
  1312. X}
  1313. X
  1314. X/*
  1315. X * Returns non-zero if the argument file is readable.
  1316. X * XXX - muct be careful if "cvs" is ever made setuid!
  1317. X */
  1318. Xisreadable(file)
  1319. X    char *file;
  1320. X{
  1321. X    return (access(file, R_OK) != -1);
  1322. X}
  1323. X
  1324. X/*
  1325. X * Returns non-zero if the argument file is writable
  1326. X * XXX - muct be careful if "cvs" is ever made setuid!
  1327. X */
  1328. Xiswritable(file)
  1329. X    char *file;
  1330. X{
  1331. X    return (access(file, W_OK) != -1);
  1332. X}
  1333. X
  1334. X/*
  1335. X * Open a file and die if it fails
  1336. X */
  1337. XFILE *
  1338. Xopen_file(name, mode)
  1339. X    char *name;
  1340. X    char *mode;
  1341. X{
  1342. X    FILE *fp;
  1343. X
  1344. X    if ((fp = fopen(name, mode)) == NULL)
  1345. X    error(1, "cannot open %s", name);
  1346. X    return (fp);
  1347. X}
  1348. X
  1349. X/*
  1350. X * Make a directory and die if it fails
  1351. X */
  1352. Xmake_directory(name)
  1353. X    char *name;
  1354. X{
  1355. X    if (mkdir(name, 0777) < 0)
  1356. X    error(1, "cannot make directory %s", name);
  1357. X}
  1358. X
  1359. X/*
  1360. X * malloc some data and die if it fails
  1361. X */
  1362. Xchar *
  1363. Xxmalloc(bytes)
  1364. X    int bytes;
  1365. X{
  1366. X    extern char *malloc();
  1367. X    char *cp;
  1368. X
  1369. X    if (bytes <= 0)
  1370. X    error(0, "bad malloc size %d", bytes);
  1371. X    if ((cp = malloc((unsigned)bytes)) == NULL)
  1372. X    error(0, "malloc failed");
  1373. X    return (cp);
  1374. X}
  1375. X
  1376. X
  1377. X/*
  1378. X * ppstrcmp() is a front-end for strcmp() when the arguments
  1379. X * are pointers to pointers to chars.
  1380. X */
  1381. Xppstrcmp(pp1, pp2)
  1382. X    register char **pp1, **pp2;
  1383. X{
  1384. X    return (strcmp(*pp1, *pp2));
  1385. X}
  1386. X
  1387. X/*
  1388. X * ppstrcmp_files() is a front-end for strcmp() when the arguments
  1389. X * are pointers to pointers to chars.
  1390. X * For some reason, the ppstrcmp() above sorts in reverse order when
  1391. X * called from Entries2Files().
  1392. X */
  1393. Xppstrcmp_files(pp1, pp2)
  1394. X    register char **pp1, **pp2;
  1395. X{
  1396. X    /*
  1397. X     * Reversing the arguments here cause for the
  1398. X     * correct alphabetical order sort, as we desire.
  1399. X     */
  1400. X    return (strcmp(*pp2, *pp1));
  1401. X}
  1402. X
  1403. X/* Some UNIX distributions don't include these in their stat.h */
  1404. X#ifndef S_IWRITE
  1405. X#define    S_IWRITE    0000200        /* write permission, owner */
  1406. X#endif !S_IWRITE
  1407. X#ifndef S_IWGRP
  1408. X#define    S_IWGRP        0000020        /* write permission, grougroup */
  1409. X#endif !S_IWGRP
  1410. X#ifndef S_IWOTH
  1411. X#define    S_IWOTH        0000002        /* write permission, other */
  1412. X#endif !S_IWOTH
  1413. X
  1414. X/*
  1415. X * Change the mode of a file, either adding write permissions, or
  1416. X * removing all write permissions.  Adding write permissions honors
  1417. X * the current umask setting.
  1418. X */
  1419. Xxchmod(fname, writable)
  1420. X    char *fname;
  1421. X    int writable;
  1422. X{
  1423. X    struct stat sb;
  1424. X    int mode, oumask;
  1425. X
  1426. X    if (stat(fname, &sb) < 0) {
  1427. X    warn(1, "cannot stat %s", fname);
  1428. X    return;
  1429. X    }
  1430. X    if (writable) {
  1431. X    oumask = umask(0);
  1432. X    (void) umask(oumask);
  1433. X    mode = sb.st_mode | ((S_IWRITE|S_IWGRP|S_IWOTH) & ~oumask);
  1434. X    } else {
  1435. X    mode = sb.st_mode & ~(S_IWRITE|S_IWGRP|S_IWOTH);
  1436. X    }
  1437. X    if (chmod(fname, mode) < 0)
  1438. X    warn(1, "cannot change mode of file %s", fname);
  1439. X}
  1440. X
  1441. X/*
  1442. X * Rename a file and die if it fails
  1443. X */
  1444. Xrename_file(from, to)
  1445. X    char *from;
  1446. X    char *to;
  1447. X{
  1448. X    if (rename(from, to) < 0)
  1449. X    error(1, "cannot rename file %s to %s", from, to);
  1450. X}
  1451. X
  1452. X/*
  1453. X * Compare "file1" to "file2".
  1454. X * Return non-zero if they don't compare exactly.
  1455. X *
  1456. X * mallocs a buffer large enough to hold the entire file and
  1457. X * does two reads to load the buffer and calls bcmp to do the cmp.
  1458. X * This is reasonable, since source files are typically not too large.
  1459. X */
  1460. Xxcmp(file1, file2)
  1461. X    char *file1;
  1462. X    char *file2;
  1463. X{
  1464. X    register char *buf1, *buf2;
  1465. X    struct stat sb;
  1466. X    off_t size;
  1467. X    int ret, fd1, fd2;
  1468. X
  1469. X    if ((fd1 = open(file1, O_RDONLY)) < 0)
  1470. X    error(1, "cannot open file %s for comparing", file1);
  1471. X    if ((fd2 = open(file2, O_RDONLY)) < 0)
  1472. X    error(1, "cannot open file %s for comparing", file2);
  1473. X    if (fstat(fd1, &sb) < 0)
  1474. X    error(1, "cannot fstat %s", file1);
  1475. X    size = sb.st_size;
  1476. X    if (fstat(fd2, &sb) < 0)
  1477. X    error(1, "cannot fstat %s", file2);
  1478. X    if (size == sb.st_size) {
  1479. X    if (size == 0)
  1480. X        ret = 0;
  1481. X    else {
  1482. X        buf1 = xmalloc((int)size);
  1483. X        buf2 = xmalloc((int)size);
  1484. X        if (read(fd1, buf1, (int)size) != (int)size)
  1485. X        error(1, "cannot read file %s cor comparing", file1);
  1486. X        if (read(fd2, buf2, (int)size) != (int)size)
  1487. X        error(1, "cannot read file %s for comparing", file2);
  1488. X        ret = bcmp(buf1, buf2, (int)size);
  1489. X        free(buf1);
  1490. X        free(buf2);
  1491. X    }
  1492. X    } else
  1493. X    ret = 1;
  1494. X    (void) close(fd1);
  1495. X    (void) close(fd2);
  1496. X    return (ret);
  1497. X}
  1498. X
  1499. X/*
  1500. X * Recover the space allocated by Find_Names() and line2argv()
  1501. X */
  1502. Xfree_names(pargc, argv)
  1503. X    int *pargc;
  1504. X    char *argv[];
  1505. X{
  1506. X    register int i;
  1507. X
  1508. X    for (i = 0; i < *pargc; i++) {    /* only do through *pargc */
  1509. X    free(argv[i]);
  1510. X    }
  1511. X    *pargc = 0;                /* and set it to zero when done */
  1512. X}
  1513. X
  1514. X/*
  1515. X * Convert a line into argc/argv components and return the result in
  1516. X * the arguments as passed.  Use free_names() to return the memory
  1517. X * allocated here back to the free pool.
  1518. X */
  1519. Xline2argv(pargc, argv, line)
  1520. X    int *pargc;
  1521. X    char *argv[];
  1522. X    char *line;
  1523. X{
  1524. X    char *cp;
  1525. X
  1526. X    *pargc = 0;
  1527. X    for (cp = strtok(line, " \t"); cp; cp = strtok((char *)NULL, " \t")) {
  1528. X    argv[*pargc] = xmalloc(strlen(cp) + 1);
  1529. X    (void) strcpy(argv[*pargc], cp);
  1530. X    (*pargc)++;
  1531. X    }
  1532. X}
  1533. X
  1534. X/*
  1535. X * Returns the number of dots ('.') found in an RCS revision number
  1536. X */
  1537. Xnumdots(s)
  1538. X    char *s;
  1539. X{
  1540. X    char *cp;
  1541. X    int dots = 0;
  1542. X
  1543. X    for (cp = s; *cp; cp++) {
  1544. X    if (*cp == '.')
  1545. X        dots++;
  1546. X    }
  1547. X    return (dots);
  1548. X}
  1549. END_OF_FILE
  1550. if test 8199 -ne `wc -c <'src/subr.c'`; then
  1551.     echo shar: \"'src/subr.c'\" unpacked with wrong size!
  1552. fi
  1553. # end of 'src/subr.c'
  1554. fi
  1555. if test -f 'src/version_number.c' -a "${1}" != "-c" ; then 
  1556.   echo shar: Will not clobber existing file \"'src/version_number.c'\"
  1557. else
  1558. echo shar: Extracting \"'src/version_number.c'\" \(10526 characters\)
  1559. sed "s/^X//" >'src/version_number.c' <<'END_OF_FILE'
  1560. X#ifndef lint
  1561. Xstatic char rcsid[] = "$Id: version_number.c,v 1.16 89/11/19 23:20:35 berliner Exp $";
  1562. X#endif !lint
  1563. X
  1564. X/*
  1565. X *    Copyright (c) 1989, Brian Berliner
  1566. X *
  1567. X *    You may distribute under the terms of the GNU General Public License
  1568. X *    as specified in the README file that comes with the CVS 1.0 kit.
  1569. X *
  1570. X * Version Number
  1571. X *
  1572. X *    Returns the requested version number of the RCS file, satisfying tags
  1573. X *    and walking branches, if necessary.
  1574. X *
  1575. X *    "rcs" is the full pathname to the ,v file which is read to determine
  1576. X *    the current head version number for the RCS file.  Things are
  1577. X *    complicated by needing to walk branches and symbolic tags.  The
  1578. X *    algorithm used here for branches is not quite the same as that
  1579. X *    included with RCS, but should work 99.999% of the time (or more).
  1580. X *
  1581. X *    The result is placed in "vers"; null-string if error.
  1582. X */
  1583. X
  1584. X#include <ctype.h>
  1585. X#include "cvs.h"
  1586. X
  1587. XVersion_Number(rcs, tag, date, vers)
  1588. X    char *rcs;
  1589. X    char *tag;
  1590. X    char *date;
  1591. X    char *vers;
  1592. X{
  1593. X    FILE *fpin;
  1594. X    char rev[50];
  1595. X
  1596. X    vers[0] = rev[0] = '\0';        /* Assume failure */
  1597. X    /*
  1598. X     * Open the RCS file.  If it failed, just return, as the version
  1599. X     * is already nulled.
  1600. X     */
  1601. X    if ((fpin = fopen(rcs, "r")) == NULL)
  1602. X    return;
  1603. X    get_version(fpin, tag, date, rev, vers);
  1604. X    if (vers[0] == '\0' && tag[0] != '\0') {
  1605. X    if (!isdigit(tag[0])) {
  1606. X        char *cp, temp[MAXLINELEN];
  1607. X        extern char update_dir[];
  1608. X
  1609. X        if (CVSroot[0] != '\0' &&
  1610. X        strncmp(rcs, CVSroot, strlen(CVSroot)) == 0) {
  1611. X        cp = rcs + strlen(CVSroot) + 1;
  1612. X        } else {
  1613. X        if ((cp = index(rcs, '/')) == NULL && update_dir[0] != '\0') {
  1614. X            (void) sprintf(temp, "%s/%s", update_dir, rcs);
  1615. X            cp = temp;
  1616. X        } else {
  1617. X            cp = rcs;
  1618. X        }
  1619. X        }
  1620. X        if (force_tag_match) {
  1621. X        if (!quiet)
  1622. X            warn(0, "tag %s undefined in %s; ignored", tag, cp);
  1623. X        } else {
  1624. X        if (!quiet)
  1625. X            warn(0, "tag %s undefined in %s; using %s", tag, cp, rev);
  1626. X        (void) strcpy(vers, rev);
  1627. X        }
  1628. X    } else {
  1629. X        (void) strcpy(vers, rev);
  1630. X    }
  1631. X    }
  1632. X    (void) fclose(fpin);
  1633. X}
  1634. X
  1635. X/*
  1636. X * The main driver for doing the work of getting the required revision
  1637. X * number.  Returns computed revision in "rev" or "vers" depending
  1638. X * on whether the "tag" could be satisfied or not.
  1639. X */
  1640. Xstatic
  1641. Xget_version(fp, tag, date, rev, vers)
  1642. X    FILE *fp;
  1643. X    char *tag;
  1644. X    char *date;
  1645. X    char *rev;
  1646. X    char *vers;
  1647. X{
  1648. X    char line[MAXLINELEN];
  1649. X    char *cp;
  1650. X    int symtag_matched = 0;
  1651. X
  1652. X    /*
  1653. X     * Scan to find the current "head" setting,
  1654. X     * which really isn't the head if the RCS file is using a branch
  1655. X     * for the head, sigh.
  1656. X     *
  1657. X     * Assumption here is that the "head" line is always first.
  1658. X     */
  1659. X    if (fgets(line, sizeof(line), fp) == NULL)
  1660. X    return;
  1661. X    if (strncmp(line, RCSHEAD, sizeof(RCSHEAD) - 1) != 0 ||
  1662. X    (cp = rindex(line, ';')) == NULL)
  1663. X    return;
  1664. X    *cp = '\0';                /* strip the ';' */
  1665. X    if ((cp = rindex(line, ' ')) == NULL &&
  1666. X    (cp = rindex(line, '\t')) == NULL)
  1667. X    return;
  1668. X    cp++;
  1669. X    (void) strcpy(rev, cp);
  1670. X    /*
  1671. X     * The "rev" string now contains the value of the RCS "head".
  1672. X     * Read the next line to find out if we should walk the branches.
  1673. X     *
  1674. X     * Assumption here is that "branch" is always on the second line
  1675. X     * of the RCS file.  If a "branch" line does not exist, we assume
  1676. X     * it is an old format RCS file, and blow it off.
  1677. X     */
  1678. X    if (fgets(line, sizeof(line), fp) == NULL)
  1679. X    return;
  1680. X    if (strncmp(line, RCSBRANCH, sizeof(RCSBRANCH) - 1) == 0 &&
  1681. X    (cp = rindex(line, ';')) != NULL) {
  1682. X    *cp = '\0';            /* strip the ';' */
  1683. X    if ((cp = rindex(line, ' ')) == NULL &&
  1684. X        (cp = rindex(line, '\t')) == NULL)
  1685. X        return;
  1686. X    cp++;
  1687. X    if (*cp != NULL)
  1688. X        (void) strcpy(rev, cp);
  1689. X    }
  1690. X    /*
  1691. X     * "rev" now contains either the "head" value, or the "branch"
  1692. X     * value (if it was set).  If we're looking for a particular symbolic
  1693. X     * or numeric tag, we must find the symbol, and then do
  1694. X     * branch completion as usual, if necessary.
  1695. X     */
  1696. X    if (date[0] != '\0') {
  1697. X    get_date(fp, date, rev, vers);
  1698. X    return;
  1699. X    }
  1700. X    if (tag[0] != '\0') {
  1701. X    /* return of 0 means we found an exact match, or there was an error */
  1702. X    if ((symtag_matched = get_tag(fp, tag, rev, vers)) == 0)
  1703. X        return;
  1704. X    }
  1705. X    /*
  1706. X     * "rev" now contains either the "head" value, or the tag value,
  1707. X     * or the "branch" value.  get_branch() will fill in "rev" with
  1708. X     * the highest numbered branch off "rev", if necessary.
  1709. X     */
  1710. X    get_branch(fp, rev);
  1711. X    if (tag[0] == '\0' || isdigit(tag[0]) || symtag_matched < 0)
  1712. X    (void) strcpy(vers, rev);
  1713. X}
  1714. X
  1715. X/*
  1716. X * We were requested to find a particular symbolic or numeric revision.
  1717. X * So, scan for the "symbols" line in the RCS file, or for the first
  1718. X * match of a line if the tag is numeric.
  1719. X *
  1720. X * Would really like to use strtok() here, but callers in update() are
  1721. X * already using it.
  1722. X *
  1723. X * Return value of 0 means to use "vers" as it stands, while a return value
  1724. X * of 1 means to use "rev", but to check for possible branch completions
  1725. X * to find the head of a branch.
  1726. X */
  1727. Xstatic
  1728. Xget_tag(fp, tag, rev, vers)
  1729. X    FILE *fp;
  1730. X    char *tag;
  1731. X    char *rev;
  1732. X    char *vers;
  1733. X{
  1734. X    char line[MAXLINELEN], tagdot[50];
  1735. X    char *cp, *cprev;
  1736. X    int tagdots, tagdotlen;
  1737. X
  1738. X    if (isdigit(tag[0])) {
  1739. X    while (tag[strlen(tag)] == '.')
  1740. X        tag[strlen(tag)] = '\0';    /* strip trailing dots */
  1741. X    (void) sprintf(tagdot, "%s.", tag);
  1742. X    tagdotlen = strlen(tagdot);
  1743. X    tagdots = numdots(tag);
  1744. X    }
  1745. X    while (fgets(line, sizeof(line), fp) != NULL) {
  1746. X    if (strncmp(line, RCSDESC, sizeof(RCSDESC) - 1) == 0) {
  1747. X        /*
  1748. X         * Use head, or a partial branch match found with the strncmp
  1749. X         * call with tagdot below
  1750. X         */
  1751. X        rewind(fp);
  1752. X        return (1);
  1753. X    }
  1754. X    /*
  1755. X     * For numeric tags, the RCS file contains the revision
  1756. X     * number all by itself on a single line, so we check for
  1757. X     * that match here.
  1758. X     */
  1759. X    if (isdigit(tag[0])) {
  1760. X        if ((cp = rindex(line, '\n')) != NULL)
  1761. X        *cp = '\0';
  1762. X        if (strcmp(tag, line) == 0) {
  1763. X        (void) strcpy(vers, line);
  1764. X        return (0);        /* a match for a numeric tag */
  1765. X        }
  1766. X        if (strncmp(tagdot, line, tagdotlen) == 0) {
  1767. X        if ((tagdots & 1) == 0 && numdots(line) == tagdots+1)
  1768. X            (void) strcpy(rev, line);
  1769. X        }
  1770. X        continue;
  1771. X    }
  1772. X    if (strncmp(line, RCSSYMBOL, sizeof(RCSSYMBOL)-1)!=0 ||
  1773. X        (cp = rindex(line, ';')) == NULL)
  1774. X        continue;
  1775. X    *cp = ' ';            /* strip the ';' */
  1776. X    if ((cp = index(line, ' ')) == NULL &&
  1777. X        (cp = index(line, '\t')) == NULL)
  1778. X        continue;
  1779. X    /*
  1780. X     * A rather ugly loop to process the "symbols" line.  I would
  1781. X     * really rather use strtok(), but other above me already are,
  1782. X     * and strtok() blows up in this case.
  1783. X     */
  1784. X    while (cp && *cp) {
  1785. X        while (isspace(*cp))
  1786. X        cp++;
  1787. X        /* symbols and revisions are separated by a colon */
  1788. X        if ((cprev = index(cp, ':')) == NULL) {
  1789. X        while (*cp && !isspace(*cp))
  1790. X            cp++;
  1791. X        continue;
  1792. X        }
  1793. X        *cprev++ = '\0';
  1794. X        /*
  1795. X         * "cp" points to the NULL-terminated symbolic name;
  1796. X         * "cprev" points to the revision, which must be NULL-terminated;
  1797. X         */
  1798. X        if (strcmp(tag, cp) == 0) {
  1799. X        if ((cp = index(cprev, ' ')) == NULL
  1800. X            && (cp = index(cprev, ';')) == NULL
  1801. X            && (cp = index(cprev, '\n')) == NULL)
  1802. X            continue;
  1803. X        *cp = '\0';
  1804. X        (void) strcpy(rev, cprev);
  1805. X        return (-1);        /* look for branches off rev */
  1806. X        } else {
  1807. X        while (!isspace(*cp))
  1808. X            cp++;
  1809. X        cp++;
  1810. X        }
  1811. X    }
  1812. X    return (1);
  1813. X    }
  1814. X    return (0);
  1815. X}
  1816. X
  1817. X/*
  1818. X * Decides if we should determine the highest branch number, and
  1819. X * returns it in "rev".  This is only done if there are
  1820. X * an even number of dots ('.') in the revision number passed in.
  1821. X */
  1822. Xstatic
  1823. Xget_branch(fp, rev)
  1824. X    FILE *fp;
  1825. X    char *rev;
  1826. X{
  1827. X    char line[MAXLINELEN];
  1828. X    char branch[50];
  1829. X    char *cp;
  1830. X    int len, dots = numdots(rev);
  1831. X
  1832. X    if ((dots & 1) != 0)
  1833. X    return;
  1834. X    (void) sprintf(branch, "%s.", rev);
  1835. X    len = strlen(branch);
  1836. X    while (fgets(line, sizeof(line), fp) != NULL) {
  1837. X    if (strncmp(line, RCSDESC, sizeof(RCSDESC) - 1) == 0)
  1838. X        return;
  1839. X    if (isdigit(line[0])) {
  1840. X        if ((cp = rindex(line, '\n')) != NULL)
  1841. X        *cp = '\0';        /* strip the newline */
  1842. X        if (numdots(line) == dots+1 &&
  1843. X        strncmp(line, branch, len) == 0) {
  1844. X        if (strcmp(branch, line) <= 0)
  1845. X            (void) strcpy(rev, line);
  1846. X        }
  1847. X    }
  1848. X    }
  1849. X}
  1850. X
  1851. X/*
  1852. X * Look up a the most recent revision, based on the supplied date.
  1853. X * But do some funky stuff if necessary to follow any vendor branch.
  1854. X */
  1855. Xstatic
  1856. Xget_date(fp, date, rev, version)
  1857. X    FILE *fp;
  1858. X    char *date;
  1859. X    char *rev;
  1860. X    char *version;
  1861. X{
  1862. X    char *cp;
  1863. X
  1864. X    if ((numdots(rev) & 1) == 0) {
  1865. X    /*
  1866. X     * A branch is the head, so get the revision from the branch
  1867. X     * specified in "rev".  If we didn't get a match, try the trunk.
  1868. X     */
  1869. X    get_branch_date(fp, date, rev, version);
  1870. X    if (version[0] == '\0') {
  1871. X        if ((cp = index(rev, '.')) != NULL)
  1872. X        *cp = '\0';
  1873. X        rewind(fp);
  1874. X        get_branch_date(fp, date, rev, version);
  1875. X    }
  1876. X    } else {
  1877. X    /*
  1878. X     * The trunk is the head.  Get the revision from the trunk and
  1879. X     * see if it evaluates to 1.1.  If so, walk the 1.1.1 branch looking
  1880. X     * for a match and return that; if not, just return 1.1.
  1881. X     */
  1882. X    if ((cp = rindex(rev, '.')) != NULL)
  1883. X        *cp = '\0';            /* turn the revision into a branch */
  1884. X    get_branch_date(fp, date, rev, version);
  1885. X    if (strcmp(version, "1.1") == 0) {
  1886. X        rewind(fp);
  1887. X        get_branch_date(fp, date, "1.1.1", version);
  1888. X    }
  1889. X    }
  1890. X}
  1891. X
  1892. Xstatic
  1893. Xget_branch_date(fp, date, rev, version)
  1894. X    FILE *fp;
  1895. X    char *date;
  1896. X    char *rev;
  1897. X    char *version;
  1898. X{
  1899. X    char line[MAXLINELEN], last_rev[50], curdate[50], date_dot[50];
  1900. X    int date_dots, date_dotlen;
  1901. X    char *cp, *semi;
  1902. X
  1903. X    last_rev[0] = '\0';
  1904. X    (void) strcpy(curdate, "00");    /* what happens at 2000 ad? */
  1905. X    (void) sprintf(date_dot, "%s.", rev);
  1906. X    date_dotlen = strlen(date_dot);
  1907. X    date_dots = numdots(rev);
  1908. X    while (fgets(line, sizeof(line), fp) != NULL) {
  1909. X    if (strncmp(line, RCSDESC, sizeof(RCSDESC) - 1) == 0)
  1910. X        return;
  1911. X    if (isdigit(line[0])) {
  1912. X        if ((cp = rindex(line, '\n')) != NULL)
  1913. X        *cp = '\0';        /* strip the newline */
  1914. X        if ((date_dots == 0 || strncmp(date_dot, line, date_dotlen) == 0) &&
  1915. X        numdots(line) == date_dots+1)
  1916. X        (void) strcpy(last_rev, line);
  1917. X        else
  1918. X        last_rev[0] = '\0';
  1919. X        continue;
  1920. X    }
  1921. X    if (strncmp(line, RCSDATE, sizeof(RCSDATE) - 1) == 0 &&
  1922. X        last_rev[0] != '\0') {
  1923. X        for (cp = line; *cp && !isspace(*cp); cp++)
  1924. X        ;
  1925. X        while (*cp && isspace(*cp))
  1926. X        cp++;
  1927. X        if (*cp && (semi = index(cp, ';')) != NULL) {
  1928. X        *semi = '\0';        /* strip the semicolon */
  1929. X        if (strcmp(cp, date) <= 0 && strcmp(cp, curdate) >= 0) {
  1930. X            (void) strcpy(curdate, cp);
  1931. X            (void) strcpy(version, last_rev);
  1932. X        }
  1933. X        }
  1934. X    }
  1935. X    }
  1936. X}
  1937. END_OF_FILE
  1938. if test 10526 -ne `wc -c <'src/version_number.c'`; then
  1939.     echo shar: \"'src/version_number.c'\" unpacked with wrong size!
  1940. fi
  1941. # end of 'src/version_number.c'
  1942. fi
  1943. echo shar: End of archive 3 \(of 7\).
  1944. cp /dev/null ark3isdone
  1945. MISSING=""
  1946. for I in 1 2 3 4 5 6 7 ; do
  1947.     if test ! -f ark${I}isdone ; then
  1948.     MISSING="${MISSING} ${I}"
  1949.     fi
  1950. done
  1951. if test "${MISSING}" = "" ; then
  1952.     echo You have unpacked all 7 archives.
  1953.     rm -f ark[1-9]isdone
  1954. else
  1955.     echo You still need to unpack the following archives:
  1956.     echo "        " ${MISSING}
  1957. fi
  1958. ##  End of shell archive.
  1959. exit 0
  1960. exit 0 # Just in case...
  1961.