home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume27 / aegis-2.1 / part14 < prev    next >
Encoding:
Text File  |  1993-09-24  |  134.1 KB  |  6,226 lines

  1. Newsgroups: comp.sources.unix
  2. From: pmiller@bmr.gov.au (Peter Miller)
  3. Subject: v27i049: aegis - project change supervisor (V2.1), Part14/19
  4. References: <1.748951883.12788@gw.home.vix.com>
  5. Sender: unix-sources-moderator@gw.home.vix.com
  6. Approved: vixie@gw.home.vix.com
  7.  
  8. Submitted-By: pmiller@bmr.gov.au (Peter Miller)
  9. Posting-Number: Volume 27, Issue 49
  10. Archive-Name: aegis-2.1/part14
  11.  
  12. #! /bin/sh
  13. # This is a shell archive.  Remove anything before this line, then unpack
  14. # it by saving it into a file and typing "sh file".  To overwrite existing
  15. # files, type "sh file -c".  You can also feed this as standard input via
  16. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  17. # will see the following message at the end:
  18. #        "End of archive 14 (of 19)."
  19. # Contents:  aegis/glue.c aegis/project.c aegis/user.c doc/c7.1.so
  20. # Wrapped by vixie@gw.home.vix.com on Sat Sep 25 03:00:50 1993
  21. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  22. if test -f 'aegis/glue.c' -a "${1}" != "-c" ; then 
  23.   echo shar: Will not clobber existing file \"'aegis/glue.c'\"
  24. else
  25. echo shar: Extracting \"'aegis/glue.c'\" \(40198 characters\)
  26. sed "s/^X//" >'aegis/glue.c' <<'END_OF_FILE'
  27. X/*
  28. X *    aegis - project change supervisor
  29. X *    Copyright (C) 1993 Peter Miller.
  30. X *    All rights reserved.
  31. X *
  32. X *    This program is free software; you can redistribute it and/or modify
  33. X *    it under the terms of the GNU General Public License as published by
  34. X *    the Free Software Foundation; either version 2 of the License, or
  35. X *    (at your option) any later version.
  36. X *
  37. X *    This program is distributed in the hope that it will be useful,
  38. X *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  39. X *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  40. X *    GNU General Public License for more details.
  41. X *
  42. X *    You should have received a copy of the GNU General Public License
  43. X *    along with this program; if not, write to the Free Software
  44. X *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  45. X *
  46. X * MANIFEST: functions to perform systems calls in subprocesses
  47. X *
  48. X * Most of the functions in this file are only used when
  49. X * the CONF_NO_seteuid  symbol is defined in common/conf.h.
  50. X *
  51. X * The various system calls wgich must be perfomed as a specific user
  52. X * are given to a "proxy" process to perform, where this proxy process
  53. X * has the appropriate real (and effective) user id and group id.
  54. X * These processes terminate when they see end-of-file on their command
  55. X * streams, which only happens when the parent process exits.
  56. X *
  57. X * The overhead of communicating with the proxy process will
  58. X * naturally result in a performance hit for systems without
  59. X * a seteuid system call.
  60. X *
  61. X * Note that some systems, notably AIX, have a seteuid system call
  62. X * which is broken.  These systems will also need to use this glue.
  63. X */
  64. X
  65. X#include <sys/types.h>
  66. X#include <dirent.h>
  67. X#include <errno.h>
  68. X#include <fcntl.h>
  69. X#include <signal.h>
  70. X#include <stdio.h>
  71. X#include <stdlib.h>
  72. X#include <unistd.h>
  73. X#include <utime.h>
  74. X
  75. X#include <error.h>
  76. X#include <glue.h>
  77. X#include <mem.h>
  78. X#include <os.h>
  79. X#include <trace.h>
  80. X#include <undo.h>
  81. X
  82. X#ifdef CONF_NO_seteuid
  83. X
  84. X#define GUARD1 0x416E6479
  85. X#define GUARD2 0x4C777279
  86. X
  87. Xenum
  88. X{
  89. X    command_access,
  90. X    command_catfile,
  91. X    command_chmod,
  92. X    command_chown,
  93. X    command_close,
  94. X    command_copyfile,
  95. X    command_creat,
  96. X    command_fcntl,
  97. X    command_getcwd,
  98. X    command_link,
  99. X    command_lstat,
  100. X    command_mkdir,
  101. X    command_open,
  102. X    command_read,
  103. X    command_readlink,
  104. X    command_read_whole_dir,
  105. X    command_rename,
  106. X    command_rmdir,
  107. X    command_stat,
  108. X    command_unlink,
  109. X    command_utime,
  110. X    command_write,
  111. X};
  112. X
  113. X
  114. Xtypedef struct proxy_ty proxy_ty;
  115. Xstruct proxy_ty
  116. X{
  117. X    int        hash;
  118. X    int        uid;
  119. X    int        gid;
  120. X    int        umask;
  121. X    int        pid;
  122. X    FILE        *command;
  123. X    FILE        *reply;
  124. X    proxy_ty    *next;
  125. X
  126. X};
  127. X
  128. X#define proxy_hash(u, g, m) (((u) * 16 + (g) * 4 + (m)) & 0x7FFF)
  129. X
  130. X/*
  131. X * This table will be sparsely filled.
  132. X * The maximum number of entries expected is 4.
  133. X * The size MUST be prime.
  134. X */
  135. Xstatic proxy_ty    *proxy_table[17];
  136. X
  137. X
  138. X#ifdef DEBUG
  139. X
  140. Xstatic char *command_name _((int));
  141. X
  142. Xstatic char *
  143. Xcommand_name(n)
  144. X    int        n;
  145. X{
  146. X    static char    buf[12];
  147. X
  148. X    switch (n)
  149. X    {
  150. X    case EOF:        return "quit";
  151. X    case command_access:    return "access";
  152. X    case command_catfile:    return "catfile";
  153. X    case command_chmod:    return "chmod";
  154. X    case command_chown:    return "chown";
  155. X    case command_close:    return "close";
  156. X    case command_copyfile:    return "copyfile";
  157. X    case command_creat:    return "creat";
  158. X    case command_fcntl:    return "fcntl";
  159. X    case command_getcwd:    return "getcwd";
  160. X    case command_link:    return "link";
  161. X    case command_lstat:    return "lstat";
  162. X    case command_mkdir:    return "mkdir";
  163. X    case command_open:    return "open";
  164. X    case command_read:    return "read";
  165. X    case command_readlink:    return "readlink";
  166. X    case command_read_whole_dir: return "read_whole_dir";
  167. X    case command_rename:    return "rename";
  168. X    case command_rmdir:    return "rmdir";
  169. X    case command_stat:    return "stat";
  170. X    case command_unlink:    return "unlink";
  171. X    case command_utime:    return "utime";
  172. X    case command_write:    return "write";
  173. X    }
  174. X    sprintf(buf, "%d", n);
  175. X    return buf;
  176. X}
  177. X
  178. X#endif
  179. X
  180. X
  181. Xstatic void put_int _((FILE *, int));
  182. X
  183. Xstatic void
  184. Xput_int(fp, n)
  185. X    FILE        *fp;
  186. X    int        n;
  187. X{
  188. X    int        j;
  189. X    unsigned char    *ptr;
  190. X
  191. X    trace(("put_int(%d)\n{\n"/*}*/, n));
  192. X    ptr = (unsigned char *)&n; 
  193. X    for (j = 0; j < sizeof(int); ++j)
  194. X        putc(ptr[j], fp);
  195. X    if (ferror(fp))
  196. X        nfatal("writing pipe");
  197. X    trace((/*{*/"}\n"));
  198. X}
  199. X
  200. X
  201. Xstatic int get_int _((FILE *));
  202. X
  203. Xstatic int
  204. Xget_int(fp)
  205. X    FILE        *fp;
  206. X{
  207. X    int        result;
  208. X    int        j;
  209. X    unsigned char    *ptr;
  210. X
  211. X    trace(("get_int()\n{\n"/*}*/));
  212. X    ptr = (unsigned char *)&result;
  213. X    for (j = 0; j < sizeof(int); ++j)
  214. X    {
  215. X        int    c;
  216. X
  217. X        c = getc(fp);
  218. X        if (c == EOF)
  219. X        {
  220. X            if (ferror(fp))
  221. X                nfatal("reading pipe");
  222. X            fatal("reading pipe: proxy protocol error (%d)", getpid());
  223. X        }
  224. X        ptr[j] = c;
  225. X    }
  226. X    trace(("return %d;\n", result));
  227. X    trace((/*{*/"}\n"));
  228. X    return result;
  229. X}
  230. X
  231. X
  232. Xstatic void put_long _((FILE *, long));
  233. X
  234. Xstatic void
  235. Xput_long(fp, n)
  236. X    FILE        *fp;
  237. X    long        n;
  238. X{
  239. X    int        j;
  240. X    unsigned char    *ptr;
  241. X
  242. X    trace(("put_long(%ld)\n{\n"/*}*/, n));
  243. X    ptr = (unsigned char *)&n; 
  244. X    for (j = 0; j < sizeof(long); ++j)
  245. X        putc(ptr[j], fp);
  246. X    if (ferror(fp))
  247. X        nfatal("writing pipe");
  248. X    trace((/*{*/"}\n"));
  249. X}
  250. X
  251. X
  252. Xstatic long get_long _((FILE *));
  253. X
  254. Xstatic long
  255. Xget_long(fp)
  256. X    FILE        *fp;
  257. X{
  258. X    long        result;
  259. X    int        j;
  260. X    unsigned char    *ptr;
  261. X
  262. X    trace(("get_long()\n{\n"/*}*/));
  263. X    ptr = (unsigned char *)&result;
  264. X    for (j = 0; j < sizeof(long); ++j)
  265. X    {
  266. X        int    c;
  267. X
  268. X        c = getc(fp);
  269. X        if (c == EOF)
  270. X        {
  271. X            if (ferror(fp))
  272. X                nfatal("reading pipe");
  273. X            fatal("reading pipe: proxy protocol error (%d)", getpid());
  274. X        }
  275. X        ptr[j] = c;
  276. X    }
  277. X    trace(("return %ld;\n", result));
  278. X    trace((/*{*/"}\n"));
  279. X    return result;
  280. X}
  281. X
  282. X
  283. Xstatic void put_binary _((FILE *, void *, size_t));
  284. X
  285. Xstatic void
  286. Xput_binary(fp, ptr, len)
  287. X    FILE        *fp;
  288. X    void        *ptr;
  289. X    size_t        len;
  290. X{
  291. X    trace(("put_binary(%ld)\n{\n"/*}*/, len));
  292. X    fwrite(ptr, 1, len, fp);
  293. X    if (ferror(fp))
  294. X        nfatal("writing pipe");
  295. X    trace((/*{*/"}\n"));
  296. X}
  297. X
  298. X
  299. Xstatic void get_binary _((FILE *, void *, size_t));
  300. X
  301. Xstatic void
  302. Xget_binary(fp, ptr, len)
  303. X    FILE        *fp;
  304. X    void        *ptr;
  305. X    size_t        len;
  306. X{
  307. X    long        n;
  308. X
  309. X    trace(("get_binary(%ld)\n{\n"/*}*/, len));
  310. X    n = fread(ptr, 1, len, fp);
  311. X    if (n != len)
  312. X    {
  313. X        if (ferror(fp))
  314. X            nfatal("reading pipe");
  315. X        fatal("reading pipe: proxy protocol error (%d)", getpid());
  316. X    }
  317. X    trace((/*{*/"}\n"));
  318. X}
  319. X
  320. X
  321. Xstatic void put_string _((FILE *, char *));
  322. X
  323. Xstatic void
  324. Xput_string(fp, s)
  325. X    FILE        *fp;
  326. X    char        *s;
  327. X{
  328. X    trace(("put_string(\"%s\")\n{\n"/*}*/, s));
  329. X    for (;;)
  330. X    {
  331. X        putc(*s, fp);
  332. X        if (ferror(fp))
  333. X            nfatal("writing pipe");
  334. X        if (!*s)
  335. X            break;
  336. X        ++s;
  337. X    }
  338. X    trace((/*{*/"}\n"));
  339. X}
  340. X
  341. X
  342. Xstatic void *get_string _((FILE *));
  343. X
  344. Xstatic void *
  345. Xget_string(fp)
  346. X    FILE        *fp;
  347. X{
  348. X    static char    *result;
  349. X    static size_t    result_max;
  350. X    size_t        pos;
  351. X
  352. X    trace(("get_string()\n{\n"/*}*/));
  353. X    if (!result)
  354. X    {
  355. X        result_max = (1L << 10 ) - 32;
  356. X        result = mem_alloc(result_max);
  357. X    }
  358. X
  359. X    pos = 0;
  360. X    for (;;)
  361. X    {
  362. X        int    c;
  363. X
  364. X        c = getc(fp);
  365. X        if (c == EOF)
  366. X        {
  367. X            if (ferror(fp))
  368. X                nfatal("reading pipe");
  369. X            fatal("reading pipe: proxy protocol error (%d)", getpid());
  370. X        }
  371. X        if (pos >= result_max)
  372. X        {
  373. X            result_max += (1L << 10);
  374. X            mem_change_size(&result, result_max);
  375. X        }
  376. X        result[pos] = c;
  377. X        if (!c)
  378. X            break;
  379. X        ++pos;
  380. X    }
  381. X    trace(("return \"%s\";\n", result));
  382. X    trace((/*{*/"}\n"));
  383. X    return result;
  384. X}
  385. X
  386. X
  387. Xstatic void proxy _((int rd, int wr));
  388. X
  389. Xstatic void
  390. Xproxy(rd_fd, wr_fd)
  391. X    int        rd_fd;
  392. X    int        wr_fd;
  393. X{
  394. X    FILE        *command;
  395. X    FILE        *reply;
  396. X    char        *path;
  397. X    char        *path1;
  398. X    int        mode;
  399. X    struct stat    st;
  400. X    struct utimbuf    utb;
  401. X    int        uid;
  402. X    int        gid;
  403. X    char        *buf;
  404. X    int        max;
  405. X    int        perm;
  406. X    int        result;
  407. X    int        fd;
  408. X    long        nbytes;
  409. X    long        nbytes2;
  410. X    struct flock    flock;
  411. X
  412. X    trace(("proxy(%d, %d)\n{\n"/*}*/, rd_fd, wr_fd));
  413. X    errno = 0;
  414. X    command = fdopen(rd_fd, "r");
  415. X    if (!command)
  416. X    {
  417. X        if (!errno)
  418. X            errno = ENOMEM;
  419. X        exit(errno);
  420. X    }
  421. X
  422. X    errno = 0;
  423. X    reply = fdopen(wr_fd, "w");
  424. X    if (!reply)
  425. X    {
  426. X        if (!errno)
  427. X            errno = ENOMEM;
  428. X        exit(errno);
  429. X    }
  430. X
  431. X    for (;;)
  432. X    {
  433. X        int    c;
  434. X
  435. X        c = getc(command);
  436. X        trace(("command: %s\n", command_name(c)));
  437. X        trace(("uid = %d;\n", getuid()));
  438. X        trace(("gid = %d;\n", getgid()));
  439. X        switch (c)
  440. X        {
  441. X        case EOF:
  442. X            if (ferror(command))
  443. X                exit(errno);
  444. X            exit(0);
  445. X
  446. X        default:
  447. X            fatal("proxy: unknown %d command (bug)", c);
  448. X
  449. X        case command_access:
  450. X            path = get_string(command);
  451. X            mode = get_int(command);
  452. X            if (access(path, mode))
  453. X                result = errno;
  454. X            else
  455. X                result = 0;
  456. X            put_int(reply, result);
  457. X            break;
  458. X
  459. X        case command_catfile:
  460. X            path = get_string(command);
  461. X            if (catfile(path))
  462. X                result = errno;
  463. X            else
  464. X                result = 0;
  465. X            put_int(reply, result);
  466. X            break;
  467. X
  468. X        case command_chmod:
  469. X            path = get_string(command);
  470. X            mode = get_int(command);
  471. X            if (chmod(path, mode))
  472. X                result = errno;
  473. X            else
  474. X                result = 0;
  475. X            put_int(reply, result);
  476. X            break;
  477. X
  478. X        case command_chown:
  479. X            path = get_string(command);
  480. X            uid = get_int(command);
  481. X            gid = get_int(command);
  482. X            if (chown(path, uid, gid))
  483. X                result = errno;
  484. X            else
  485. X                result = 0;
  486. X            put_int(reply, result);
  487. X            break;
  488. X
  489. X        case command_close:
  490. X            fd = get_int(command);
  491. X            if (close(fd))
  492. X                result = errno;
  493. X            else
  494. X                result = 0;
  495. X            put_int(reply, result);
  496. X            break;
  497. X
  498. X        case command_copyfile:
  499. X            path = get_string(command);
  500. X            path1 = mem_copy_string(path);
  501. X            path = get_string(command);
  502. X            result = copyfile(path1, path);
  503. X            if (result)
  504. X                result = errno;
  505. X            mem_free(path1);
  506. X            put_int(reply, result);
  507. X            break;
  508. X
  509. X        case command_creat:
  510. X            path = get_string(command);
  511. X            mode = get_int(command);
  512. X            result = creat(path, mode);
  513. X            put_int(reply, result);
  514. X            if (result < 0)
  515. X                put_int(reply, errno);
  516. X            break;
  517. X
  518. X        case command_getcwd:
  519. X            max = get_int(command);
  520. X            path = mem_alloc(max);
  521. X            if (!getcwd(path, max))
  522. X                put_int(reply, errno);
  523. X            else
  524. X            {
  525. X                put_int(reply, 0);
  526. X                put_string(reply, path);
  527. X            }
  528. X            mem_free(path);
  529. X            break;
  530. X
  531. X        case command_fcntl:
  532. X            fd = get_int(command);
  533. X            mode = get_int(command);
  534. X            get_binary(command, &flock, sizeof(flock));
  535. X            result = fcntl(fd, mode, &flock);
  536. X            if (result)
  537. X                result = errno;
  538. X            put_int(reply, result);
  539. X            put_binary(reply, &flock, sizeof(flock));
  540. X            break;
  541. X
  542. X        case command_lstat:
  543. X            path = get_string(command);
  544. X#ifdef S_IFLNK
  545. X            result = lstat(path, &st);
  546. X#else
  547. X            result = stat(path, &st);
  548. X#endif
  549. X            if (result)
  550. X                put_int(reply, errno);
  551. X            else
  552. X            {
  553. X                put_int(reply, 0);
  554. X                put_binary(reply, &st, sizeof(st));
  555. X            }
  556. X            break;
  557. X
  558. X
  559. X        case command_link:
  560. X            path = get_string(command);
  561. X            path1 = mem_copy_string(path);
  562. X            path = get_string(command);
  563. X            result = link(path1, path);
  564. X            if (result)
  565. X                result = errno;
  566. X            mem_free(path1);
  567. X            put_int(reply, result);
  568. X            break;
  569. X
  570. X        case command_mkdir:
  571. X            path = get_string(command);
  572. X            mode = get_int(command);
  573. X            if (mkdir(path, mode))
  574. X                result = errno;
  575. X            else
  576. X                result = 0;
  577. X            put_int(reply, result);
  578. X            break;
  579. X
  580. X        case command_open:
  581. X            path = get_string(command);
  582. X            mode = get_int(command);
  583. X            perm = get_int(command);
  584. X            result = open(path, mode, perm);
  585. X            put_int(reply, result);
  586. X            if (result < 0)
  587. X                put_int(reply, errno);
  588. X            break;
  589. X
  590. X        case command_rename:
  591. X            path = get_string(command);
  592. X            path1 = mem_copy_string(path);
  593. X            path = get_string(command);
  594. X            result = rename(path1, path);
  595. X            if (result)
  596. X                result = errno;
  597. X            mem_free(path1);
  598. X            put_int(reply, result);
  599. X            break;
  600. X
  601. X        case command_read:
  602. X            fd = get_int(command);
  603. X            nbytes = get_long(command);
  604. X            buf = mem_alloc(nbytes);
  605. X            nbytes2 = read(fd, buf, nbytes);
  606. X            put_long(reply, nbytes2);
  607. X            if (nbytes2 > 0)
  608. X                put_binary(reply, buf, nbytes2);
  609. X            else if (nbytes2 < 0)
  610. X                put_int(reply, errno);
  611. X            mem_free(buf);
  612. X            break;
  613. X
  614. X        case command_readlink:
  615. X            path = get_string(command);
  616. X            max = get_int(command);
  617. X            path1 = mem_alloc(max + 1);
  618. X            result = readlink(path, path1, max);
  619. X            put_int(reply, result);
  620. X            if (result < 0)
  621. X                put_int(reply, errno);
  622. X            else
  623. X                put_binary(reply, path1, result);
  624. X            mem_free(path1);
  625. X            break;
  626. X
  627. X        case command_read_whole_dir:
  628. X            path = get_string(command);
  629. X            result = read_whole_dir(path, &buf, &nbytes);
  630. X            if (result < 0)
  631. X                put_int(reply, errno);
  632. X            else
  633. X            {
  634. X                put_int(reply, 0);
  635. X                put_long(reply, nbytes);
  636. X                put_binary(reply, buf, nbytes);
  637. X            }
  638. X            break;
  639. X
  640. X        case command_rmdir:
  641. X            path = get_string(command);
  642. X            if (rmdir(path))
  643. X                result = errno;
  644. X            else
  645. X                result = 0;
  646. X            put_int(reply, result);
  647. X            break;
  648. X
  649. X        case command_stat:
  650. X            path = get_string(command);
  651. X            result = stat(path, &st);
  652. X            if (result)
  653. X                put_int(reply, errno);
  654. X            else
  655. X            {
  656. X                put_int(reply, 0);
  657. X                put_binary(reply, &st, sizeof(st));
  658. X            }
  659. X            break;
  660. X
  661. X        case command_unlink:
  662. X            path = get_string(command);
  663. X            result = unlink(path);
  664. X            if (result)
  665. X                result = errno;
  666. X            put_int(reply, result);
  667. X            break;
  668. X
  669. X        case command_utime:
  670. X            path = get_string(command);
  671. X            get_binary(command, &utb, sizeof(utb));
  672. X            result = utime(path, &utb);
  673. X            if (result)
  674. X                result = errno;
  675. X            put_int(reply, result);
  676. X            break;
  677. X
  678. X        case command_write:
  679. X            fd = get_int(command);
  680. X            nbytes = get_long(command);
  681. X            buf = mem_alloc(nbytes);
  682. X            get_binary(command, buf, nbytes);
  683. X            nbytes2 = write(fd, buf, nbytes);
  684. X            put_long(reply, nbytes2);
  685. X            if (nbytes2 < 0)
  686. X                put_int(reply, errno);
  687. X            mem_free(buf);
  688. X            break;
  689. X        }
  690. X        fflush(reply);
  691. X        if (ferror(reply))
  692. X            exit(errno);
  693. X    }
  694. X    trace((/*{*/"}\n"));
  695. X}
  696. X
  697. X
  698. Xstatic void get_pipe _((int *, int *));
  699. X
  700. Xstatic void
  701. Xget_pipe(rd, wr)
  702. X    int    *rd;
  703. X    int    *wr;
  704. X{
  705. X    int    fd[2];
  706. X
  707. X    trace(("get_pipe()\n{\n"/*}*/));
  708. X    if (pipe(fd))
  709. X        nfatal("pipe");
  710. X    *rd = fd[0];
  711. X    *wr = fd[1];
  712. X    trace(("read %d\n", fd[0]));
  713. X    trace(("write %d\n", fd[1]));
  714. X    trace((/*{*/"}\n"));
  715. X}
  716. X
  717. X
  718. Xstatic  void proxy_close _((void));
  719. X
  720. Xstatic void
  721. Xproxy_close()
  722. X{
  723. X    proxy_ty    *p;
  724. X    int        j;
  725. X
  726. X    trace(("proxy_close()\n{\n"/*}*/));
  727. X    trace(("pid = %d;\n", getpid()));
  728. X    for (j = 0; j < SIZEOF(proxy_table); ++j)
  729. X    {
  730. X        for (;;)
  731. X        {
  732. X            p = proxy_table[j];
  733. X            if (!p)
  734. X                break;
  735. X            trace(("p->pid %d; uid %d; gid %d\n", p->pid, p->uid, p->gid));
  736. X            proxy_table[j] = p->next;
  737. X            fclose(p->command);
  738. X            fclose(p->reply);
  739. X            mem_free((char *)p);
  740. X        }
  741. X    }
  742. X    trace((/*{*/"}\n"));
  743. X}
  744. X
  745. X
  746. Xstatic void proxy_spawn _((proxy_ty *));
  747. X
  748. Xstatic void
  749. Xproxy_spawn(pp)
  750. X    proxy_ty    *pp;
  751. X{
  752. X    int        command_read_fd;
  753. X    int        command_write_fd;
  754. X    int        reply_read_fd;
  755. X    int        reply_write_fd;
  756. X    int        pid;
  757. X    static int    quitregd;
  758. X
  759. X    trace(("proxy_spawn()\n{\n"/*}*/));
  760. X    if (!quitregd)
  761. X    {
  762. X        quitregd = 1;
  763. X        signal(SIGPIPE, SIG_IGN);
  764. X        trace(("master pid %d;\n", getpid()));
  765. X    }
  766. X
  767. X    get_pipe(&command_read_fd, &command_write_fd);
  768. X    get_pipe(&reply_read_fd, &reply_write_fd);
  769. X    switch (pid = fork())
  770. X    {
  771. X    case -1:
  772. X        nfatal("fork");
  773. X
  774. X    case 0:
  775. X        /*
  776. X         * close the ends of the pipes the proxy will not be using
  777. X         */
  778. X        close(command_write_fd);
  779. X        close(reply_read_fd);
  780. X        error_set_id_func((error_id_ty)0);
  781. X
  782. X        /*
  783. X         * the proxy now assumes the appropriate ID
  784. X         */
  785. X        if (setgid(pp->gid))
  786. X            exit(errno);
  787. X        if (setuid(pp->uid))
  788. X            exit(errno);
  789. X        umask(pp->umask);
  790. X
  791. X        /*
  792. X         * close all of the master ends of all the other proxys
  793. X         * otherwise they will keep each other alive
  794. X         * after the master dies
  795. X         */
  796. X        trace_indent_reset();
  797. X        proxy_close();
  798. X
  799. X        /*
  800. X         * normally the proxys are silent,
  801. X         * returning all errors to the master.
  802. X         * Should one of the error functions be called,
  803. X         * make sure the proxy does not perform the undo.
  804. X         */
  805. X        undo_cancel();
  806. X
  807. X        /*
  808. X         * do whatever is asked
  809. X         */
  810. X        proxy(command_read_fd, reply_write_fd);
  811. X        exit(0);
  812. X
  813. X    default:
  814. X        /*
  815. X         * close the ends of the pipes the master will not be using
  816. X         */
  817. X        close(command_read_fd);
  818. X        close(reply_write_fd);
  819. X
  820. X        /*
  821. X         * remember who the child is
  822. X         * (even though we don't have a use for it at the moment)
  823. X         */
  824. X        pp->pid = pid;
  825. X        trace(("child pid %d\n", getpid()));
  826. X
  827. X        /*
  828. X         * open a buffered stream for commands
  829. X         */
  830. X        errno = 0;
  831. X        pp->command = fdopen(command_write_fd, "w");
  832. X        if (!pp->command)
  833. X        {
  834. X            if (!errno)
  835. X                errno = ENOMEM;
  836. X            nfatal("fdopen");
  837. X        }
  838. X
  839. X        /*
  840. X         * open a buffered stream for replies
  841. X         */
  842. X        errno = 0;
  843. X        pp->reply = fdopen(reply_read_fd, "r");
  844. X        if (!pp->reply)
  845. X        {
  846. X            if (!errno)
  847. X                errno = ENOMEM;
  848. X            nfatal("fdopen");
  849. X        }
  850. X        break;
  851. X    }
  852. X    trace((/*{*/"}\n"));
  853. X}
  854. X
  855. X
  856. Xstatic proxy_ty *proxy_find _((void));
  857. X
  858. Xstatic proxy_ty *
  859. Xproxy_find()
  860. X{
  861. X    int        uid;
  862. X    int        gid;
  863. X    int        um;
  864. X    int        hash;
  865. X    int        pix;
  866. X    proxy_ty    *pp;
  867. X
  868. X    /*
  869. X     * search for an existing proxy
  870. X     */
  871. X    trace(("proxy_find()\n{\n"/*}*/));
  872. X    os_become_must_be_active();
  873. X    os_become_query(&uid, &gid, &um);
  874. X    hash = proxy_hash(uid, gid, um);
  875. X    pix = hash % SIZEOF(proxy_table);
  876. X    for (pp = proxy_table[pix]; pp; pp = pp->next)
  877. X    {
  878. X        if (pp->hash != hash)
  879. X            continue;
  880. X        if (pp->uid != uid || pp->gid != gid || pp->umask != um)
  881. X            continue;
  882. X        goto done;
  883. X    }
  884. X
  885. X    /*
  886. X     * no such proxy, so create a new one
  887. X     */
  888. X    trace(("uid = %d; gid = %d; umask = 0%o;\n", uid, gid, um));
  889. X    pp = (proxy_ty *)mem_alloc(sizeof(proxy_ty));
  890. X    pp->hash = hash;
  891. X    pp->uid = uid;
  892. X    pp->gid = gid;
  893. X    pp->umask = um;
  894. X    pp->pid = -1;
  895. X    pp->command = 0;
  896. X    pp->reply = 0;
  897. X    pp->next = 0;
  898. X    proxy_spawn(pp);
  899. X
  900. X    /*
  901. X     * glue into the table AFTER the spawn,
  902. X     * so the child doesn't close the wrong things.
  903. X     */
  904. X    pp->next = proxy_table[pix];
  905. X    proxy_table[pix] = pp;
  906. X
  907. X    /*
  908. X     * here for all exits
  909. X     */
  910. X    done:
  911. X    trace(("return %08lX;\n", (long)pp));
  912. X    trace((/*{*/"}\n"));
  913. X    return pp;
  914. X}
  915. X
  916. X
  917. Xstatic void end_of_command _((proxy_ty *));
  918. X
  919. Xstatic void
  920. Xend_of_command(pp)
  921. X    proxy_ty    *pp;
  922. X{
  923. X    trace(("end_of_command()\n{\n"/*}*/));
  924. X    if (fflush(pp->command))
  925. X        nfatal("write pipe");
  926. X    trace((/*{*/"}\n"));
  927. X}
  928. X
  929. X
  930. Xint
  931. Xglue_stat(path, st)
  932. X    char        *path;
  933. X    struct stat    *st;
  934. X{
  935. X    proxy_ty    *pp;
  936. X    int        result;
  937. X
  938. X    trace(("glue_stat()\n{\n"/*}*/));
  939. X    pp = proxy_find();
  940. X    putc(command_stat, pp->command);
  941. X    put_string(pp->command, path);
  942. X    end_of_command(pp);
  943. X    result = get_int(pp->reply);
  944. X    if (result)
  945. X    {
  946. X        errno = result;
  947. X        result = -1;
  948. X    }
  949. X    else
  950. X        get_binary(pp->reply, st, sizeof(*st));
  951. X    trace(("return %d; /* errno = %d */\n", result, errno));
  952. X    trace((/*{*/"}\n"));
  953. X    return result;
  954. X}
  955. X
  956. X
  957. Xint
  958. Xglue_lstat(path, st)
  959. X    char        *path;
  960. X    struct stat    *st;
  961. X{
  962. X    proxy_ty    *pp;
  963. X    int        result;
  964. X
  965. X    trace(("glue_lstat()\n{\n"/*}*/));
  966. X    pp = proxy_find();
  967. X    putc(command_lstat, pp->command);
  968. X    put_string(pp->command, path);
  969. X    end_of_command(pp);
  970. X    result = get_int(pp->reply);
  971. X    if (result)
  972. X    {
  973. X        errno = result;
  974. X        result = -1;
  975. X    }
  976. X    else
  977. X        get_binary(pp->reply, st, sizeof(*st));
  978. X    trace(("return %d; /* errno = %d */\n", result, errno));
  979. X    trace((/*{*/"}\n"));
  980. X    return result;
  981. X}
  982. X
  983. X
  984. Xint
  985. Xglue_mkdir(path, mode)
  986. X    char        *path;
  987. X    int        mode;
  988. X{
  989. X    proxy_ty    *pp;
  990. X    int        result;
  991. X
  992. X    trace(("glue_mkdir()\n{\n"/*}*/));
  993. X    pp = proxy_find();
  994. X    putc(command_mkdir, pp->command);
  995. X    put_string(pp->command, path);
  996. X    put_int(pp->command, mode);
  997. X    end_of_command(pp);
  998. X    result = get_int(pp->reply);
  999. X    if (result)
  1000. X    {
  1001. X        errno = result;
  1002. X        result = -1;
  1003. X    }
  1004. X    trace(("return %d; /* errno = %d */\n", result, errno));
  1005. X    trace((/*{*/"}\n"));
  1006. X    return result;
  1007. X}
  1008. X
  1009. X
  1010. Xint
  1011. Xglue_chown(path, uid, gid)
  1012. X    char        *path;
  1013. X    int        uid;
  1014. X    int        gid;
  1015. X{
  1016. X    proxy_ty    *pp;
  1017. X    int        result;
  1018. X
  1019. X    trace(("glue_chown()\n{\n"/*}*/));
  1020. X    pp = proxy_find();
  1021. X    putc(command_chown, pp->command);
  1022. X    put_string(pp->command, path);
  1023. X    put_int(pp->command, uid);
  1024. X    put_int(pp->command, gid);
  1025. X    end_of_command(pp);
  1026. X    result = get_int(pp->reply);
  1027. X    if (result)
  1028. X    {
  1029. X        errno = result;
  1030. X        result = -1;
  1031. X    }
  1032. X    trace(("return %d; /* errno = %d */\n", result, errno));
  1033. X    trace((/*{*/"}\n"));
  1034. X    return result;
  1035. X}
  1036. X
  1037. X
  1038. Xint
  1039. Xglue_catfile(path)
  1040. X    char        *path;
  1041. X{
  1042. X    proxy_ty    *pp;
  1043. X    int        result;
  1044. X
  1045. X    trace(("glue_catfile()\n{\n"/*}*/));
  1046. X    pp = proxy_find();
  1047. X    putc(command_catfile, pp->command);
  1048. X    put_string(pp->command, path);
  1049. X    end_of_command(pp);
  1050. X    result = get_int(pp->reply);
  1051. X    if (result)
  1052. X    {
  1053. X        errno = result;
  1054. X        result = -1;
  1055. X    }
  1056. X    trace(("return %d; /* errno = %d */\n", result, errno));
  1057. X    trace((/*{*/"}\n"));
  1058. X    return result;
  1059. X}
  1060. X
  1061. X
  1062. Xint
  1063. Xglue_chmod(path, mode)
  1064. X    char        *path;
  1065. X    int        mode;
  1066. X{
  1067. X    proxy_ty    *pp;
  1068. X    int        result;
  1069. X
  1070. X    trace(("glue_chmod()\n{\n"/*}*/));
  1071. X    pp = proxy_find();
  1072. X    putc(command_chmod, pp->command);
  1073. X    put_string(pp->command, path);
  1074. X    put_int(pp->command, mode);
  1075. X    end_of_command(pp);
  1076. X    result = get_int(pp->reply);
  1077. X    if (result)
  1078. X    {
  1079. X        errno = result;
  1080. X        result = -1;
  1081. X    }
  1082. X    trace(("return %d; /* errno = %d */\n", result, errno));
  1083. X    trace((/*{*/"}\n"));
  1084. X    return result;
  1085. X}
  1086. X
  1087. X
  1088. Xint
  1089. Xglue_rmdir(path)
  1090. X    char        *path;
  1091. X{
  1092. X    proxy_ty    *pp;
  1093. X    int        result;
  1094. X
  1095. X    trace(("glue_rmdir()\n{\n"/*}*/));
  1096. X    pp = proxy_find();
  1097. X    putc(command_rmdir, pp->command);
  1098. X    put_string(pp->command, path);
  1099. X    end_of_command(pp);
  1100. X    result = get_int(pp->reply);
  1101. X    if (result)
  1102. X    {
  1103. X        errno = result;
  1104. X        result = -1;
  1105. X    }
  1106. X    trace(("return %d; /* errno = %d */\n", result, errno));
  1107. X    trace((/*{*/"}\n"));
  1108. X    return result;
  1109. X}
  1110. X
  1111. X
  1112. Xint
  1113. Xglue_rename(p1, p2)
  1114. X    char        *p1;
  1115. X    char        *p2;
  1116. X{
  1117. X    proxy_ty    *pp;
  1118. X    int        result;
  1119. X
  1120. X    trace(("glue_rename()\n{\n"/*}*/));
  1121. X    pp = proxy_find();
  1122. X    putc(command_rename, pp->command);
  1123. X    put_string(pp->command, p1);
  1124. X    put_string(pp->command, p2);
  1125. X    end_of_command(pp);
  1126. X    result = get_int(pp->reply);
  1127. X    if (result)
  1128. X    {
  1129. X        errno = result;
  1130. X        result = -1;
  1131. X    }
  1132. X    trace(("return %d; /* errno = %d */\n", result, errno));
  1133. X    trace((/*{*/"}\n"));
  1134. X    return result;
  1135. X}
  1136. X
  1137. X
  1138. Xint
  1139. Xglue_unlink(path)
  1140. X    char        *path;
  1141. X{
  1142. X    proxy_ty    *pp;
  1143. X    int        result;
  1144. X
  1145. X    trace(("glue_unlink()\n{\n"/*}*/));
  1146. X    pp = proxy_find();
  1147. X    putc(command_unlink, pp->command);
  1148. X    put_string(pp->command, path);
  1149. X    end_of_command(pp);
  1150. X    result = get_int(pp->reply);
  1151. X    if (result)
  1152. X    {
  1153. X        errno = result;
  1154. X        result = -1;
  1155. X    }
  1156. X    trace(("return %d; /* errno = %d */\n", result, errno));
  1157. X    trace((/*{*/"}\n"));
  1158. X    return result;
  1159. X}
  1160. X
  1161. X
  1162. Xint
  1163. Xglue_link(p1, p2)
  1164. X    char        *p1;
  1165. X    char        *p2;
  1166. X{
  1167. X    proxy_ty    *pp;
  1168. X    int        result;
  1169. X
  1170. X    trace(("glue_link()\n{\n"/*}*/));
  1171. X    pp = proxy_find();
  1172. X    putc(command_link, pp->command);
  1173. X    put_string(pp->command, p1);
  1174. X    put_string(pp->command, p2);
  1175. X    end_of_command(pp);
  1176. X    result = get_int(pp->reply);
  1177. X    if (result)
  1178. X    {
  1179. X        errno = result;
  1180. X        result = -1;
  1181. X    }
  1182. X    trace(("return %d; /* errno = %d */\n", result, errno));
  1183. X    trace((/*{*/"}\n"));
  1184. X    return result;
  1185. X}
  1186. X
  1187. X
  1188. Xint
  1189. Xglue_access(path, mode)
  1190. X    char        *path;
  1191. X    int        mode;
  1192. X{
  1193. X    proxy_ty    *pp;
  1194. X    int        result;
  1195. X
  1196. X    trace(("glue_access()\n{\n"/*}*/));
  1197. X    pp = proxy_find();
  1198. X    putc(command_access, pp->command);
  1199. X    put_string(pp->command, path);
  1200. X    put_int(pp->command, mode);
  1201. X    end_of_command(pp);
  1202. X    result = get_int(pp->reply);
  1203. X    if (result)
  1204. X    {
  1205. X        errno = result;
  1206. X        result = -1;
  1207. X    }
  1208. X    trace(("return %d; /* errno = %d */\n", result, errno));
  1209. X    trace((/*{*/"}\n"));
  1210. X    return result;
  1211. X}
  1212. X
  1213. X
  1214. Xchar *
  1215. Xglue_getcwd(buf, max)
  1216. X    char        *buf;
  1217. X    int        max;
  1218. X{
  1219. X    proxy_ty    *pp;
  1220. X    char        *s;
  1221. X    int        result;
  1222. X
  1223. X    /*
  1224. X     * don't bother with the case where buf
  1225. X     * is the NULL pointer, aegis never uses it.
  1226. X     */
  1227. X    trace(("glue_getcwd()\n{\n"/*}*/));
  1228. X    assert(buf);
  1229. X    pp = proxy_find();
  1230. X    putc(command_getcwd, pp->command);
  1231. X    put_int(pp->command, max);
  1232. X    end_of_command(pp);
  1233. X    result = get_int(pp->reply);
  1234. X    if (result)
  1235. X    {
  1236. X        s = 0;
  1237. X        trace(("return NULL; /* errno = %d */\n", result));
  1238. X        errno = result;
  1239. X    }
  1240. X    else
  1241. X    {
  1242. X        s = get_string(pp->reply);
  1243. X        strcpy(buf, s);
  1244. X        s = buf;
  1245. X        trace(("return \"%s\";\n", s));
  1246. X    }
  1247. X    trace((/*{*/"}\n"));
  1248. X    return s;
  1249. X}
  1250. X
  1251. X
  1252. Xint
  1253. Xglue_readlink(path, buf, max)
  1254. X    char        *path;
  1255. X    char        *buf;
  1256. X    int        max;
  1257. X{
  1258. X    proxy_ty    *pp;
  1259. X    int        result;
  1260. X
  1261. X    trace(("glue_readlink()\n{\n"/*}*/));
  1262. X    pp = proxy_find();
  1263. X    putc(command_readlink, pp->command);
  1264. X    put_string(pp->command, path);
  1265. X    put_int(pp->command, max);
  1266. X    end_of_command(pp);
  1267. X    result = get_int(pp->reply);
  1268. X    if (result < 0)
  1269. X    {
  1270. X        errno = get_int(pp->reply);
  1271. X        result = -1;
  1272. X    }
  1273. X    else
  1274. X    {
  1275. X        get_binary(pp->reply, buf, result);
  1276. X        trace(("buf = \"%.*s\";\n", result, buf));
  1277. X    }
  1278. X    trace(("return %d; /* errno = %d */\n", result, errno));
  1279. X    trace((/*{*/"}\n"));
  1280. X    return result;
  1281. X}
  1282. X
  1283. X
  1284. Xint
  1285. Xglue_utime(path, values)
  1286. X    char        *path;
  1287. X    struct utimbuf    *values;
  1288. X{
  1289. X    proxy_ty    *pp;
  1290. X    int        result;
  1291. X
  1292. X    trace(("glue_utime()\n{\n"/*}*/));
  1293. X    pp = proxy_find();
  1294. X    putc(command_utime, pp->command);
  1295. X    put_string(pp->command, path);
  1296. X    put_binary(pp->command, values, sizeof(*values));
  1297. X    end_of_command(pp);
  1298. X    result = get_int(pp->reply);
  1299. X    if (result)
  1300. X    {
  1301. X        errno = result;
  1302. X        result = -1;
  1303. X    }
  1304. X    trace(("return %d; /* errno = %d */\n", result, errno));
  1305. X    trace((/*{*/"}\n"));
  1306. X    return result;
  1307. X}
  1308. X
  1309. X
  1310. Xint
  1311. Xglue_copyfile(p1, p2)
  1312. X    char        *p1;
  1313. X    char        *p2;
  1314. X{
  1315. X    proxy_ty    *pp;
  1316. X    int        result;
  1317. X
  1318. X    trace(("glue_copyfile()\n{\n"/*}*/));
  1319. X    pp = proxy_find();
  1320. X    putc(command_copyfile, pp->command);
  1321. X    put_string(pp->command, p1);
  1322. X    put_string(pp->command, p2);
  1323. X    end_of_command(pp);
  1324. X    result = get_int(pp->reply);
  1325. X    if (result)
  1326. X    {
  1327. X        errno = result;
  1328. X        result = -1;
  1329. X    }
  1330. X    trace(("return %d; /* errno = %d */\n", result, errno));
  1331. X    trace((/*{*/"}\n"));
  1332. X    return result;
  1333. X}
  1334. X
  1335. X
  1336. Xtypedef struct glue_file_ty glue_file_ty;
  1337. Xstruct glue_file_ty
  1338. X{
  1339. X    long        guard1;
  1340. X    char        *path;
  1341. X    int        fmode;
  1342. X    char        *buffer_end;
  1343. X    char        *buffer_pos;
  1344. X    int        fd;
  1345. X    int        errno;
  1346. X    int        pushback;
  1347. X    proxy_ty    *pp;
  1348. X    char        buffer[1 << 11]; /* fits within knl pipe buf */
  1349. X    long        guard2;
  1350. X};
  1351. X
  1352. X
  1353. XFILE *
  1354. Xglue_fopen(path, mode)
  1355. X    char        *path;
  1356. X    char        *mode;
  1357. X{
  1358. X    glue_file_ty    *gfp;
  1359. X    proxy_ty    *pp;
  1360. X    int        fmode;
  1361. X    int        fd;
  1362. X
  1363. X    trace(("glue_fopen()\n{\n"/*}*/));
  1364. X    if (!strcmp(mode, "r"))
  1365. X        fmode = O_RDONLY;
  1366. X    else if (!strcmp(mode, "w"))
  1367. X        fmode = O_WRONLY | O_CREAT | O_TRUNC;
  1368. X    else
  1369. X    {
  1370. X        errno = EINVAL;
  1371. X        gfp = 0;
  1372. X        goto done;
  1373. X    }
  1374. X
  1375. X    pp = proxy_find();
  1376. X    putc(command_open, pp->command);
  1377. X    put_string(pp->command, path);
  1378. X    put_int(pp->command, fmode);
  1379. X    put_int(pp->command, 0666);
  1380. X    end_of_command(pp);
  1381. X    fd = get_int(pp->reply);
  1382. X    if (fd < 0)
  1383. X    {
  1384. X        errno = get_int(pp->reply);
  1385. X        gfp = 0;
  1386. X        goto done;
  1387. X    }
  1388. X
  1389. X    /*
  1390. X     * build our file structure
  1391. X     */
  1392. X    gfp = (glue_file_ty *)mem_alloc(sizeof(glue_file_ty));
  1393. X    gfp->pp = pp;
  1394. X    gfp->path = mem_copy_string(path);
  1395. X    gfp->fmode = fmode;
  1396. X    gfp->fd = fd;
  1397. X    gfp->guard1 = GUARD1;
  1398. X    gfp->guard2 = GUARD2;
  1399. X    if (gfp->fmode == O_RDONLY)
  1400. X    {
  1401. X        gfp->buffer_end = 0;
  1402. X        gfp->buffer_pos = 0;
  1403. X    }
  1404. X    else
  1405. X    {
  1406. X        gfp->buffer_pos = gfp->buffer;
  1407. X        gfp->buffer_end = gfp->buffer + sizeof(gfp->buffer);
  1408. X    }
  1409. X    gfp->errno = 0;
  1410. X    gfp->pushback = EOF;
  1411. X
  1412. X    /*
  1413. X     * NOTE: this is veeerrry nasty.
  1414. X     * The return value is not really a valid FILE,
  1415. X     * but is only useful to the glue file functions.
  1416. X     */
  1417. X    done:
  1418. X    trace(("return %08lX; /* errno = %d */\n", (long)gfp, errno));
  1419. X    trace((/*{*/"}\n"));
  1420. X    return (FILE *)gfp;
  1421. X}
  1422. X
  1423. X
  1424. Xint
  1425. Xglue_fclose(fp)
  1426. X    FILE        *fp;
  1427. X{
  1428. X    proxy_ty    *pp;
  1429. X    glue_file_ty    *gfp;
  1430. X    int        result;
  1431. X    int        result2;
  1432. X
  1433. X    /*
  1434. X     * Leave the standard file streams alone.
  1435. X     * There is a chance these will be seen here.
  1436. X     */
  1437. X    if (fp == stdout || fp == stdin || fp == stderr)
  1438. X        return fclose(fp);
  1439. X
  1440. X    /*
  1441. X     * flush the buffers
  1442. X     */
  1443. X    trace(("glue_fclose()\n{\n"/*}*/));
  1444. X    result = glue_fflush(fp) ? errno : 0;
  1445. X
  1446. X    /*
  1447. X     * locate the appropriate proxy
  1448. X     */
  1449. X    gfp = (glue_file_ty *)fp;
  1450. X    assert(gfp->guard1 == GUARD1);
  1451. X    assert(gfp->guard2 == GUARD2);
  1452. X    pp = gfp->pp;
  1453. X
  1454. X    /*
  1455. X     * tell the proxy to close
  1456. X     */
  1457. X    putc(command_close, pp->command);
  1458. X    put_int(pp->command, gfp->fd);
  1459. X    end_of_command(pp);
  1460. X    result2 = get_int(pp->reply);
  1461. X    if (!result)
  1462. X        result = result2;
  1463. X
  1464. X    /*
  1465. X     * Fclose always closes the file,
  1466. X     * even when the implicit write fails.
  1467. X     * Always dispose of our data.
  1468. X     */
  1469. X    mem_free(gfp->path);
  1470. X    mem_free((char *)gfp);
  1471. X    
  1472. X    /*
  1473. X     * set errno and get out of here
  1474. X     */
  1475. X    if (result)
  1476. X    {
  1477. X        errno = result;
  1478. X        result = -1;
  1479. X    }
  1480. X    trace(("return %d; /* errno = %d */\n", result, errno));
  1481. X    trace((/*{*/"}\n"));
  1482. X    return result;
  1483. X}
  1484. X
  1485. X
  1486. Xint
  1487. Xglue_fgetc(fp)
  1488. X    FILE        *fp;
  1489. X{
  1490. X    proxy_ty    *pp;
  1491. X    glue_file_ty    *gfp;
  1492. X    int        result;
  1493. X    long        nbytes;
  1494. X
  1495. X    /*
  1496. X     * Leave the standard file streams alone.
  1497. X     * There is a chance these will be seen here.
  1498. X     */
  1499. X    if (fp == stdout || fp == stdin || fp == stderr)
  1500. X        return getc(fp);
  1501. X
  1502. X    /*
  1503. X     * locate the appropriate proxy
  1504. X     */
  1505. X#if 0
  1506. X    trace(("glue_fgetc()\n{\n"/*}*/));
  1507. X#endif
  1508. X    gfp = (glue_file_ty *)fp;
  1509. X    assert(gfp->guard1 == GUARD1);
  1510. X    assert(gfp->guard2 == GUARD2);
  1511. X    pp = gfp->pp;
  1512. X
  1513. X    /*
  1514. X     * complain if we are in an error state,
  1515. X     * or they asked for something stupid
  1516. X     */
  1517. X    if (gfp->errno)
  1518. X    {
  1519. X        errno = gfp->errno;
  1520. X        result = EOF;
  1521. X        goto done;
  1522. X    }
  1523. X    if (gfp->fmode != O_RDONLY)
  1524. X    {
  1525. X        gfp->errno = EINVAL;
  1526. X        errno = EINVAL;
  1527. X        result = EOF;
  1528. X        goto done;
  1529. X    }
  1530. X
  1531. X    /*
  1532. X     * use pushback if there is anything in it
  1533. X     */
  1534. X    if (gfp->pushback != EOF)
  1535. X    {
  1536. X        result = gfp->pushback;
  1537. X        gfp->pushback = EOF;
  1538. X        goto done;
  1539. X    }
  1540. X
  1541. X    /*
  1542. X     * use the buffer if there is anything in it
  1543. X     */
  1544. X    if (gfp->buffer_pos && gfp->buffer_pos < gfp->buffer_end)
  1545. X    {
  1546. X        result = (unsigned char)*gfp->buffer_pos++;
  1547. X        goto done;
  1548. X    }
  1549. X    gfp->buffer_pos = 0;
  1550. X    gfp->buffer_end = 0;
  1551. X
  1552. X    /*
  1553. X     * tell the proxy to read another buffer-full
  1554. X     */
  1555. X    pp = gfp->pp;
  1556. X    putc(command_read, pp->command);
  1557. X    put_int(pp->command, gfp->fd);
  1558. X    put_long(pp->command, (long)sizeof(gfp->buffer));
  1559. X    end_of_command(pp);
  1560. X    nbytes = get_long(pp->reply);
  1561. X    if (nbytes < 0)
  1562. X    {
  1563. X        gfp->errno = get_int(pp->reply);
  1564. X        errno = gfp->errno;
  1565. X        result = EOF;
  1566. X        goto done;
  1567. X    }
  1568. X    if (nbytes == 0)
  1569. X    {
  1570. X        errno = 0;
  1571. X        result = EOF;
  1572. X        goto done;
  1573. X    }
  1574. X    assert(nbytes <= sizeof(gfp->buffer));
  1575. X    get_binary(pp->reply, gfp->buffer, nbytes);
  1576. X    gfp->buffer_pos = gfp->buffer;
  1577. X    gfp->buffer_end = gfp->buffer + nbytes;
  1578. X    result = (unsigned char)*gfp->buffer_pos++;
  1579. X
  1580. X    /*
  1581. X     * here for all exits
  1582. X     */
  1583. X    done:
  1584. X#if 0
  1585. X    trace(("return %d; /* errno = %d */\n", result, errno));
  1586. X    trace((/*{*/"}\n"));
  1587. X#endif
  1588. X    return result;
  1589. X}
  1590. X
  1591. X
  1592. Xint
  1593. Xglue_ungetc(c, fp)
  1594. X    int        c;
  1595. X    FILE        *fp;
  1596. X{
  1597. X    glue_file_ty    *gfp;
  1598. X    int        result;
  1599. X
  1600. X    /*
  1601. X     * Leave the standard file streams alone.
  1602. X     * There is a chance these will be seen here.
  1603. X     */
  1604. X    if (fp == stdout || fp == stdin || fp == stderr)
  1605. X        return ungetc(c, fp);
  1606. X
  1607. X    /*
  1608. X     * make a pointer to our data
  1609. X     */
  1610. X    trace(("glue_ungetc()\n{\n"/*}*/));
  1611. X    result = EOF;
  1612. X    gfp = (glue_file_ty *)fp;
  1613. X    assert(gfp->guard1 == GUARD1);
  1614. X    assert(gfp->guard2 == GUARD2);
  1615. X
  1616. X    /*
  1617. X     * make sure not too many
  1618. X     */
  1619. X    if (gfp->pushback != EOF || c == EOF)
  1620. X    {
  1621. X        gfp->errno = EINVAL;
  1622. X        errno = EINVAL;
  1623. X        gfp->pushback = EOF;
  1624. X        goto done;
  1625. X    }
  1626. X
  1627. X    /*
  1628. X     * stash the returned char
  1629. X     */
  1630. X    gfp->pushback = (unsigned char)c;
  1631. X    result = (unsigned char)c;
  1632. X
  1633. X    /*
  1634. X     * here for all exits
  1635. X     */
  1636. X    done:
  1637. X    trace(("return %d; /* errno = %d */\n", result, errno));
  1638. X    trace((/*{*/"}\n"));
  1639. X    return result;
  1640. X}
  1641. X
  1642. X
  1643. Xint
  1644. Xglue_fputc(c, fp)
  1645. X    int        c;
  1646. X    FILE        *fp;
  1647. X{
  1648. X    proxy_ty    *pp;
  1649. X    glue_file_ty    *gfp;
  1650. X    int        result;
  1651. X
  1652. X    /*
  1653. X     * Leave the standard file streams alone.
  1654. X     * There is a chance these will be seen here.
  1655. X     */
  1656. X    if (fp == stdout || fp == stdin || fp == stderr)
  1657. X        return putc(c, fp);
  1658. X
  1659. X    /*
  1660. X     * locate the appropriate proxy
  1661. X     */
  1662. X#if 0
  1663. X    trace(("glue_fputc()\n{\n"/*}*/));
  1664. X#endif
  1665. X    result = EOF;
  1666. X    gfp = (glue_file_ty *)fp;
  1667. X    assert(gfp->guard1 == GUARD1);
  1668. X    assert(gfp->guard2 == GUARD2);
  1669. X    pp = gfp->pp;
  1670. X
  1671. X    /*
  1672. X     * if the stream is in an error state,
  1673. X     * or we were asked to do something stupid,
  1674. X     * return the error
  1675. X     */
  1676. X    if (gfp->errno)
  1677. X    {
  1678. X        errno = gfp->errno;
  1679. X        goto done;
  1680. X    }
  1681. X    if (gfp->fmode == O_RDONLY)
  1682. X    {
  1683. X        gfp->errno = EINVAL;
  1684. X        errno = EINVAL;
  1685. X        goto done;
  1686. X    }
  1687. X
  1688. X    /*
  1689. X     * if there is no room in the buffer,
  1690. X     * flush it to the proxy
  1691. X     */
  1692. X    assert(gfp->buffer_pos);
  1693. X    if (gfp->buffer_pos >= gfp->buffer_end)
  1694. X    {
  1695. X        long    nbytes;
  1696. X        long    nbytes2;
  1697. X
  1698. X        putc(command_write, pp->command);
  1699. X        put_int(pp->command, gfp->fd);
  1700. X        nbytes = gfp->buffer_pos - gfp->buffer;
  1701. X        put_long(pp->command, nbytes);
  1702. X        put_binary(pp->command, gfp->buffer, nbytes);
  1703. X        end_of_command(pp);
  1704. X        nbytes2 = get_long(pp->reply);
  1705. X        if (nbytes2 < 0)
  1706. X        {
  1707. X            gfp->errno = get_int(pp->reply);
  1708. X            errno = gfp->errno;
  1709. X            goto done;
  1710. X        }
  1711. X        if (nbytes2 != nbytes)
  1712. X        {
  1713. X            gfp->errno = EIO;
  1714. X            errno = EIO;
  1715. X            goto done;
  1716. X        }
  1717. X        gfp->buffer_pos = gfp->buffer;
  1718. X        gfp->buffer_end = gfp->buffer + sizeof(gfp->buffer);
  1719. X    }
  1720. X
  1721. X    /*
  1722. X     * stash the character
  1723. X     */
  1724. X    *gfp->buffer_pos++ = c;
  1725. X    result = (unsigned char)c;
  1726. X
  1727. X    /*
  1728. X     * here for all exits
  1729. X     */
  1730. X    done:
  1731. X#if 0
  1732. X    trace(("return %d; /* errno = %d */\n", result, errno));
  1733. X    trace((/*{*/"}\n"));
  1734. X#endif
  1735. X    return result;
  1736. X}
  1737. X
  1738. X
  1739. Xint
  1740. Xglue_ferror(fp)
  1741. X    FILE        *fp;
  1742. X{
  1743. X    glue_file_ty    *gfp;
  1744. X
  1745. X    /*
  1746. X     * Leave the standard file streams alone.
  1747. X     * There is a chance these will be seen here.
  1748. X     */
  1749. X    if (fp == stdout || fp == stdin || fp == stderr)
  1750. X        return ferror(fp);
  1751. X
  1752. X    /*
  1753. X     * locate the appropriate proxy
  1754. X     */
  1755. X    gfp = (glue_file_ty *)fp;
  1756. X    assert(gfp->guard1 == GUARD1);
  1757. X    assert(gfp->guard2 == GUARD2);
  1758. X
  1759. X    /*
  1760. X     * set errno depending on
  1761. X     * the error for this stream.
  1762. X     */
  1763. X    if (gfp->errno)
  1764. X    {
  1765. X        errno = gfp->errno;
  1766. X        gfp->errno = 0;
  1767. X        return 1;
  1768. X    }
  1769. X    return 0;
  1770. X}
  1771. X
  1772. X
  1773. Xint
  1774. Xglue_fflush(fp)
  1775. X    FILE        *fp;
  1776. X{
  1777. X    glue_file_ty    *gfp;
  1778. X    proxy_ty    *pp;
  1779. X    int        result;
  1780. X
  1781. X    /*
  1782. X     * Leave the standard file streams alone.
  1783. X     * There is a chance these will be seen here.
  1784. X     */
  1785. X    if (fp == stdout || fp == stdin || fp == stderr)
  1786. X        return fflush(fp);
  1787. X
  1788. X    /*
  1789. X     * locate the appropriate proxy
  1790. X     */
  1791. X    trace(("glue_fflush()\n{\n"/*}*/));
  1792. X    result = EOF;
  1793. X    gfp = (glue_file_ty *)fp;
  1794. X    assert(gfp->guard1 == GUARD1);
  1795. X    assert(gfp->guard2 == GUARD2);
  1796. X    pp = gfp->pp;
  1797. X
  1798. X    /*
  1799. X     * if the stream is in an error state,
  1800. X     * don't do anything
  1801. X     */
  1802. X    if (gfp->errno)
  1803. X    {
  1804. X        errno = gfp->errno;
  1805. X        goto done;
  1806. X    }
  1807. X    if (gfp->fmode == O_RDONLY)
  1808. X    {
  1809. X        gfp->errno = EINVAL;
  1810. X        errno = EINVAL;
  1811. X        goto done;
  1812. X    }
  1813. X
  1814. X    /*
  1815. X     * if there is anything in the buffer,
  1816. X     * send it to the proxy
  1817. X     */
  1818. X    if (gfp->buffer_pos && gfp->buffer_pos > gfp->buffer)
  1819. X    {
  1820. X        long    nbytes;
  1821. X        long    nbytes2;
  1822. X
  1823. X        putc(command_write, pp->command);
  1824. X        put_int(pp->command, gfp->fd);
  1825. X        nbytes = gfp->buffer_pos - gfp->buffer;
  1826. X        put_long(pp->command, nbytes);
  1827. X        put_binary(pp->command, gfp->buffer, nbytes);
  1828. X        end_of_command(pp);
  1829. X        nbytes2 = get_long(pp->reply);
  1830. X        if (nbytes2 < 0)
  1831. X        {
  1832. X            gfp->errno = get_int(pp->reply);
  1833. X            errno = gfp->errno;
  1834. X            goto done;
  1835. X        }
  1836. X        if (nbytes2 != nbytes)
  1837. X        {
  1838. X            gfp->errno = EIO;
  1839. X            errno = EIO;
  1840. X            goto done;
  1841. X        }
  1842. X        gfp->buffer_pos = gfp->buffer;
  1843. X        gfp->buffer_end = gfp->buffer + sizeof(gfp->buffer);
  1844. X    }
  1845. X    result = 0;
  1846. X
  1847. X    /*
  1848. X     * here for all exits
  1849. X     */
  1850. X    done:
  1851. X    trace(("return %d; /* errno = %d */\n", result, errno));
  1852. X    trace((/*{*/"}\n"));
  1853. X    return result;
  1854. X}
  1855. X
  1856. X
  1857. Xint
  1858. Xglue_open(path, mode, perm)
  1859. X    char        *path;
  1860. X    int        mode;
  1861. X    int        perm;
  1862. X{
  1863. X    proxy_ty    *pp;
  1864. X    int        result;
  1865. X
  1866. X    trace(("glue_open()\n{\n"/*}*/));
  1867. X    pp = proxy_find();
  1868. X    putc(command_open, pp->command);
  1869. X    put_string(pp->command, path);
  1870. X    put_int(pp->command, mode);
  1871. X    put_int(pp->command, perm);
  1872. X    end_of_command(pp);
  1873. X    result = get_int(pp->reply);
  1874. X    if (result < 0)
  1875. X        errno = get_int(pp->reply);
  1876. X    trace(("return %d; /* errno = %d */\n", result, errno));
  1877. X    trace((/*{*/"}\n"));
  1878. X    return result;
  1879. X}
  1880. X
  1881. X
  1882. Xint
  1883. Xglue_creat(path, mode)
  1884. X    char        *path;
  1885. X    int        mode;
  1886. X{
  1887. X    proxy_ty    *pp;
  1888. X    int        result;
  1889. X
  1890. X    trace(("glue_creat()\n{\n"/*}*/));
  1891. X    pp = proxy_find();
  1892. X    putc(command_creat, pp->command);
  1893. X    put_string(pp->command, path);
  1894. X    put_int(pp->command, mode);
  1895. X    end_of_command(pp);
  1896. X    result = get_int(pp->reply);
  1897. X    if (result < 0)
  1898. X        errno = get_int(pp->reply);
  1899. X    trace(("return %d; /* errno = %d */\n", result, errno));
  1900. X    trace((/*{*/"}\n"));
  1901. X    return result;
  1902. X}
  1903. X
  1904. X
  1905. Xint
  1906. Xglue_close(fd)
  1907. X    int        fd;
  1908. X{
  1909. X    proxy_ty    *pp;
  1910. X    int        result;
  1911. X
  1912. X    trace(("glue_close()\n{\n"/*}*/));
  1913. X    pp = proxy_find();
  1914. X    putc(command_close, pp->command);
  1915. X    put_int(pp->command, fd);
  1916. X    end_of_command(pp);
  1917. X    result = get_int(pp->reply);
  1918. X    if (result)
  1919. X    {
  1920. X        errno = result;
  1921. X        result = -1;
  1922. X    }
  1923. X    trace(("return %d; /* errno = %d */\n", result, errno));
  1924. X    trace((/*{*/"}\n"));
  1925. X    return result;
  1926. X}
  1927. X
  1928. X
  1929. Xint
  1930. Xglue_write(fd, buf, len)
  1931. X    int        fd;
  1932. X    char        *buf;
  1933. X    long        len;
  1934. X{
  1935. X    proxy_ty    *pp;
  1936. X    int        result;
  1937. X
  1938. X    trace(("glue_write()\n{\n"/*}*/));
  1939. X    pp = proxy_find();
  1940. X    putc(command_write, pp->command);
  1941. X    put_int(pp->command, fd);
  1942. X    put_long(pp->command, len);
  1943. X    put_binary(pp->command, buf, len);
  1944. X    end_of_command(pp);
  1945. X    result = get_int(pp->reply);
  1946. X    if (result)
  1947. X    {
  1948. X        errno = result;
  1949. X        result = -1;
  1950. X    }
  1951. X    trace(("return %d; /* errno = %d */\n", result, errno));
  1952. X    trace((/*{*/"}\n"));
  1953. X    return result;
  1954. X}
  1955. X
  1956. X
  1957. Xint
  1958. Xglue_fcntl(fd, cmd, data)
  1959. X    int        fd;
  1960. X    int        cmd;
  1961. X    struct flock    *data;
  1962. X{
  1963. X    proxy_ty    *pp;
  1964. X    int        result;
  1965. X
  1966. X    trace(("glue_fcntl()\n{\n"/*}*/));
  1967. X    assert(cmd == F_SETLKW || cmd == F_SETLK || cmd == F_UNLCK ||
  1968. X        cmd == F_GETLK);
  1969. X    pp = proxy_find();
  1970. X    putc(command_fcntl, pp->command);
  1971. X    put_int(pp->command, fd);
  1972. X    put_int(pp->command, cmd);
  1973. X    put_binary(pp->command, data, sizeof(*data));
  1974. X    end_of_command(pp);
  1975. X    result = get_int(pp->reply);
  1976. X    get_binary(pp->reply, data, sizeof(*data));
  1977. X    if (result)
  1978. X    {
  1979. X        errno = result;
  1980. X        result = -1;
  1981. X    }
  1982. X    trace(("return %d; /* errno = %d */\n", result, errno));
  1983. X    trace((/*{*/"}\n"));
  1984. X    return result;
  1985. X}
  1986. X
  1987. X
  1988. Xint
  1989. Xglue_read_whole_dir(path, data_p, data_len_p)
  1990. X    char        *path;
  1991. X    char        **data_p;
  1992. X    long        *data_len_p;
  1993. X{
  1994. X    static char    *data;
  1995. X    static long    data_max;
  1996. X    long        data_len;
  1997. X    proxy_ty    *pp;
  1998. X    int        result;
  1999. X
  2000. X    trace(("glue_read_whole_dir(path = \"%s\")\n{\n"/*}*/, path));
  2001. X    pp = proxy_find();
  2002. X    putc(command_read_whole_dir, pp->command);
  2003. X    put_string(pp->command, path);
  2004. X    end_of_command(pp);
  2005. X    result = get_int(pp->reply);
  2006. X    if (result)
  2007. X    {
  2008. X        errno = result;
  2009. X        result = -1;
  2010. X    }
  2011. X    else
  2012. X    {
  2013. X        data_len = get_long(pp->reply);
  2014. X        if (data_len > data_max)
  2015. X        {
  2016. X            data_max = data_len;
  2017. X            if (!data)
  2018. X                data = mem_alloc(data_max);
  2019. X            else
  2020. X                mem_change_size(&data, data_max);
  2021. X        }
  2022. X        get_binary(pp->reply, data, data_len);
  2023. X        *data_len_p = data_len;
  2024. X        *data_p = data;
  2025. X    }
  2026. X    trace(("return %d; /* errno = %d */\n", result, errno));
  2027. X    trace((/*{*/"}\n"));
  2028. X    return result;
  2029. X}
  2030. X
  2031. X
  2032. X#endif /* CONF_NO_seteuid */
  2033. X
  2034. X
  2035. X/*
  2036. X * NAME
  2037. X *    copyfile - copy a file
  2038. X *
  2039. X * SYNOPSIS
  2040. X *    int copyfile(char *src, char *dst);
  2041. X *
  2042. X * DESCRIPTION
  2043. X *    The copyfile function complements the link and rename functions.
  2044. X *
  2045. X * ARGUMENTS
  2046. X *    src    - pathname of source file
  2047. X *    dst    - pathname of destination file
  2048. X *
  2049. X * RETURNS
  2050. X *    0    on success
  2051. X *    -1    on error, setting errno appropriately
  2052. X */
  2053. X
  2054. Xint
  2055. Xcopyfile(src, dst)
  2056. X    char    *src;
  2057. X    char    *dst;
  2058. X{
  2059. X    int    src_fd;
  2060. X    int    dst_fd;
  2061. X    char    *buffer;
  2062. X    long    max;
  2063. X    long    nbytes;
  2064. X    long    nbytes2;
  2065. X    int    err;
  2066. X    int    result;
  2067. X
  2068. X    trace(("copyfile(\"%s\", \"%s\")\n{\n"/*}*/, src, dst));
  2069. X    result = -1;
  2070. X    src_fd = open(src, O_RDONLY, 0666);
  2071. X    if (src_fd < 0)
  2072. X        goto done;
  2073. X    dst_fd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0666);
  2074. X    if (dst_fd < 0)
  2075. X    {
  2076. X        err = errno;
  2077. X        close(src_fd);
  2078. X        errno = err;
  2079. X        goto done;
  2080. X    }
  2081. X
  2082. X    max = 1L << 13;
  2083. X    errno = 0;
  2084. X    buffer = malloc(max);
  2085. X    if (!buffer)
  2086. X    {
  2087. X        err = errno ? errno : ENOMEM;
  2088. X        close(dst_fd);
  2089. X        close(src_fd);
  2090. X        errno = err;
  2091. X        goto done;
  2092. X    }
  2093. X
  2094. X    for (;;)
  2095. X    {
  2096. X        nbytes = read(src_fd, buffer, max);
  2097. X        if (nbytes < 0)
  2098. X        {
  2099. X            err = errno;
  2100. X            close(src_fd);
  2101. X            close(dst_fd);
  2102. X            free(buffer);
  2103. X            errno = err;
  2104. X            goto done;
  2105. X        }
  2106. X        if (nbytes == 0)
  2107. X            break;
  2108. X        
  2109. X        nbytes2 = write(dst_fd, buffer, nbytes);
  2110. X        if (nbytes2 < 0)
  2111. X        {
  2112. X            err = errno;
  2113. X            close(src_fd);
  2114. X            close(dst_fd);
  2115. X            free(buffer);
  2116. X            errno = err;
  2117. X            goto done;
  2118. X        }
  2119. X        if (nbytes2 != nbytes)
  2120. X        {
  2121. X            close(src_fd);
  2122. X            close(dst_fd);
  2123. X            free(buffer);
  2124. X            errno = EIO; /* weird device, probably */
  2125. X            goto done;
  2126. X        }
  2127. X    }
  2128. X    free(buffer);
  2129. X    if (close(src_fd))
  2130. X    {
  2131. X        err = errno;
  2132. X        close(dst_fd);
  2133. X        errno = err;
  2134. X        goto done;
  2135. X    }
  2136. X    result = close(dst_fd);
  2137. X
  2138. X    /*
  2139. X     * here for all exits
  2140. X     */
  2141. X    done:
  2142. X    trace(("return %d; /* errno = %d */\n", result, errno));
  2143. X    trace((/*{*/"}\n"));
  2144. X    return result;
  2145. X}
  2146. X
  2147. X
  2148. X/*
  2149. X * NAME
  2150. X *    catfile - copy a file
  2151. X *
  2152. X * SYNOPSIS
  2153. X *    int catfile(char *path);
  2154. X *
  2155. X * DESCRIPTION
  2156. X *    The catfile function is used to print the contents of
  2157. X *    a file on the standard output.
  2158. X *
  2159. X * ARGUMENTS
  2160. X *    path    - pathname of source file
  2161. X *
  2162. X * RETURNS
  2163. X *    0    on success
  2164. X *    -1    on error, setting errno appropriately
  2165. X */
  2166. X
  2167. Xint
  2168. Xcatfile(path)
  2169. X    char    *path;
  2170. X{
  2171. X    int    fd;
  2172. X    char    *buffer;
  2173. X    long    max;
  2174. X    long    nbytes;
  2175. X    long    nbytes2;
  2176. X    int    err;
  2177. X    int    result;
  2178. X
  2179. X    trace(("catfile(\"%s\")\n{\n"/*}*/, path));
  2180. X    result = -1;
  2181. X    fd = open(path, O_RDONLY, 0666);
  2182. X    if (fd < 0)
  2183. X        goto done;
  2184. X
  2185. X    max = 1L << 13;
  2186. X    errno = 0;
  2187. X    buffer = malloc(max);
  2188. X    if (!buffer)
  2189. X    {
  2190. X        err = errno ? errno : ENOMEM;
  2191. X        close(fd);
  2192. X        errno = err;
  2193. X        goto done;
  2194. X    }
  2195. X
  2196. X    for (;;)
  2197. X    {
  2198. X        nbytes = read(fd, buffer, max);
  2199. X        if (nbytes < 0)
  2200. X        {
  2201. X            err = errno;
  2202. X            close(fd);
  2203. X            free(buffer);
  2204. X            errno = err;
  2205. X            goto done;
  2206. X        }
  2207. X        if (nbytes == 0)
  2208. X            break;
  2209. X        
  2210. X        nbytes2 = write(fileno(stdout), buffer, nbytes);
  2211. X        if (nbytes2 < 0)
  2212. X        {
  2213. X            err = errno;
  2214. X            close(fd);
  2215. X            free(buffer);
  2216. X            errno = err;
  2217. X            goto done;
  2218. X        }
  2219. X        if (nbytes2 != nbytes)
  2220. X        {
  2221. X            close(fd);
  2222. X            free(buffer);
  2223. X            errno = EIO; /* weird device, probably */
  2224. X            goto done;
  2225. X        }
  2226. X    }
  2227. X    free(buffer);
  2228. X    result = close(fd);
  2229. X
  2230. X    /*
  2231. X     * here for all exits
  2232. X     */
  2233. X    done:
  2234. X    trace(("return %d; /* errno = %d */\n", result, errno));
  2235. X    trace((/*{*/"}\n"));
  2236. X    return result;
  2237. X}
  2238. X
  2239. X
  2240. Xint
  2241. Xread_whole_dir(path, data_p, data_len_p)
  2242. X    char        *path;
  2243. X    char        **data_p;
  2244. X    long        *data_len_p;
  2245. X{
  2246. X    DIR        *dp;
  2247. X    struct dirent    *de;
  2248. X    static char    *data;
  2249. X    static size_t    data_len;
  2250. X    static size_t    data_max;
  2251. X    char        *np;
  2252. X    size_t        len;
  2253. X
  2254. X    errno = ENOMEM;
  2255. X    dp = opendir(path);
  2256. X    if (!dp)
  2257. X        return -1;
  2258. X    errno = 0;
  2259. X    if (!data)
  2260. X    {
  2261. X        data_max = 1000;
  2262. X        data = mem_alloc(data_max);
  2263. X    }
  2264. X    data_len = 0;
  2265. X    for (;;)
  2266. X    {
  2267. X        de = readdir(dp);
  2268. X        if (!de)
  2269. X            break;
  2270. X        np = de->d_name;
  2271. X#ifdef CONF_pyramid_broken_readdir
  2272. X        np -= 2;
  2273. X#endif
  2274. X        if (np[0] == '.' && (!np[1] || (np[1] == '.' && !np[2])))
  2275. X            continue;
  2276. X        len = strlen(np) + 1;
  2277. X        if (data_len + len > data_max)
  2278. X        {
  2279. X            data_max += 1000;
  2280. X            mem_change_size(&data, data_max);
  2281. X        }
  2282. X        memcpy(data + data_len, np, len);
  2283. X        data_len += len;
  2284. X    }
  2285. X    closedir(dp);
  2286. X    *data_p = data;
  2287. X    *data_len_p = data_len;
  2288. X    return 0;
  2289. X}
  2290. END_OF_FILE
  2291. if test 40198 -ne `wc -c <'aegis/glue.c'`; then
  2292.     echo shar: \"'aegis/glue.c'\" unpacked with wrong size!
  2293. fi
  2294. # end of 'aegis/glue.c'
  2295. fi
  2296. if test -f 'aegis/project.c' -a "${1}" != "-c" ; then 
  2297.   echo shar: Will not clobber existing file \"'aegis/project.c'\"
  2298. else
  2299. echo shar: Extracting \"'aegis/project.c'\" \(27441 characters\)
  2300. sed "s/^X//" >'aegis/project.c' <<'END_OF_FILE'
  2301. X/*
  2302. X *    aegis - project change supervisor
  2303. X *    Copyright (C) 1991, 1992, 1993 Peter Miller.
  2304. X *    All rights reserved.
  2305. X *
  2306. X *    This program is free software; you can redistribute it and/or modify
  2307. X *    it under the terms of the GNU General Public License as published by
  2308. X *    the Free Software Foundation; either version 2 of the License, or
  2309. X *    (at your option) any later version.
  2310. X *
  2311. X *    This program is distributed in the hope that it will be useful,
  2312. X *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  2313. X *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  2314. X *    GNU General Public License for more details.
  2315. X *
  2316. X *    You should have received a copy of the GNU General Public License
  2317. X *    along with this program; if not, write to the Free Software
  2318. X *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  2319. X *
  2320. X * MANIFEST: functions to manipulate project state data
  2321. X */
  2322. X
  2323. X#include <stdio.h>
  2324. X#include <string.h>
  2325. X#include <stdlib.h>
  2326. X
  2327. X#include <change.h>
  2328. X#include <commit.h>
  2329. X#include <error.h>
  2330. X#include <gonzo.h>
  2331. X#include <lock.h>
  2332. X#include <mem.h>
  2333. X#include <option.h>
  2334. X#include <os.h>
  2335. X#include <project.h>
  2336. X#include <pstate.h>
  2337. X#include <s-v-arg.h>
  2338. X#include <trace.h>
  2339. X#include <user.h>
  2340. X#include <undo.h>
  2341. X
  2342. X
  2343. Xstatic void improve _((pstate));
  2344. X
  2345. Xstatic void
  2346. Ximprove(pstate_data)
  2347. X    pstate        pstate_data;
  2348. X{
  2349. X    trace(("improve(pstate_data = %08lX)\n{\n"/*}*/, pstate_data));
  2350. X    if (!pstate_data->administrator)
  2351. X        pstate_data->administrator =
  2352. X            (pstate_administrator_list)
  2353. X            pstate_administrator_list_type.alloc();
  2354. X    if (!pstate_data->developer)
  2355. X        pstate_data->developer =
  2356. X            (pstate_developer_list)
  2357. X            pstate_developer_list_type.alloc();
  2358. X    if (!pstate_data->reviewer)
  2359. X        pstate_data->reviewer =
  2360. X            (pstate_reviewer_list)
  2361. X            pstate_reviewer_list_type.alloc();
  2362. X    if (!pstate_data->integrator)
  2363. X        pstate_data->integrator =
  2364. X            (pstate_integrator_list)
  2365. X            pstate_integrator_list_type.alloc();
  2366. X    if (!pstate_data->change)
  2367. X        pstate_data->change =
  2368. X            (pstate_change_list)
  2369. X            pstate_change_list_type.alloc();
  2370. X    if (!pstate_data->src)
  2371. X        pstate_data->src =
  2372. X            (pstate_src_list)
  2373. X            pstate_src_list_type.alloc();
  2374. X    if (!pstate_data->history)
  2375. X        pstate_data->history =
  2376. X            (pstate_history_list)
  2377. X            pstate_history_list_type.alloc();
  2378. X    if (!(pstate_data->mask & pstate_version_major_mask))
  2379. X        pstate_data->version_major = 1;
  2380. X    if (!(pstate_data->mask & pstate_version_minor_mask))
  2381. X        pstate_data->version_minor = 0;
  2382. X    /*
  2383. X     * owner: always read, always write, always search/exec.
  2384. X     * group: always read, never write, always search/exec.
  2385. X     * other: configurable read, never write, configurable search/exec.
  2386. X     *
  2387. X     * This means that the X's can be configured:
  2388. X     *    000 010 X1X
  2389. X     * and all else is pre-ordained.
  2390. X     */
  2391. X    if (!pstate_data->umask)
  2392. X        pstate_data->umask = DEFAULT_UMASK;
  2393. X    pstate_data->umask = (pstate_data->umask & 5) | 022;
  2394. X    trace((/*{*/"}\n"));
  2395. X}
  2396. X
  2397. X
  2398. Xproject_ty *
  2399. Xproject_alloc(s)
  2400. X    string_ty    *s;
  2401. X{
  2402. X    project_ty    *pp;
  2403. X
  2404. X    trace(("project_alloc(s = \"%s\")\n{\n"/*}*/, s->str_text));
  2405. X    pp = (project_ty *)mem_alloc_clear(sizeof(project_ty));
  2406. X    pp->reference_count = 1;
  2407. X    pp->name = str_copy(s);
  2408. X    trace(("return %08lX;\n", pp));
  2409. X    trace((/*{*/"}\n"));
  2410. X    return pp;
  2411. X}
  2412. X
  2413. X
  2414. Xproject_ty *
  2415. Xproject_copy(pp)
  2416. X    project_ty    *pp;
  2417. X{
  2418. X    trace(("project_copy(pp = %08lX)\n{\n"/*}*/, pp));
  2419. X    assert(pp->reference_count >= 1);
  2420. X    pp->reference_count++;
  2421. X    trace(("return %08lX;\n", pp));
  2422. X    trace((/*{*/"}\n"));
  2423. X    return pp;
  2424. X}
  2425. X
  2426. X
  2427. Xvoid
  2428. Xproject_free(pp)
  2429. X    project_ty    *pp;
  2430. X{
  2431. X    trace(("project_free(pp = %08lX)\n{\n"/*}*/, pp));
  2432. X    assert(pp->reference_count >= 1);
  2433. X    pp->reference_count--;
  2434. X    if (pp->reference_count <= 0)
  2435. X    {
  2436. X        str_free(pp->name);
  2437. X        if (pp->home_path)
  2438. X            str_free(pp->home_path);
  2439. X        if (pp->baseline_path_unresolved)
  2440. X            str_free(pp->baseline_path_unresolved);
  2441. X        if (pp->baseline_path)
  2442. X            str_free(pp->baseline_path);
  2443. X        if (pp->history_path)
  2444. X            str_free(pp->history_path);
  2445. X        if (pp->info_path)
  2446. X            str_free(pp->info_path);
  2447. X        if (pp->pstate_path)
  2448. X            str_free(pp->pstate_path);
  2449. X        if (pp->changes_path)
  2450. X            str_free(pp->changes_path);
  2451. X        if (pp->pstate_data)
  2452. X            pstate_type.free(pp->pstate_data);
  2453. X        mem_free((char *)pp);
  2454. X    }
  2455. X    trace((/*{*/"}\n"));
  2456. X}
  2457. X
  2458. X
  2459. Xstatic void lock_sync _((project_ty *));
  2460. X
  2461. Xstatic void
  2462. Xlock_sync(pp)
  2463. X    project_ty    *pp;
  2464. X{
  2465. X    long        n;
  2466. X
  2467. X    n = lock_magic();
  2468. X    if (pp->lock_magic == n)
  2469. X        return;
  2470. X    pp->lock_magic = n;
  2471. X
  2472. X    if (!pp->pstate_data)
  2473. X        return;
  2474. X    if (pp->is_a_new_file)
  2475. X        return;
  2476. X    pstate_type.free(pp->pstate_data);
  2477. X    pp->pstate_data = 0;
  2478. X}
  2479. X
  2480. X
  2481. Xpstate
  2482. Xproject_pstate_get(pp)
  2483. X    project_ty    *pp;
  2484. X{
  2485. X    int        j, k;
  2486. X
  2487. X    trace(("project_pstate_get(pp = %08lX)\n{\n"/*}*/, pp));
  2488. X    lock_sync(pp);
  2489. X    if (!pp->pstate_data)
  2490. X    {
  2491. X        string_ty    *path;
  2492. X
  2493. X        path = project_pstate_path_get(pp);
  2494. X        pp->is_a_new_file = 0;
  2495. X
  2496. X        /*
  2497. X         * can't become the project, because don't know who
  2498. X         * the project is, yet.
  2499. X         *
  2500. X         * This also means we can use UNIX system security
  2501. X         * to exclude unwelcome access.
  2502. X         */
  2503. X        os_become_orig();
  2504. X        pp->pstate_data = pstate_read_file(path->str_text);
  2505. X        os_become_undo();
  2506. X
  2507. X        if (pp->pstate_data->next_change_number < 1)
  2508. X        {
  2509. X            project_fatal
  2510. X            (
  2511. X                pp,
  2512. X                "%S: corrupted next_change_number field",
  2513. X                pp->pstate_path
  2514. X            );
  2515. X        }
  2516. X        if (pp->pstate_data->next_delta_number < 1)
  2517. X        {
  2518. X            project_fatal
  2519. X            (
  2520. X                pp,
  2521. X                "%S: corrupted next_delta_number field",
  2522. X                pp->pstate_path
  2523. X            );
  2524. X        }
  2525. X        if (!pp->pstate_data->owner_name)
  2526. X        {
  2527. X            project_fatal
  2528. X            (
  2529. X                pp,
  2530. X                "%S: corrupted owner_name field",
  2531. X                pp->pstate_path
  2532. X            );
  2533. X        }
  2534. X        if (!pp->pstate_data->group_name)
  2535. X        {
  2536. X            project_fatal
  2537. X            (
  2538. X                pp,
  2539. X                "%S: corrupted group_name field",
  2540. X                pp->pstate_path
  2541. X            );
  2542. X        }
  2543. X        if (!pp->pstate_data->next_test_number)
  2544. X        {
  2545. X            project_fatal
  2546. X            (
  2547. X                pp,
  2548. X                "%S: corrupted next_test_number field",
  2549. X                pp->pstate_path
  2550. X            );
  2551. X        }
  2552. X        improve(pp->pstate_data);
  2553. X
  2554. X        for (j = 0; j < pp->pstate_data->src->length; ++j)
  2555. X        {
  2556. X            for (k = j + 1; k < pp->pstate_data->src->length; ++k)
  2557. X            {
  2558. X                if
  2559. X                (
  2560. X                    str_equal
  2561. X                    (
  2562. X                      pp->pstate_data->src->list[j]->file_name,
  2563. X                      pp->pstate_data->src->list[k]->file_name
  2564. X                    )
  2565. X                )
  2566. X                {
  2567. X                    project_fatal
  2568. X                    (
  2569. X                        pp,
  2570. X                           "%S: duplicate \"%S\" src entry",
  2571. X                        pp->pstate_path,
  2572. X                       pp->pstate_data->src->list[j]->file_name
  2573. X                    );
  2574. X                }
  2575. X
  2576. X            }
  2577. X        }
  2578. X    }
  2579. X    trace(("return %08lX;\n", pp->pstate_data));
  2580. X    trace((/*{*/"}\n"));
  2581. X    return pp->pstate_data;
  2582. X}
  2583. X
  2584. X
  2585. Xvoid
  2586. Xproject_pstate_lock_prepare(pp)
  2587. X    project_ty    *pp;
  2588. X{
  2589. X    trace(("project_pstate_lock_prepare(pp = %08lX)\n{\n"/*}*/, pp));
  2590. X    lock_prepare_pstate(pp->name);
  2591. X    trace((/*{*/"}\n"));
  2592. X}
  2593. X
  2594. X
  2595. Xvoid
  2596. Xproject_build_read_lock_prepare(pp)
  2597. X    project_ty    *pp;
  2598. X{
  2599. X    trace(("project_build_read_lock_prepare(pp = %08lX)\n{\n"/*}*/, pp));
  2600. X    lock_prepare_build_read(pp->name);
  2601. X    trace((/*{*/"}\n"));
  2602. X}
  2603. X
  2604. X
  2605. Xvoid
  2606. Xproject_build_write_lock_prepare(pp)
  2607. X    project_ty    *pp;
  2608. X{
  2609. X    trace(("project_build_write_lock_prepare(pp = %08lX)\n{\n"/*}*/, pp));
  2610. X    lock_prepare_build_write(pp->name);
  2611. X    trace((/*{*/"}\n"));
  2612. X}
  2613. X
  2614. X
  2615. Xvoid
  2616. Xproject_bind_existing(pp)
  2617. X    project_ty    *pp;
  2618. X{
  2619. X    string_ty    *s;
  2620. X
  2621. X    /*
  2622. X     * make sure project exists
  2623. X     */
  2624. X    trace(("project_bind_existing(pp = %08lX)\n{\n"/*}*/, pp));
  2625. X    assert(!pp->home_path);
  2626. X    s = gonzo_project_home_path_from_name(pp->name);
  2627. X    if (!s)
  2628. X        fatal("project \"%s\" unknown", pp->name->str_text);
  2629. X    /*
  2630. X     * To cope with automounters, directories are stored as given,
  2631. X     * or are derived from the home directory in the passwd file.
  2632. X     * Within aegis, pathnames have their symbolic links resolved,
  2633. X     * and any comparison of paths is done on this "system idea"
  2634. X     * of the pathname.
  2635. X     */
  2636. X    pp->home_path = str_copy(s);
  2637. X    trace((/*{*/"}\n"));
  2638. X}
  2639. X
  2640. X
  2641. Xvoid
  2642. Xproject_bind_new(pp)
  2643. X    project_ty    *pp;
  2644. X{
  2645. X    /*
  2646. X     * make sure name is appropriate length
  2647. X     */
  2648. X    trace(("project_bind_new()\n{\n"/*}*/));
  2649. X    if (pp->name->str_length > PATH_ELEMENT_MAX - 4)
  2650. X    {
  2651. X        fatal
  2652. X        (
  2653. X            "project name \"%s\" too long (by %d)",
  2654. X            pp->name->str_text,
  2655. X            pp->name->str_length - (PATH_ELEMENT_MAX - 4)
  2656. X        );
  2657. X    }
  2658. X
  2659. X    /*
  2660. X     * make sure does not already exist
  2661. X     */
  2662. X    if (gonzo_project_home_path_from_name(pp->name))
  2663. X        fatal("project name \"%s\" already in use", pp->name->str_text);
  2664. X
  2665. X    /*
  2666. X     * allocate data structures
  2667. X     */
  2668. X    assert(!pp->pstate_data);
  2669. X    assert(!pp->pstate_path);
  2670. X    pp->is_a_new_file = 1;
  2671. X    pp->pstate_data = (pstate)pstate_type.alloc();
  2672. X    pp->pstate_data->next_change_number = 1;
  2673. X    pp->pstate_data->next_delta_number = 1;
  2674. X    pp->pstate_data->next_test_number = 1;
  2675. X    pp->pstate_data->version_major = 1;
  2676. X    improve(pp->pstate_data);
  2677. X    trace((/*{*/"}\n"));
  2678. X}
  2679. X
  2680. X
  2681. Xstatic int src_cmp _((const void *, const void *));
  2682. X
  2683. Xstatic int
  2684. Xsrc_cmp(s1p, s2p)
  2685. X    const void    *s1p;
  2686. X    const void    *s2p;
  2687. X{
  2688. X    pstate_src    s1;
  2689. X    pstate_src    s2;
  2690. X
  2691. X    s1 = *(pstate_src *)s1p;
  2692. X    s2 = *(pstate_src *)s2p;
  2693. X    return strcmp(s1->file_name->str_text, s2->file_name->str_text);
  2694. X}
  2695. X
  2696. X
  2697. Xvoid
  2698. Xproject_pstate_write(pp)
  2699. X    project_ty    *pp;
  2700. X{
  2701. X    string_ty    *filename;
  2702. X    string_ty    *filename_new;
  2703. X    string_ty    *filename_old;
  2704. X    static int    count;
  2705. X
  2706. X    trace(("project_pstate_write(pp)\n{\n"/*}*/, pp));
  2707. X    assert(pp->pstate_data);
  2708. X    filename = project_pstate_path_get(pp);
  2709. X
  2710. X    /*
  2711. X     * sort the file names
  2712. X     */
  2713. X    assert(pp->pstate_data->src);
  2714. X    if (pp->pstate_data->src->length >= 2)
  2715. X    {
  2716. X        assert(pp->pstate_data->src->list);
  2717. X        qsort
  2718. X        (
  2719. X            pp->pstate_data->src->list,
  2720. X            pp->pstate_data->src->length,
  2721. X            sizeof(*pp->pstate_data->src->list),
  2722. X            src_cmp
  2723. X        );
  2724. X    }
  2725. X
  2726. X    /*
  2727. X     * write it out
  2728. X     */
  2729. X    filename_new = str_format("%S,%d", filename, ++count);
  2730. X    filename_old = str_format("%S,%d", filename, ++count);
  2731. X    project_become(pp);
  2732. X    if (pp->is_a_new_file)
  2733. X    {
  2734. X        undo_unlink_errok(filename_new);
  2735. X        pstate_write_file(filename_new->str_text, pp->pstate_data);
  2736. X        commit_rename(filename_new, filename);
  2737. X    }
  2738. X    else
  2739. X    {
  2740. X        undo_unlink_errok(filename_new);
  2741. X        pstate_write_file(filename_new->str_text, pp->pstate_data);
  2742. X        commit_rename(filename, filename_old);
  2743. X        commit_rename(filename_new, filename);
  2744. X        commit_unlink_errok(filename_old);
  2745. X    }
  2746. X
  2747. X    /*
  2748. X     * Change so the project owns it.
  2749. X     * (Only needed for new files, but be paranoid.)
  2750. X     */
  2751. X    os_chmod(filename_new, 0644 & ~project_umask(pp));
  2752. X    project_become_undo();
  2753. X    str_free(filename_new);
  2754. X    str_free(filename_old);
  2755. X    trace((/*{*/"}\n"));
  2756. X}
  2757. X
  2758. X
  2759. Xstring_ty *
  2760. Xproject_home_path_get(pp)
  2761. X    project_ty    *pp;
  2762. X{
  2763. X    trace(("project_home_path_get(pp = %08lX)\n{\n"/*}*/, pp));
  2764. X    if (!pp->home_path)
  2765. X    {
  2766. X        string_ty    *s;
  2767. X
  2768. X        /*
  2769. X         * it is an error if the project name is not known
  2770. X         */
  2771. X        s = gonzo_project_home_path_from_name(pp->name);
  2772. X        if (!s)
  2773. X            fatal("project \"%s\" unknown", pp->name->str_text);
  2774. X        /*
  2775. X         * To cope with automounters, directories are stored as given,
  2776. X         * or are derived from the home directory in the passwd file.
  2777. X         * Within aegis, pathnames have their symbolic links resolved,
  2778. X         * and any comparison of paths is done on this "system idea"
  2779. X         * of the pathname.
  2780. X         */
  2781. X        pp->home_path = str_copy(s);
  2782. X    }
  2783. X    trace(("return \"%s\";\n", pp->home_path->str_text));
  2784. X    trace((/*{*/"}\n"));
  2785. X    return pp->home_path;
  2786. X}
  2787. X
  2788. X
  2789. Xvoid
  2790. Xproject_home_path_set(pp, s)
  2791. X    project_ty    *pp;
  2792. X    string_ty    *s;
  2793. X{
  2794. X    /*
  2795. X     * To cope with automounters, directories are stored as given,
  2796. X     * or are derived from the home directory in the passwd file.
  2797. X     * Within aegis, pathnames have their symbolic links resolved,
  2798. X     * and any comparison of paths is done on this "system idea"
  2799. X     * of the pathname.
  2800. X     */
  2801. X    trace(("project_home_path_set(pp = %08lX, s = \"%s\")\n{\n"/*}*/, pp, s->str_text));
  2802. X    if (pp->home_path)
  2803. X        fatal("duplicate -DIRectory option");
  2804. X    pp->home_path = str_copy(s);
  2805. X    trace((/*{*/"}\n"));
  2806. X}
  2807. X
  2808. X
  2809. Xstring_ty *
  2810. Xproject_info_path_get(pp)
  2811. X    project_ty    *pp;
  2812. X{
  2813. X    trace(("project_info_path_get(pp = %08lX)\n{\n"/*}*/, pp));
  2814. X    if (!pp->info_path)
  2815. X    {
  2816. X        pp->info_path =
  2817. X            str_format("%S/info", project_home_path_get(pp));
  2818. X    }
  2819. X    trace(("return \"%s\";\n", pp->info_path->str_text));
  2820. X    trace((/*{*/"}\n"));
  2821. X    return pp->info_path;
  2822. X}
  2823. X
  2824. X
  2825. Xstring_ty *
  2826. Xproject_changes_path_get(pp)
  2827. X    project_ty    *pp;
  2828. X{
  2829. X    trace(("project_changes_path_get(pp = %08lX)\n{\n"/*}*/, pp));
  2830. X    if (!pp->changes_path)
  2831. X    {
  2832. X        pp->changes_path =
  2833. X            str_format("%S/change", project_info_path_get(pp));
  2834. X    }
  2835. X    trace(("return \"%s\";\n", pp->changes_path->str_text));
  2836. X    trace((/*{*/"}\n"));
  2837. X    return pp->changes_path;
  2838. X}
  2839. X
  2840. X
  2841. Xstring_ty *
  2842. Xproject_change_path_get(pp, n)
  2843. X    project_ty    *pp;
  2844. X    long        n;
  2845. X{
  2846. X    string_ty    *s;
  2847. X
  2848. X    trace(("project_change_path_get(pp = %08lX, n = %ld)\n{\n"/*}*/, pp, n));
  2849. X    s = str_format("%S/%d/%3.3d", project_changes_path_get(pp), n / 100, n);
  2850. X    trace(("return \"%s\";\n", s->str_text));
  2851. X    trace((/*{*/"}\n"));
  2852. X    return s;
  2853. X}
  2854. X
  2855. X
  2856. Xstring_ty *
  2857. Xproject_pstate_path_get(pp)
  2858. X    project_ty    *pp;
  2859. X{
  2860. X    trace(("project_pstate_path_get(pp = %08lX)\n{\n"/*}*/, pp));
  2861. X    if (!pp->pstate_path)
  2862. X    {
  2863. X        pp->pstate_path =
  2864. X            str_format("%S/state", project_info_path_get(pp));
  2865. X    }
  2866. X    trace(("return \"%s\";\n", pp->pstate_path->str_text));
  2867. X    trace((/*{*/"}\n"));
  2868. X    return pp->pstate_path;
  2869. X}
  2870. X
  2871. X
  2872. Xstring_ty *
  2873. Xproject_baseline_path_get(pp, resolve)
  2874. X    project_ty    *pp;
  2875. X    int        resolve;
  2876. X{
  2877. X    string_ty    *result;
  2878. X
  2879. X    /*
  2880. X     * To cope with automounters, directories are stored as given,
  2881. X     * or are derived from the home directory in the passwd file.
  2882. X     * Within aegis, pathnames have their symbolic links resolved,
  2883. X     * and any comparison of paths is done on this "system idea"
  2884. X     * of the pathname.
  2885. X     */
  2886. X    trace(("project_baseline_path_get(pp = %08lX)\n{\n"/*}*/, pp));
  2887. X    if (!pp->baseline_path_unresolved)
  2888. X    {
  2889. X        pp->baseline_path_unresolved =
  2890. X            str_format("%S/baseline", project_home_path_get(pp));
  2891. X    }
  2892. X    if (!resolve)
  2893. X        result = pp->baseline_path_unresolved;
  2894. X    else
  2895. X    {
  2896. X        if (!pp->baseline_path)
  2897. X        {
  2898. X            project_become(pp);
  2899. X            pp->baseline_path =
  2900. X                os_pathname(pp->baseline_path_unresolved, 1);
  2901. X            project_become_undo();
  2902. X        }
  2903. X        result = pp->baseline_path;
  2904. X    }
  2905. X    trace(("return \"%s\";\n", result->str_text));
  2906. X    trace((/*{*/"}\n"));
  2907. X    return result;
  2908. X}
  2909. X
  2910. X
  2911. Xstring_ty *
  2912. Xproject_history_path_get(pp)
  2913. X    project_ty    *pp;
  2914. X{
  2915. X    trace(("project_history_path_get(pp = %08lX)\n{\n"/*}*/, pp));
  2916. X    if (!pp->history_path)
  2917. X    {
  2918. X        pp->history_path =
  2919. X            str_format("%S/history", project_home_path_get(pp));
  2920. X    }
  2921. X    trace(("return \"%s\";\n", pp->history_path->str_text));
  2922. X    trace((/*{*/"}\n"));
  2923. X    return pp->history_path;
  2924. X}
  2925. X
  2926. X
  2927. Xstring_ty *
  2928. Xproject_name_get(pp)
  2929. X    project_ty    *pp;
  2930. X{
  2931. X    trace(("project_name_get(pp = %08lX)\n{\n"/*}*/, pp));
  2932. X    trace(("return \"%s\";\n", pp->name->str_text));
  2933. X    trace((/*{*/"}\n"));
  2934. X    return pp->name;
  2935. X}
  2936. X
  2937. X
  2938. Xpstate_src
  2939. Xproject_src_find(pp, file_name)
  2940. X    project_ty    *pp;
  2941. X    string_ty    *file_name;
  2942. X{
  2943. X    pstate        pstate_data;
  2944. X    int        j;
  2945. X    pstate_src    src_data;
  2946. X
  2947. X    trace(("project_src_find(pp = %08lX, file_name = \"%s\")\n{\n"/*}*/, pp, file_name->str_text));
  2948. X    pstate_data = project_pstate_get(pp);
  2949. X    assert(pstate_data->src);
  2950. X    for (j = 0; j < pstate_data->src->length; ++j)
  2951. X    {
  2952. X        src_data = pstate_data->src->list[j];
  2953. X        if (str_equal(src_data->file_name, file_name))
  2954. X            goto ret;
  2955. X    }
  2956. X    src_data = 0;
  2957. X    ret:
  2958. X    trace(("return %08lX;\n", src_data));
  2959. X    trace((/*{*/"}\n"));
  2960. X    return src_data;
  2961. X}
  2962. X
  2963. X
  2964. Xpstate_src
  2965. Xproject_src_new(pp, file_name)
  2966. X    project_ty    *pp;
  2967. X    string_ty    *file_name;
  2968. X{
  2969. X    pstate        pstate_data;
  2970. X    pstate_src    src_data;
  2971. X    pstate_src    *addr;
  2972. X    type_ty        *type_p;
  2973. X
  2974. X    trace(("project_src_new(pp = %08lX, file_name = \"%s\")\n{\n"/*}*/, pp, file_name->str_text));
  2975. X    pstate_data = project_pstate_get(pp);
  2976. X    assert(pstate_data->src);
  2977. X    pstate_src_list_type.list_parse(pstate_data->src, &type_p, (void **)&addr);
  2978. X    src_data = (pstate_src)pstate_src_type.alloc();
  2979. X    *addr = src_data;
  2980. X    src_data->file_name = str_copy(file_name);
  2981. X    trace(("return %08lX;\n", src_data));
  2982. X    trace((/*{*/"}\n"));
  2983. X    return src_data;
  2984. X}
  2985. X
  2986. X
  2987. Xvoid
  2988. Xproject_src_remove(pp, file_name)
  2989. X    project_ty    *pp;
  2990. X    string_ty    *file_name;
  2991. X{
  2992. X    pstate        pstate_data;
  2993. X    pstate_src    src_data;
  2994. X    int        j;
  2995. X
  2996. X    trace(("project_src_remove(pp = %08lX, file_name = \"%s\")\n{\n"/*}*/, pp, file_name->str_text));
  2997. X    pstate_data = project_pstate_get(pp);
  2998. X    assert(pstate_data->src);
  2999. X    for (j = 0; ; ++j)
  3000. X    {
  3001. X        if (j >= pstate_data->src->length)
  3002. X            goto ret;
  3003. X        src_data = pstate_data->src->list[j];
  3004. X        if (str_equal(src_data->file_name, file_name))
  3005. X            break;
  3006. X    }
  3007. X    pstate_data->src->list[j] =
  3008. X        pstate_data->src->list[--pstate_data->src->length];
  3009. X    pstate_src_type.free(src_data);
  3010. X    ret:
  3011. X    trace((/*{*/"}\n"));
  3012. X}
  3013. X
  3014. X
  3015. Xint
  3016. Xproject_administrator_query(pp, user)
  3017. X    project_ty    *pp;
  3018. X    string_ty    *user;
  3019. X{
  3020. X    pstate        pstate_data;
  3021. X    int        j;
  3022. X
  3023. X    pstate_data = project_pstate_get(pp);
  3024. X    assert(pstate_data->administrator);
  3025. X    for (j = 0; j < pstate_data->administrator->length; ++j)
  3026. X    {
  3027. X        if (str_equal(user, pstate_data->administrator->list[j]))
  3028. X            return 1;
  3029. X    }
  3030. X    return 0;
  3031. X}
  3032. X
  3033. X
  3034. Xvoid
  3035. Xproject_administrator_add(pp, name)
  3036. X    project_ty    *pp;
  3037. X    string_ty    *name;
  3038. X{
  3039. X    pstate        pstate_data;
  3040. X    type_ty        *type_p;
  3041. X    string_ty    **who_p;
  3042. X
  3043. X    trace(("project_administrator_add(pp = %08lX, name = \"%s\")\n{\n"/*}*/, pp, name->str_text));
  3044. X    pstate_data = project_pstate_get(pp);
  3045. X    pstate_administrator_list_type.list_parse
  3046. X    (
  3047. X        pstate_data->administrator,
  3048. X        &type_p,
  3049. X        (void **)&who_p
  3050. X    );
  3051. X    *who_p = str_copy(name);
  3052. X    trace((/*{*/"}\n"));
  3053. X}
  3054. X
  3055. X
  3056. Xvoid
  3057. Xproject_administrator_delete(pp, name)
  3058. X    project_ty    *pp;
  3059. X    string_ty    *name;
  3060. X{
  3061. X    size_t        k;
  3062. X    pstate        pstate_data;
  3063. X
  3064. X    pstate_data = project_pstate_get(pp);
  3065. X    for (k = 0; k < pstate_data->administrator->length; ++k)
  3066. X    {
  3067. X        if (str_equal(name, pstate_data->administrator->list[k]))
  3068. X        {
  3069. X            str_free(pstate_data->administrator->list[k]);
  3070. X            pstate_data->administrator->list[k] =
  3071. X                pstate_data->administrator->list
  3072. X                [
  3073. X                    --pstate_data->administrator->length
  3074. X                ];
  3075. X            --k;
  3076. X        }
  3077. X    }
  3078. X}
  3079. X
  3080. X
  3081. Xint
  3082. Xproject_developer_query(pp, user)
  3083. X    project_ty    *pp;
  3084. X    string_ty    *user;
  3085. X{
  3086. X    pstate        pstate_data;
  3087. X    size_t        j;
  3088. X
  3089. X    pstate_data = project_pstate_get(pp);
  3090. X    assert(pstate_data->developer);
  3091. X    for (j = 0; j < pstate_data->developer->length; ++j)
  3092. X    {
  3093. X        if (str_equal(user, pstate_data->developer->list[j]))
  3094. X            return 1;
  3095. X    }
  3096. X    return 0;
  3097. X}
  3098. X
  3099. X
  3100. Xvoid
  3101. Xproject_developer_add(pp, name)
  3102. X    project_ty    *pp;
  3103. X    string_ty    *name;
  3104. X{
  3105. X    pstate        pstate_data;
  3106. X    type_ty        *type_p;
  3107. X    string_ty    **who_p;
  3108. X
  3109. X    pstate_data = project_pstate_get(pp);
  3110. X    pstate_developer_list_type.list_parse
  3111. X    (
  3112. X        pstate_data->developer,
  3113. X        &type_p,
  3114. X        (void **)&who_p
  3115. X    );
  3116. X    *who_p = str_copy(name);
  3117. X}
  3118. X
  3119. X
  3120. Xvoid
  3121. Xproject_developer_delete(pp, name)
  3122. X    project_ty    *pp;
  3123. X    string_ty    *name;
  3124. X{
  3125. X    size_t        k;
  3126. X    pstate        pstate_data;
  3127. X
  3128. X    pstate_data = project_pstate_get(pp);
  3129. X    for (k = 0; k < pstate_data->developer->length; ++k)
  3130. X    {
  3131. X        if (str_equal(name, pstate_data->developer->list[k]))
  3132. X        {
  3133. X            str_free(pstate_data->developer->list[k]);
  3134. X            pstate_data->developer->list[k] =
  3135. X                pstate_data->developer->list
  3136. X                [
  3137. X                    --pstate_data->developer->length
  3138. X                ];
  3139. X            --k;
  3140. X        }
  3141. X    }
  3142. X}
  3143. X
  3144. X
  3145. Xint
  3146. Xproject_integrator_query(pp, user)
  3147. X    project_ty    *pp;
  3148. X    string_ty    *user;
  3149. X{
  3150. X    pstate        pstate_data;
  3151. X    size_t        j;
  3152. X
  3153. X    pstate_data = project_pstate_get(pp);
  3154. X    assert(pstate_data->integrator);
  3155. X    for (j = 0; j < pstate_data->integrator->length; ++j)
  3156. X    {
  3157. X        if (str_equal(user, pstate_data->integrator->list[j]))
  3158. X            return 1;
  3159. X    }
  3160. X    return 0;
  3161. X}
  3162. X
  3163. X
  3164. Xvoid
  3165. Xproject_integrator_add(pp, name)
  3166. X    project_ty    *pp;
  3167. X    string_ty    *name;
  3168. X{
  3169. X    pstate        pstate_data;
  3170. X    type_ty        *type_p;
  3171. X    string_ty    **who_p;
  3172. X
  3173. X    pstate_data = project_pstate_get(pp);
  3174. X    pstate_integrator_list_type.list_parse
  3175. X    (
  3176. X        pstate_data->integrator,
  3177. X        &type_p,
  3178. X        (void **)&who_p
  3179. X    );
  3180. X    *who_p = str_copy(name);
  3181. X}
  3182. X
  3183. X
  3184. Xvoid
  3185. Xproject_integrator_delete(pp, name)
  3186. X    project_ty    *pp;
  3187. X    string_ty    *name;
  3188. X{
  3189. X    size_t        k;
  3190. X    pstate        pstate_data;
  3191. X
  3192. X    pstate_data = project_pstate_get(pp);
  3193. X    for (k = 0; k < pstate_data->integrator->length; ++k)
  3194. X    {
  3195. X        if (str_equal(name, pstate_data->integrator->list[k]))
  3196. X        {
  3197. X            str_free(pstate_data->integrator->list[k]);
  3198. X            pstate_data->integrator->list[k] =
  3199. X                pstate_data->integrator->list
  3200. X                [
  3201. X                    --pstate_data->integrator->length
  3202. X                ];
  3203. X            --k;
  3204. X        }
  3205. X    }
  3206. X}
  3207. X
  3208. X
  3209. Xint
  3210. Xproject_reviewer_query(pp, user)
  3211. X    project_ty    *pp;
  3212. X    string_ty    *user;
  3213. X{
  3214. X    pstate        pstate_data;
  3215. X    size_t        j;
  3216. X
  3217. X    pstate_data = project_pstate_get(pp);
  3218. X    assert(pstate_data->reviewer);
  3219. X    for (j = 0; j < pstate_data->reviewer->length; ++j)
  3220. X    {
  3221. X        if (str_equal(user, pstate_data->reviewer->list[j]))
  3222. X            return 1;
  3223. X    }
  3224. X    return 0;
  3225. X}
  3226. X
  3227. X
  3228. Xvoid
  3229. Xproject_reviewer_add(pp, name)
  3230. X    project_ty    *pp;
  3231. X    string_ty    *name;
  3232. X{
  3233. X    pstate        pstate_data;
  3234. X    type_ty        *type_p;
  3235. X    string_ty    **who_p;
  3236. X
  3237. X    pstate_data = project_pstate_get(pp);
  3238. X    pstate_reviewer_list_type.list_parse
  3239. X    (
  3240. X        pstate_data->reviewer,
  3241. X        &type_p,
  3242. X        (void **)&who_p
  3243. X    );
  3244. X    *who_p = str_copy(name);
  3245. X}
  3246. X
  3247. X
  3248. Xvoid
  3249. Xproject_reviewer_delete(pp, name)
  3250. X    project_ty    *pp;
  3251. X    string_ty    *name;
  3252. X{
  3253. X    size_t        k;
  3254. X    pstate        pstate_data;
  3255. X
  3256. X    pstate_data = project_pstate_get(pp);
  3257. X    for (k = 0; k < pstate_data->reviewer->length; ++k)
  3258. X    {
  3259. X        if (str_equal(name, pstate_data->reviewer->list[k]))
  3260. X        {
  3261. X            str_free(pstate_data->reviewer->list[k]);
  3262. X            pstate_data->reviewer->list[k] =
  3263. X                pstate_data->reviewer->list
  3264. X                [
  3265. X                    --pstate_data->reviewer->length
  3266. X                ];
  3267. X            --k;
  3268. X        }
  3269. X    }
  3270. X}
  3271. X
  3272. X
  3273. Xpstate_history
  3274. Xproject_history_new(pp)
  3275. X    project_ty    *pp;
  3276. X{
  3277. X    pstate        pstate_data;
  3278. X    pstate_history    history_data;
  3279. X    pstate_history    *history_data_p;
  3280. X    type_ty        *type_p;
  3281. X
  3282. X    trace(("project_history_new()\n{\n"/*}*/));
  3283. X    pstate_data = project_pstate_get(pp);
  3284. X    assert(pstate_data->history);
  3285. X    pstate_history_list_type.list_parse
  3286. X    (
  3287. X        pstate_data->history,
  3288. X        &type_p,
  3289. X        (void **)&history_data_p
  3290. X    );
  3291. X    history_data = (pstate_history)pstate_history_type.alloc();
  3292. X    *history_data_p = history_data;
  3293. X    trace(("return %08lX;\n", history_data));
  3294. X    trace((/*{*/"}\n"));
  3295. X    return history_data;
  3296. X}
  3297. X
  3298. X
  3299. Xlong
  3300. Xproject_last_change_integrated(pp)
  3301. X    project_ty    *pp;
  3302. X{
  3303. X    pstate        pstate_data;
  3304. X    pstate_history    history_data;
  3305. X
  3306. X    pstate_data = project_pstate_get(pp);
  3307. X    if (!pstate_data->history || !pstate_data->history->length)
  3308. X        return -1;
  3309. X    history_data =
  3310. X        pstate_data->history->list[pstate_data->history->length - 1];
  3311. X    return history_data->change_number;
  3312. X}
  3313. X
  3314. X
  3315. Xvoid
  3316. Xproject_error(pp, s sva_last)
  3317. X    project_ty    *pp;
  3318. X    char        *s;
  3319. X    sva_last_decl
  3320. X{
  3321. X    va_list        ap;
  3322. X    string_ty    *msg;
  3323. X
  3324. X    sva_init(ap, s);
  3325. X    msg = str_vformat(s, ap);
  3326. X    va_end(ap);
  3327. X    error
  3328. X    (
  3329. X        "project \"%s\": %s",
  3330. X        project_name_get(pp)->str_text,
  3331. X        msg->str_text
  3332. X    );
  3333. X    str_free(msg);
  3334. X}
  3335. X
  3336. X
  3337. Xvoid
  3338. Xproject_fatal(pp, s sva_last)
  3339. X    project_ty    *pp;
  3340. X    char        *s;
  3341. X    sva_last_decl
  3342. X{
  3343. X    va_list        ap;
  3344. X    string_ty    *msg;
  3345. X
  3346. X    sva_init(ap, s);
  3347. X    msg = str_vformat(s, ap);
  3348. X    va_end(ap);
  3349. X    fatal
  3350. X    (
  3351. X        "project \"%s\": %s",
  3352. X        project_name_get(pp)->str_text,
  3353. X        msg->str_text
  3354. X    );
  3355. X}
  3356. X
  3357. X
  3358. Xvoid
  3359. Xproject_verbose(pp, s sva_last)
  3360. X    project_ty    *pp;
  3361. X    char        *s;
  3362. X    sva_last_decl
  3363. X{
  3364. X    va_list        ap;
  3365. X    string_ty    *msg;
  3366. X
  3367. X    sva_init(ap, s);
  3368. X    msg = str_vformat(s, ap);
  3369. X    va_end(ap);
  3370. X    verbose
  3371. X    (
  3372. X        "project \"%s\": %s",
  3373. X        project_name_get(pp)->str_text,
  3374. X        msg->str_text
  3375. X    );
  3376. X    str_free(msg);
  3377. X}
  3378. X
  3379. X
  3380. Xvoid
  3381. Xproject_change_append(pp, cn)
  3382. X    project_ty    *pp;
  3383. X    long        cn;
  3384. X{
  3385. X    pstate        pstate_data;
  3386. X    long        *change_p;
  3387. X    type_ty        *type_p;
  3388. X
  3389. X    pstate_data = project_pstate_get(pp);
  3390. X    assert(pstate_data->change);
  3391. X    pstate_change_list_type.list_parse
  3392. X    (
  3393. X        pstate_data->change,
  3394. X        &type_p,
  3395. X        (void **)&change_p
  3396. X    );
  3397. X    *change_p = cn;
  3398. X}
  3399. X
  3400. X
  3401. Xvoid
  3402. Xproject_change_delete(pp, cn)
  3403. X    project_ty    *pp;
  3404. X    long        cn;
  3405. X{
  3406. X    pstate        pstate_data;
  3407. X    long        j, k;
  3408. X
  3409. X    pstate_data = project_pstate_get(pp);
  3410. X    assert(pstate_data->change);
  3411. X    for (j = 0; j < pstate_data->change->length; ++j)
  3412. X    {
  3413. X        if (pstate_data->change->list[j] != cn)
  3414. X            continue;
  3415. X        for (k = j + 1; k < pstate_data->change->length; ++k)
  3416. X            pstate_data->change->list[k - 1] =
  3417. X                pstate_data->change->list[k];
  3418. X        pstate_data->change->length--;
  3419. X        break;
  3420. X    }
  3421. X}
  3422. X
  3423. X
  3424. Xstring_ty *
  3425. Xproject_version_get(pp)
  3426. X    project_ty    *pp;
  3427. X{
  3428. X    string_ty    *s;
  3429. X    pstate        pstate_data;
  3430. X    pstate_history    history_data;
  3431. X
  3432. X    trace(("project_version_get(pp = %08lX)\n{\n"/*}*/, pp));
  3433. X    pstate_data = project_pstate_get(pp);
  3434. X    assert(pstate_data->history);
  3435. X    assert(pstate_data->history->length > 0);
  3436. X    history_data =
  3437. X        pstate_data->history->list[pstate_data->history->length - 1];
  3438. X    s =
  3439. X        str_format
  3440. X        (
  3441. X            "%ld.%ld.D%3.3ld",
  3442. X            pstate_data->version_major,
  3443. X            pstate_data->version_minor,
  3444. X            history_data->delta_number
  3445. X        );
  3446. X    trace(("return \"%s\";\n", s->str_text));
  3447. X    trace((/*{*/"}\n"));
  3448. X    return s;
  3449. X}
  3450. X
  3451. X
  3452. Xstring_ty *
  3453. Xproject_owner(pp)
  3454. X    project_ty    *pp;
  3455. X{
  3456. X    return project_pstate_get(pp)->owner_name;
  3457. X}
  3458. X
  3459. X
  3460. Xstring_ty *
  3461. Xproject_group(pp)
  3462. X    project_ty    *pp;
  3463. X{
  3464. X    return project_pstate_get(pp)->group_name;
  3465. X}
  3466. X
  3467. X
  3468. Xstring_ty *
  3469. Xproject_default_development_directory(pp)
  3470. X    project_ty    *pp;
  3471. X{
  3472. X    pstate        pstate_data;
  3473. X
  3474. X    /*
  3475. X     * To cope with automounters, directories are stored as given,
  3476. X     * or are derived from the home directory in the passwd file.
  3477. X     * Within aegis, pathnames have their symbolic links resolved,
  3478. X     * and any comparison of paths is done on this "system idea"
  3479. X     * of the pathname.
  3480. X     */
  3481. X    pstate_data = project_pstate_get(pp);
  3482. X    return pstate_data->default_development_directory;
  3483. X}
  3484. X
  3485. X
  3486. Xuser_ty *
  3487. Xproject_user(pp)
  3488. X    project_ty    *pp;
  3489. X{
  3490. X    user_ty        *up;
  3491. X
  3492. X    trace(("project_user(pp = %08lX)\n{\n"/*}*/, pp));
  3493. X    up = user_symbolic(pp, project_owner(pp));
  3494. X    trace(("return %08lX;\n", up));
  3495. X    trace((/*{*/"}\n"));
  3496. X    return up;
  3497. X}
  3498. X
  3499. X
  3500. Xvoid
  3501. Xproject_become(pp)
  3502. X    project_ty    *pp;
  3503. X{
  3504. X    user_ty        *up;
  3505. X
  3506. X    trace(("project_become(pp = %08lX)\n{\n"/*}*/, pp));
  3507. X    up = project_user(pp);
  3508. X    user_become(up);
  3509. X    user_free(up);
  3510. X    trace((/*{*/"}\n"));
  3511. X}
  3512. X
  3513. X
  3514. Xvoid
  3515. Xproject_become_undo()
  3516. X{
  3517. X    trace(("project_become_undo()\n{\n"/*}*/));
  3518. X    user_become_undo();
  3519. X    trace((/*{*/"}\n"));
  3520. X}
  3521. X
  3522. X
  3523. Xint
  3524. Xproject_umask(pp)
  3525. X    project_ty    *pp;
  3526. X{
  3527. X    return project_pstate_get(pp)->umask;
  3528. X}
  3529. X
  3530. X
  3531. Xint
  3532. Xproject_delta_exists(pp, delta_number)
  3533. X    project_ty    *pp;
  3534. X    long        delta_number;
  3535. X{
  3536. X    int        result;
  3537. X    pstate        pstate_data;
  3538. X    long        j;
  3539. X    pstate_history    history_data;
  3540. X
  3541. X    trace(("project_delta_exists(pp = %08lX, delta_number = %ld)\n{\n"/*}*/, pp, delta_number));
  3542. X    pstate_data = project_pstate_get(pp);
  3543. X    if
  3544. X    (
  3545. X        pstate_data->history->length > 0
  3546. X    &&
  3547. X        pstate_data->history->list[pstate_data->history->length - 1]
  3548. X            ->delta_number == delta_number
  3549. X    )
  3550. X        result = -1;
  3551. X    else
  3552. X    {
  3553. X        result = 0;
  3554. X        for (j = 0; j < pstate_data->history->length; ++j)
  3555. X        {
  3556. X            history_data = pstate_data->history->list[j];
  3557. X            if (history_data->delta_number == delta_number)
  3558. X            {
  3559. X                result = 1;
  3560. X                break;
  3561. X            }
  3562. X        }
  3563. X    }
  3564. X    trace(("return %d;\n", result));
  3565. X    trace((/*{*/"}\n"));
  3566. X    return result;
  3567. X}
  3568. X
  3569. X
  3570. X/*
  3571. X * NAME
  3572. X *    project_delta_to_edit
  3573. X *
  3574. X * SYNOPSIS
  3575. X *    string_ty *project_delta_to_edit(project_ty *pp, long delta_number,
  3576. X *        string_ty *file_name);
  3577. X *
  3578. X * DESCRIPTION
  3579. X *    The project_delta_to_edit function is used to map a delta number
  3580. X *    into a specific edit number for a project source file.  This requires
  3581. X *    roll-forward of the edits to the named file, until the relevant
  3582. X *    delta is reached.
  3583. X *
  3584. X * ARGUMENTS
  3585. X *    pp        - project file is in
  3586. X *    delta_number    - delta number wanted
  3587. X *    file_name    - name of file
  3588. X *
  3589. X * RETURNS
  3590. X *    string_ty *;    string containing edit number,
  3591. X *            NULL if file does not exist at this delta.
  3592. X *
  3593. X * CAVEAT
  3594. X *    It is the caller's responsibility to free the string returned
  3595. X *    when not futher required.
  3596. X */
  3597. X
  3598. Xstring_ty *
  3599. Xproject_delta_to_edit(pp, delta_number, file_name)
  3600. X    project_ty    *pp;
  3601. X    long        delta_number;
  3602. X    string_ty    *file_name;
  3603. X{
  3604. X    string_ty    *edit_number;
  3605. X    pstate        pstate_data;
  3606. X    pstate_history    history_data;
  3607. X    long        j;
  3608. X    change_ty    *cp;
  3609. X    cstate_src    src_data;
  3610. X
  3611. X    trace(("project_delta_exists(pp = %08lX, delta_number = %ld)\n{\n"/*}*/, pp, delta_number));
  3612. X    edit_number = 0;
  3613. X    pstate_data = project_pstate_get(pp);
  3614. X    for (j = 0; j < pstate_data->history->length; ++j)
  3615. X    {
  3616. X        history_data = pstate_data->history->list[j];
  3617. X        cp = change_alloc(pp, history_data->change_number);
  3618. X        src_data = change_src_find(cp, file_name);
  3619. X        if (src_data)
  3620. X        {
  3621. X            if (src_data->action == file_action_remove)
  3622. X            {
  3623. X                if (edit_number)
  3624. X                    str_free(edit_number);
  3625. X                edit_number = 0;
  3626. X            }
  3627. X            else
  3628. X            {
  3629. X                /*
  3630. X                 * there should always be an edit number,
  3631. X                 * because it is checked for when the file
  3632. X                 * is read in.
  3633. X                 */
  3634. X                assert(src_data->edit_number);
  3635. X                if (edit_number)
  3636. X                    str_free(edit_number);
  3637. X                edit_number = str_copy(src_data->edit_number);
  3638. X            }
  3639. X        }
  3640. X        change_free(cp);
  3641. X        if (history_data->delta_number == delta_number)
  3642. X            break;
  3643. X    }
  3644. X    trace(("return %s;\n", edit_number ? edit_number->str_text : "NULL"));
  3645. X    trace((/*{*/"}\n"));
  3646. X    return edit_number;
  3647. X}
  3648. END_OF_FILE
  3649. if test 27441 -ne `wc -c <'aegis/project.c'`; then
  3650.     echo shar: \"'aegis/project.c'\" unpacked with wrong size!
  3651. fi
  3652. # end of 'aegis/project.c'
  3653. fi
  3654. if test -f 'aegis/user.c' -a "${1}" != "-c" ; then 
  3655.   echo shar: Will not clobber existing file \"'aegis/user.c'\"
  3656. else
  3657. echo shar: Extracting \"'aegis/user.c'\" \(30882 characters\)
  3658. sed "s/^X//" >'aegis/user.c' <<'END_OF_FILE'
  3659. X/*
  3660. X *    aegis - project change supervisor
  3661. X *    Copyright (C) 1991, 1992, 1993 Peter Miller.
  3662. X *    All rights reserved.
  3663. X *
  3664. X *    This program is free software; you can redistribute it and/or modify
  3665. X *    it under the terms of the GNU General Public License as published by
  3666. X *    the Free Software Foundation; either version 2 of the License, or
  3667. X *    (at your option) any later version.
  3668. X *
  3669. X *    This program is distributed in the hope that it will be useful,
  3670. X *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  3671. X *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  3672. X *    GNU General Public License for more details.
  3673. X *
  3674. X *    You should have received a copy of the GNU General Public License
  3675. X *    along with this program; if not, write to the Free Software
  3676. X *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  3677. X *
  3678. X * MANIFEST: functions to manage information about users
  3679. X */
  3680. X
  3681. X#include <ctype.h>
  3682. X#include <stdlib.h>
  3683. X#include <string.h>
  3684. X#include <pwd.h>
  3685. X#include <grp.h>
  3686. X#include <unistd.h>
  3687. X
  3688. X#include <commit.h>
  3689. X#include <conf.h>
  3690. X#include <error.h>
  3691. X#include <gonzo.h>
  3692. X#include <lock.h>
  3693. X#include <mem.h>
  3694. X#include <option.h>
  3695. X#include <os.h>
  3696. X#include <project.h>
  3697. X#include <trace.h>
  3698. X#include <user.h>
  3699. X#include <undo.h>
  3700. X#include <word.h>
  3701. X
  3702. X
  3703. Xstatic    size_t    nusers;
  3704. Xstatic    user_ty    **user;
  3705. X
  3706. X
  3707. X/*
  3708. X * NAME
  3709. X *    user_numeric
  3710. X *
  3711. X * SYNOPSIS
  3712. X *    user_ty *user_numeric(project_ty *pp, int uid);
  3713. X *
  3714. X * DESCRIPTION
  3715. X *    The user_numeric function is used to
  3716. X *    create a user structure from a uid.
  3717. X *
  3718. X *    The default group is derived from the project group,
  3719. X *    or the password file if a NULL project pointer is supplied.
  3720. X *
  3721. X * ARGUMENTS
  3722. X *    pp    - project user is associated with
  3723. X *    uid    - system user id
  3724. X *
  3725. X * RETURNS
  3726. X *    pointer to user structure in dynamic memory
  3727. X *
  3728. X * CAVEAT
  3729. X *    Release the user structure with the user_free() function when done.
  3730. X */
  3731. X
  3732. Xuser_ty *
  3733. Xuser_numeric(pp, uid)
  3734. X    project_ty    *pp;
  3735. X    int        uid;
  3736. X{
  3737. X    size_t        j;
  3738. X    user_ty        *up;
  3739. X    struct passwd    *pw;
  3740. X    struct group    *gr;
  3741. X
  3742. X    /*
  3743. X     * see if we already know her
  3744. X     */
  3745. X    trace(("user_numeric(pp = %08lX, uid = %d)\n{\n"/*}*/, pp, uid));
  3746. X    for (j = 0; j < nusers; ++j)
  3747. X    {
  3748. X        up = user[j];
  3749. X        if (up->uid == uid)
  3750. X        {
  3751. X            up = user_copy(up);
  3752. X            goto ret;
  3753. X        }
  3754. X    }
  3755. X
  3756. X    /*
  3757. X     * first time we have met her
  3758. X     *    group always binds to the project
  3759. X     *
  3760. X     * Always use the number as the primary reference:
  3761. X     * system treats the first entry as cannonical, so do we.
  3762. X     */
  3763. X    pw = getpwuid(uid);
  3764. X    if (!pw)
  3765. X        fatal("uid %d unknown", uid);
  3766. X    up = (user_ty *)mem_alloc_clear(sizeof(user_ty));
  3767. X    up->reference_count = 1;
  3768. X    if (pp)
  3769. X        up->pp = project_copy(pp);
  3770. X    up->uid = uid;
  3771. X    up->gid = pw->pw_gid;
  3772. X    up->umask = DEFAULT_UMASK;
  3773. X    up->name = str_from_c(pw->pw_name);
  3774. X    up->home = str_from_c(pw->pw_dir);
  3775. X    if (pw->pw_gecos && pw->pw_gecos[0])
  3776. X        up->full_name = str_from_c(pw->pw_gecos);
  3777. X#ifndef CONF_NO_pw_comment
  3778. X    else if (pw->pw_comment && pw->pw_comment[0])
  3779. X        up->full_name = str_from_c(pw->pw_comment);
  3780. X#endif
  3781. X    else
  3782. X        up->full_name = str_from_c(pw->pw_name);
  3783. X    if (pp)
  3784. X    {
  3785. X        gr = getgrnam(project_group(pp)->str_text);
  3786. X        if (!gr)
  3787. X        {
  3788. X            fatal
  3789. X            (
  3790. X                "group \"%s\" unknown",
  3791. X                project_group(pp)->str_text
  3792. X            );
  3793. X        }
  3794. X        up->gid = gr->gr_gid;
  3795. X    }
  3796. X    gr = getgrgid(up->gid);
  3797. X    if (!gr)
  3798. X        fatal("gid %d unknown", up->gid);
  3799. X    up->group = str_from_c(gr->gr_name);
  3800. X
  3801. X    /*
  3802. X     * add it to the list
  3803. X     */
  3804. X    *(user_ty **)enlarge(&nusers, (char **)&user, sizeof(user_ty *)) = up;
  3805. X
  3806. X    /*
  3807. X     * Find the umask.
  3808. X     *
  3809. X     * Only do this AFTER adding it to the list,
  3810. X     * because the project may construct the same user
  3811. X     * when it reads it's state file to find the umask.
  3812. X     */
  3813. X    if (pp)
  3814. X        up->umask = project_umask(pp);
  3815. X
  3816. X    /*
  3817. X     * here for all exits
  3818. X     */
  3819. X    ret:
  3820. X    trace(("return %08lX;\n", up));
  3821. X    trace((/*{*/"}\n"));
  3822. X    return up;
  3823. X}
  3824. X
  3825. X
  3826. X/*
  3827. X * NAME
  3828. X *    user_symbolic
  3829. X *
  3830. X * SYNOPSIS
  3831. X *    user_ty *user_symbolic(project_ty *pp, string_ty *name);
  3832. X *
  3833. X * DESCRIPTION
  3834. X *    The user_symbolic function is used to
  3835. X *    create a user structure from a login name.
  3836. X *
  3837. X *    The login name is mapped to a uid.  The password file is searched
  3838. X *    from beginning to end, so the cannonical name is the first found
  3839. X *    in the password file.  The cannonical name is used even for user
  3840. X *    structures created with this function.
  3841. X *
  3842. X * ARGUMENTS
  3843. X *    pp    - project user is associated with
  3844. X *    name    - user's login name
  3845. X *
  3846. X * RETURNS
  3847. X *    pointer to user structure in dynamic memory
  3848. X *
  3849. X * CAVEAT
  3850. X *    Release the user structure with the user_free() function when done.
  3851. X */
  3852. X
  3853. Xuser_ty *
  3854. Xuser_symbolic(pp, name)
  3855. X    project_ty    *pp;
  3856. X    string_ty    *name;
  3857. X{
  3858. X    struct passwd    *pw;
  3859. X    user_ty        *result;
  3860. X
  3861. X    trace(("user_symbolic(pp = %08lX, name = \"%s\")\n{\n"/*}*/, pp,
  3862. X        name->str_text));
  3863. X    pw = getpwnam(name->str_text);
  3864. X    if (!pw)
  3865. X        fatal("user \"%s\" unknown", name->str_text);
  3866. X    result = user_numeric(pp, pw->pw_uid);
  3867. X    trace(("return %08lX;\n", result));
  3868. X    trace((/*{*/"}\n"));
  3869. X    return result;
  3870. X}
  3871. X
  3872. X
  3873. X/*
  3874. X * NAME
  3875. X *    user_executing
  3876. X *
  3877. X * SYNOPSIS
  3878. X *    user_ty *user_executing(project_ty *pp);
  3879. X *
  3880. X * DESCRIPTION
  3881. X *    The user_executing function is used to
  3882. X *    create a user structure based on the user who invoked
  3883. X *    the currently executing program.
  3884. X *
  3885. X * ARGUMENTS
  3886. X *    pp    - project user is operating on
  3887. X *
  3888. X * RETURNS
  3889. X *    pointer to user structure in dynamic memory
  3890. X *
  3891. X * CAVEAT
  3892. X *    Release the user structure with the user_free() function when done.
  3893. X */
  3894. X
  3895. Xuser_ty *
  3896. Xuser_executing(pp)
  3897. X    project_ty    *pp;
  3898. X{
  3899. X    user_ty        *result;
  3900. X    int        uid;
  3901. X    int        gid;
  3902. X
  3903. X    trace(("user_executing(pp = %08lX)\n{\n"/*}*/, pp));
  3904. X    os_become_orig_query(&uid, &gid, (int *)0);
  3905. X    result = user_numeric(pp, uid);
  3906. X    trace(("return %08lX;\n", result));
  3907. X    trace((/*{*/"}\n"));
  3908. X    return result;
  3909. X}
  3910. X
  3911. X
  3912. X/*
  3913. X * NAME
  3914. X *    user_free
  3915. X *
  3916. X * SYNOPSIS
  3917. X *    void user_free(user_ty *up);
  3918. X *
  3919. X * DESCRIPTION
  3920. X *    The user_free function is used to
  3921. X *    release memory when a user structure is done with.
  3922. X *
  3923. X * ARGUMENTS
  3924. X *    up    - pointer to user structure in dynamic memory
  3925. X */
  3926. X
  3927. Xvoid
  3928. Xuser_free(up)
  3929. X    user_ty        *up;
  3930. X{
  3931. X    trace(("user_free(up = %08lX)\n{\n"/*}*/, up));
  3932. X    up->reference_count--;
  3933. X    if (up->reference_count <= 0)
  3934. X    {
  3935. X        size_t    j;
  3936. X
  3937. X        /*
  3938. X         * remove from cache
  3939. X         */
  3940. X        for (j = 0; j < nusers; ++j)
  3941. X        {
  3942. X            if (user[j] == up)
  3943. X            {
  3944. X                user[j] = user[--nusers];
  3945. X                break;
  3946. X            }
  3947. X        }
  3948. X
  3949. X        /*
  3950. X         * release memory references
  3951. X         */
  3952. X        if (up->pp)
  3953. X            project_free(up->pp);
  3954. X        str_free(up->name);
  3955. X        str_free(up->full_name);
  3956. X        str_free(up->home);
  3957. X        str_free(up->group);
  3958. X        if (up->ustate_path)
  3959. X            str_free(up->ustate_path);
  3960. X        if (up->ustate_data)
  3961. X            ustate_type.free(up->ustate_data);
  3962. X        if (up->uconf_path)
  3963. X            str_free(up->uconf_path);
  3964. X        if (up->uconf_data)
  3965. X            uconf_type.free(up->uconf_data);
  3966. X        mem_free((char *)up);
  3967. X    }
  3968. X    trace((/*{*/"}\n"));
  3969. X}
  3970. X
  3971. X
  3972. X/*
  3973. X * NAME
  3974. X *    user_copy
  3975. X *
  3976. X * SYNOPSIS
  3977. X *    user_ty *user_copy(user_ty *up);
  3978. X *
  3979. X * DESCRIPTION
  3980. X *    The user_copy function is used to
  3981. X *    make a logical copy of a user structure.
  3982. X *
  3983. X * ARGUMENTS
  3984. X *    up    - pointer to user structure in dynamic memory.
  3985. X *
  3986. X * RETURNS
  3987. X *    pointer to user structure in dynamic memory
  3988. X *
  3989. X * CAVEAT
  3990. X *    Release the user structure with the user_free() function when done.
  3991. X */
  3992. X
  3993. Xuser_ty *
  3994. Xuser_copy(up)
  3995. X    user_ty        *up;
  3996. X{
  3997. X    trace(("user_copy(up = %08lX)\n{\n"/*}*/, up));
  3998. X    up->reference_count++;
  3999. X    trace(("return %08lX;\n", up));
  4000. X    trace((/*{*/"}\n"));
  4001. X    return up;
  4002. X}
  4003. X
  4004. X
  4005. X/*
  4006. X * NAME
  4007. X *    user_name
  4008. X *
  4009. X * SYNOPSIS
  4010. X *    string_ty *user_name(user_ty *);
  4011. X *
  4012. X * DESCRIPTION
  4013. X *    The user_name function is used to
  4014. X *    return the login name of the user described by the user structure.
  4015. X *
  4016. X * ARGUMENTS
  4017. X *    up    - user structure describing user
  4018. X *
  4019. X * RETURNS
  4020. X *    pointer to string containing user's login name
  4021. X */
  4022. X
  4023. Xstring_ty *
  4024. Xuser_name(up)
  4025. X    user_ty        *up;
  4026. X{
  4027. X    trace(("user_name(up = %08lX)\n{\n"/*}*/, up));
  4028. X    trace(("return \"%s\";\n", up->name->str_text));
  4029. X    trace((/*{*/"}\n"));
  4030. X    return up->name;
  4031. X}
  4032. X
  4033. X
  4034. X/*
  4035. X * NAME
  4036. X *    user_id
  4037. X *
  4038. X * SYNOPSIS
  4039. X *    int user_id(user_ty *up);
  4040. X *
  4041. X * DESCRIPTION
  4042. X *    The user_id function is used to
  4043. X *    determine the uid of the user described by the user structure.
  4044. X *
  4045. X * ARGUMENTS
  4046. X *    up    - user structure describing user
  4047. X *
  4048. X * RETURNS
  4049. X *    the system uid of the user
  4050. X */
  4051. X
  4052. Xint
  4053. Xuser_id(up)
  4054. X    user_ty        *up;
  4055. X{
  4056. X    trace(("user_id(up = %08lX)\n{\n"/*}*/, up));
  4057. X    trace(("return %d;\n", up->uid));
  4058. X    trace((/*{*/"}\n"));
  4059. X    return up->uid;
  4060. X}
  4061. X
  4062. X
  4063. X/*
  4064. X * NAME
  4065. X *    user_group
  4066. X *
  4067. X * SYNOPSIS
  4068. X *    string_ty *user_group(user_ty *);
  4069. X *
  4070. X * DESCRIPTION
  4071. X *    The user_group function is used to
  4072. X *    determine the name of the default group of a user.
  4073. X *
  4074. X * ARGUMENTS
  4075. X *    up    - pointer to user structure
  4076. X *
  4077. X * RETURNS
  4078. X *    pointer to string naming default group
  4079. X */
  4080. X
  4081. Xstring_ty *
  4082. Xuser_group(up)
  4083. X    user_ty        *up;
  4084. X{
  4085. X    trace(("user_group(up = %08lX)\n{\n"/*}*/, up));
  4086. X    trace(("return \"%s\";\n", up->group->str_text));
  4087. X    trace((/*{*/"}\n"));
  4088. X    return up->group;
  4089. X}
  4090. X
  4091. X
  4092. X/*
  4093. X * NAME
  4094. X *    user_gid
  4095. X *
  4096. X * SYNOPSIS
  4097. X *    int user_gid(user_ty *up);
  4098. X *
  4099. X * DESCRIPTION
  4100. X *    The user_gid function is used to
  4101. X *    determine the system gid of the user.
  4102. X *
  4103. X * ARGUMENTS
  4104. X *    up    - pointer to user structure
  4105. X *
  4106. X * RETURNS
  4107. X *    the default system gid of the user
  4108. X */
  4109. X
  4110. Xint
  4111. Xuser_gid(up)
  4112. X    user_ty        *up;
  4113. X{
  4114. X    trace(("user_gid(up = %08lX)\n{\n"/*}*/, up));
  4115. X    trace(("return %d;\n", up->gid));
  4116. X    trace((/*{*/"}\n"));
  4117. X    return up->gid;
  4118. X}
  4119. X
  4120. X
  4121. X/*
  4122. X * NAME
  4123. X *    user_umask
  4124. X *
  4125. X * SYNOPSIS
  4126. X *    int user_umask(user_ty *up);
  4127. X *
  4128. X * DESCRIPTION
  4129. X *    The user_umask function is used to
  4130. X *    determine the umask (file creation mask) of the user.
  4131. X *
  4132. X * ARGUMENTS
  4133. X *    up    - pointer to user structure
  4134. X *
  4135. X * RETURNS
  4136. X *    the default system umask of the user
  4137. X */
  4138. X
  4139. Xint
  4140. Xuser_umask(up)
  4141. X    user_ty        *up;
  4142. X{
  4143. X    trace(("user_umask(up = %08lX)\n{\n"/*}*/, up));
  4144. X    trace(("return %05o;\n", up->umask));
  4145. X    trace((/*{*/"}\n"));
  4146. X    return up->umask;
  4147. X}
  4148. X
  4149. X
  4150. X/*
  4151. X * NAME
  4152. X *    lock_sync
  4153. X *
  4154. X * SYNOPSIS
  4155. X *    void lock_sync(user_ty *up);
  4156. X *
  4157. X * DESCRIPTION
  4158. X *    The lock_sync function is used to
  4159. X *    flush any out-of-date data caching
  4160. X *    associated with the user structure.
  4161. X *
  4162. X * ARGUMENTS
  4163. X *    up    - pointer to user structure
  4164. X */
  4165. X
  4166. Xstatic void lock_sync _((user_ty *));
  4167. X
  4168. Xstatic void
  4169. Xlock_sync(up)
  4170. X    user_ty        *up;
  4171. X{
  4172. X    long        n;
  4173. X
  4174. X    trace(("lock_sync(up = %08lX)\n{\n"/*}*/, up));
  4175. X    n = lock_magic();
  4176. X    if (up->lock_magic != n)
  4177. X    {
  4178. X        up->lock_magic = n;
  4179. X        if (up->ustate_data && !up->ustate_is_new)
  4180. X        {
  4181. X            ustate_type.free(up->ustate_data);
  4182. X            up->ustate_data = 0;
  4183. X        }
  4184. X    }
  4185. X    trace((/*{*/"}\n"));
  4186. X}
  4187. X
  4188. X
  4189. X/*
  4190. X * NAME
  4191. X *    user_ustate_get
  4192. X *
  4193. X * SYNOPSIS
  4194. X *    ustate user_ustate_get(user_ty *up);
  4195. X *
  4196. X * DESCRIPTION
  4197. X *    The user_ustate_get function is used to
  4198. X *    fetch the ``ustate'' file for this user,
  4199. X *    caching for future reference.
  4200. X *
  4201. X * ARGUMENTS
  4202. X *    up    -pointer to user structure
  4203. X *
  4204. X * RETURNS
  4205. X *    pointer to ustate structure in dynamic memory
  4206. X */
  4207. X
  4208. Xstatic ustate user_ustate_get _((user_ty *));
  4209. X
  4210. Xstatic ustate
  4211. Xuser_ustate_get(up)
  4212. X    user_ty        *up;
  4213. X{
  4214. X    trace(("user_ustate_get(up = %08lX)\n{\n"/*}*/, up));
  4215. X    lock_sync(up);
  4216. X    if (!up->ustate_path)
  4217. X        up->ustate_path =
  4218. X            gonzo_ustate_path(project_name_get(up->pp), up->name);
  4219. X    if (!up->ustate_data)
  4220. X    {
  4221. X        gonzo_become();
  4222. X        if (os_exists(up->ustate_path))
  4223. X        {
  4224. X            up->ustate_data =
  4225. X                ustate_read_file(up->ustate_path->str_text);
  4226. X        }
  4227. X        else
  4228. X        {
  4229. X            up->ustate_data = (ustate)ustate_type.alloc();
  4230. X            up->ustate_is_new = 1;
  4231. X        }
  4232. X        gonzo_become_undo();
  4233. X        if (!up->ustate_data->own)
  4234. X            up->ustate_data->own =
  4235. X                (ustate_own_list)
  4236. X                ustate_own_list_type.alloc();
  4237. X    }
  4238. X    trace(("return %08lX;\n", up->ustate_data));
  4239. X    trace((/*{*/"}\n"));
  4240. X    return up->ustate_data;
  4241. X}
  4242. X
  4243. X
  4244. X/*
  4245. X * NAME
  4246. X *    user_uconf_get
  4247. X *
  4248. X * SYNOPSIS
  4249. X *    void user_uconf_get(void);
  4250. X *
  4251. X * DESCRIPTION
  4252. X *    The user_uconf_get function is used to
  4253. X *    fetch the ``uconf'' file for this user,
  4254. X *    caching for future reference.
  4255. X *
  4256. X * ARGUMENTS
  4257. X *    up    -pointer to user structure
  4258. X *
  4259. X * RETURNS
  4260. X *    pointer to uconf structure in dynamic memory
  4261. X */
  4262. X
  4263. Xstatic uconf user_uconf_get _((user_ty *up));
  4264. X
  4265. Xstatic uconf
  4266. Xuser_uconf_get(up)
  4267. X    user_ty        *up;
  4268. X{
  4269. X    trace(("user_uconf_get(up = %08lX)\n{\n"/*}*/, up));
  4270. X    lock_sync(up);
  4271. X    if (!up->uconf_path)
  4272. X        up->uconf_path =
  4273. X            str_format("%S/.%src", up->home, option_progname_get());
  4274. X    if (!up->uconf_data)
  4275. X    {
  4276. X        user_become(up);
  4277. X        if (os_exists(up->uconf_path))
  4278. X        {
  4279. X            up->uconf_data =
  4280. X                uconf_read_file(up->uconf_path->str_text);
  4281. X        }
  4282. X        else
  4283. X            up->uconf_data = (uconf)uconf_type.alloc();
  4284. X        user_become_undo();
  4285. X    }
  4286. X    trace(("return %08lX;\n", up->uconf_data));
  4287. X    trace((/*{*/"}\n"));
  4288. X    return up->uconf_data;
  4289. X}
  4290. X
  4291. X
  4292. X/*
  4293. X * NAME
  4294. X *    user_home
  4295. X *
  4296. X * SYNOPSIS
  4297. X *    string_ty *user_home(user_ty *up);
  4298. X *
  4299. X * DESCRIPTION
  4300. X *    The user_home function is used to
  4301. X *    fetch the home directory of the user.
  4302. X *
  4303. X * ARGUMENTS
  4304. X *    up    - pointer to user structure
  4305. X *
  4306. X * RETURNS
  4307. X *    pointer to string containing absolute path
  4308. X *
  4309. X * CAVEAT
  4310. X *    If the path isn't absolute in /etc/passwd,
  4311. X *    it won't be here, either.
  4312. X */
  4313. X
  4314. Xstring_ty *
  4315. Xuser_home(up)
  4316. X    user_ty        *up;
  4317. X{
  4318. X    trace(("user_home(up = %08lX)\n{\n"/*}*/, up));
  4319. X    assert(up->home);
  4320. X    trace(("return \"%s\";\n", up->home->str_text));
  4321. X    trace((/*{*/"}\n"));
  4322. X    return up->home;
  4323. X}
  4324. X
  4325. X
  4326. X/*
  4327. X * NAME
  4328. X *    user_full_name
  4329. X *
  4330. X * SYNOPSIS
  4331. X *    char *user_full_name(string_ty *login);
  4332. X *
  4333. X * DESCRIPTION
  4334. X *    The user_full_name function is used to
  4335. X *    find the full name of a user, given their login name.
  4336. X *
  4337. X * ARGUMENTS
  4338. X *    login    - pointer to string containing login name
  4339. X *
  4340. X * RETURNS
  4341. X *    pointer to string containing user's full name
  4342. X *
  4343. X * CAVEAT
  4344. X *    Will return the empty string if the user can't be found.
  4345. X *    NEVER modify the data pointed to by the return value.
  4346. X *    The returned data is volatile, it will not remain stable for long.
  4347. X */
  4348. X
  4349. Xchar *
  4350. Xuser_full_name(name)
  4351. X    string_ty    *name;
  4352. X{
  4353. X    struct passwd    *pw;
  4354. X    char        *result;
  4355. X
  4356. X    trace(("user_full_name(name = \"%s\")\n{\n"/*}*/, name->str_text));
  4357. X    pw = getpwnam(name->str_text);
  4358. X    if (!pw)
  4359. X        result = "";
  4360. X    else if (pw->pw_gecos && pw->pw_gecos[0])
  4361. X        result = pw->pw_gecos;
  4362. X#ifndef CONF_NO_pw_comment
  4363. X    else if (pw->pw_comment && pw->pw_comment[0])
  4364. X        result = pw->pw_comment;
  4365. X#endif
  4366. X    else
  4367. X        result = "";
  4368. X    trace(("return \"%s\";\n", result));
  4369. X    trace((/*{*/"}\n"));
  4370. X    return result;
  4371. X}
  4372. X
  4373. X
  4374. X/*
  4375. X * NAME
  4376. X *    user_ustate_write
  4377. X *
  4378. X * SYNOPSIS
  4379. X *    void user_ustate_write(user_ty *up);
  4380. X *
  4381. X * DESCRIPTION
  4382. X *    The user_ustate_write function is used to
  4383. X *    write any modified ustate file contents back to disk.
  4384. X *
  4385. X * ARGUMENTS
  4386. X *    up    - pointer to user structure
  4387. X */
  4388. X
  4389. Xvoid
  4390. Xuser_ustate_write(up)
  4391. X    user_ty        *up;
  4392. X{
  4393. X    string_ty    *filename_new;
  4394. X    string_ty    *filename_old;
  4395. X    static int    count;
  4396. X
  4397. X    trace(("user_ustate_write(up = %08lX)\n{\n"/*}*/, up));
  4398. X    assert(up->ustate_data);
  4399. X    assert(up->ustate_path);
  4400. X    if (!up->ustate_modified)
  4401. X        goto ret;
  4402. X
  4403. X    /*
  4404. X     * write it out
  4405. X     */
  4406. X    filename_new = str_format("%S,%d", up->ustate_path, ++count);
  4407. X    filename_old = str_format("%S,%d", up->ustate_path, ++count);
  4408. X    gonzo_become();
  4409. X    if (up->ustate_is_new)
  4410. X    {
  4411. X        undo_unlink_errok(filename_new);
  4412. X        ustate_write_file(filename_new->str_text, up->ustate_data);
  4413. X        commit_rename(filename_new, up->ustate_path);
  4414. X    }
  4415. X    else
  4416. X    {
  4417. X        undo_unlink_errok(filename_new);
  4418. X        ustate_write_file(filename_new->str_text, up->ustate_data);
  4419. X        commit_rename(up->ustate_path, filename_old);
  4420. X        commit_rename(filename_new, up->ustate_path);
  4421. X        commit_unlink_errok(filename_old);
  4422. X    }
  4423. X    os_chmod(filename_new, 0644);
  4424. X    gonzo_become_undo();
  4425. X    str_free(filename_new);
  4426. X    str_free(filename_old);
  4427. X    up->ustate_modified = 0;
  4428. X    up->ustate_is_new = 0;
  4429. X    ret:
  4430. X    trace((/*{*/"}\n"));
  4431. X}
  4432. X
  4433. X
  4434. X/*
  4435. X * NAME
  4436. X *    user_own_add
  4437. X *
  4438. X * SYNOPSIS
  4439. X *    void user_own_add(user_ty *up, string_ty *project_name,
  4440. X *        long change_number);
  4441. X *
  4442. X * DESCRIPTION
  4443. X *    The user_own_add function is used to
  4444. X *    add a change to the user's list of owned changes.
  4445. X *
  4446. X * ARGUMENTS
  4447. X *    up        - pointer to user structure
  4448. X *    project_name    - project of the change
  4449. X *    change_number    - number of the change
  4450. X *
  4451. X * CAVEAT
  4452. X *    The change is assumed to be unique.
  4453. X */
  4454. X
  4455. Xvoid
  4456. Xuser_own_add(up, project_name, change_number)
  4457. X    user_ty        *up;
  4458. X    string_ty    *project_name;
  4459. X    long        change_number;
  4460. X{
  4461. X    ustate        ustate_data;
  4462. X    int        j;
  4463. X    ustate_own    own_data = 0;
  4464. X    ustate_own    *own_data_p;
  4465. X    long        *change_p;
  4466. X    type_ty        *type_p;
  4467. X
  4468. X    trace(("user_own_add()\n{\n"/*}*/));
  4469. X    ustate_data = user_ustate_get(up);
  4470. X    assert(ustate_data->own);
  4471. X
  4472. X    /*
  4473. X     * See if the project is already known.
  4474. X     */
  4475. X    for (j = 0; j < ustate_data->own->length; ++j)
  4476. X    {
  4477. X        own_data = ustate_data->own->list[j];
  4478. X        if (str_equal(own_data->project_name, project_name))
  4479. X            break;
  4480. X    }
  4481. X
  4482. X    /*
  4483. X     * If the project isn't known, append it to the list.
  4484. X     */
  4485. X    if (j >= ustate_data->own->length)
  4486. X    {
  4487. X        ustate_own_list_type.list_parse
  4488. X        (
  4489. X            ustate_data->own,
  4490. X            &type_p,
  4491. X            (void **)&own_data_p
  4492. X        );
  4493. X        own_data = (ustate_own)ustate_own_type.alloc();
  4494. X        *own_data_p = own_data;
  4495. X        own_data->project_name = str_copy(project_name);
  4496. X    }
  4497. X
  4498. X    /*
  4499. X     * Create a changes for the project, if necessary.
  4500. X     */
  4501. X    if (!own_data->changes)
  4502. X        own_data->changes =
  4503. X            (ustate_own_changes_list)
  4504. X            ustate_own_changes_list_type.alloc();
  4505. X
  4506. X    /*
  4507. X     * Add another item to the changes list for the project.
  4508. X     */
  4509. X    ustate_own_changes_list_type.list_parse
  4510. X    (
  4511. X        own_data->changes,
  4512. X        &type_p,
  4513. X        (void **)&change_p
  4514. X    );
  4515. X    *change_p = change_number;
  4516. X    up->ustate_modified = 1;
  4517. X    trace((/*{*/"}\n"));
  4518. X}
  4519. X
  4520. X
  4521. X/*
  4522. X * NAME
  4523. X *    user_own_nth
  4524. X *
  4525. X * SYNOPSIS
  4526. X *    int user_own_nth(user_ty *up, long n, long *change_number);
  4527. X *
  4528. X * DESCRIPTION
  4529. X *    The user_own_nth function is used to fetch the n'th
  4530. X *    change owned by a user.
  4531. X *    The project name is derived from the user structure.
  4532. X *
  4533. X * ARGUMENTS
  4534. X *    up        - pointer to user structure
  4535. X *    n        - selector
  4536. X *    change_number    - pointer to where to put number of the change
  4537. X *
  4538. X * RETURNS
  4539. X *    1 on sucess, 0 if no such n.
  4540. X */
  4541. X
  4542. Xint
  4543. Xuser_own_nth(up, n, change_number)
  4544. X    user_ty        *up;
  4545. X    long        n;
  4546. X    long        *change_number;
  4547. X{
  4548. X    ustate        ustate_data;
  4549. X    int        j;
  4550. X    int        result;
  4551. X
  4552. X    trace(("user_own_nth(up = %08lX, n = %ld)\n{\n"/*}*/, (long)up, n));
  4553. X    result = 0;
  4554. X    assert(n >= 0);
  4555. X    assert(up->pp);
  4556. X    if (n < 0 || !up->pp)
  4557. X        goto done;
  4558. X    ustate_data = user_ustate_get(up);
  4559. X    assert(ustate_data->own);
  4560. X
  4561. X    /*
  4562. X     * find the relevant project
  4563. X     *    and extract the n'th change
  4564. X     */
  4565. X    for (j = 0; j < ustate_data->own->length; ++j)
  4566. X    {
  4567. X        ustate_own    own_data;
  4568. X
  4569. X        own_data = ustate_data->own->list[j];
  4570. X        if (str_equal(project_name_get(up->pp), own_data->project_name))
  4571. X        {
  4572. X            if (n < own_data->changes->length)
  4573. X            {
  4574. X                *change_number = own_data->changes->list[n];
  4575. X                result = 1;
  4576. X            }
  4577. X            break;
  4578. X        }
  4579. X    }
  4580. X
  4581. X    /*
  4582. X     * here for all exits
  4583. X     */
  4584. X    done:
  4585. X    trace(("return %d;\n", result));
  4586. X    trace((/*{*/"}\n"));
  4587. X    return result;
  4588. X}
  4589. X
  4590. X
  4591. X/*
  4592. X * NAME
  4593. X *    user_own_remove
  4594. X *
  4595. X * SYNOPSIS
  4596. X *    void user_own_remove(user_ty *up, string_ty *project_name,
  4597. X *        long change_number);
  4598. X *
  4599. X * DESCRIPTION
  4600. X *    The user_own_remove function is used to
  4601. X *    remove a change from the user's owned change list.
  4602. X *
  4603. X * ARGUMENTS
  4604. X *    up        - pointer to user structure
  4605. X *    project_name    - project of the change
  4606. X *    change_number    - number of the change
  4607. X *
  4608. X * CAVEAT
  4609. X *    The change is assumed to be unique.
  4610. X */
  4611. X
  4612. Xvoid
  4613. Xuser_own_remove(up, project_name, change_number)
  4614. X    user_ty        *up;
  4615. X    string_ty    *project_name;
  4616. X    long        change_number;
  4617. X{
  4618. X    ustate        ustate_data;
  4619. X    int        j, k;
  4620. X    ustate_own    own_data;
  4621. X
  4622. X    trace(("usate_own_remove()\n{\n"/*}*/));
  4623. X    ustate_data = user_ustate_get(up);
  4624. X    assert(ustate_data->own);
  4625. X
  4626. X    /*
  4627. X     * Search for the project in the ``own'' list.
  4628. X     */
  4629. X    for (j = 0; ; ++j)
  4630. X    {
  4631. X        if (j >= ustate_data->own->length)
  4632. X            goto ret;
  4633. X        own_data = ustate_data->own->list[j];
  4634. X        if (str_equal(own_data->project_name, project_name))
  4635. X            break;
  4636. X    }
  4637. X
  4638. X    /*
  4639. X     * Create the ``changes'' list for the project, if necessary.
  4640. X     */
  4641. X    if (!own_data->changes)
  4642. X        own_data->changes =
  4643. X            (ustate_own_changes_list)
  4644. X            ustate_own_changes_list_type.alloc();
  4645. X
  4646. X    /*
  4647. X     * Search for the change in the ``changes'' list.
  4648. X     */
  4649. X    for (k = 0; k < own_data->changes->length; ++k)
  4650. X    {
  4651. X        if (own_data->changes->list[k] == change_number)
  4652. X            break;
  4653. X    }
  4654. X
  4655. X    /*
  4656. X     * If the change was there, remove it from the list.
  4657. X     */
  4658. X    if (k < own_data->changes->length)
  4659. X    {
  4660. X        own_data->changes->list[k] =
  4661. X            own_data->changes->list[own_data->changes->length - 1];
  4662. X        own_data->changes->length--;
  4663. X        up->ustate_modified = 1;
  4664. X    }
  4665. X
  4666. X    /*
  4667. X     * If the changes list for the project is now empty,
  4668. X     * remove the project from the ``own'' list.
  4669. X     */
  4670. X    if (!own_data->changes->length)
  4671. X    {
  4672. X        ustate_own_type.free(own_data);
  4673. X        ustate_data->own->list[j] =
  4674. X            ustate_data->own->list[ustate_data->own->length - 1];
  4675. X        ustate_data->own->length--;
  4676. X        up->ustate_modified = 1;
  4677. X    }
  4678. X
  4679. X    /*
  4680. X     * here for all exits
  4681. X     */
  4682. X    ret:
  4683. X    trace((/*{*/"}\n"));
  4684. X}
  4685. X
  4686. X
  4687. X/*
  4688. X * NAME
  4689. X *    user_ustate_lock_prepare
  4690. X *
  4691. X * SYNOPSIS
  4692. X *    void user_ustate_lock_prepare(user_ty *up);
  4693. X *
  4694. X * DESCRIPTION
  4695. X *    The user_ustate_lock_prepare function is used to
  4696. X *    notify the lock manager that a ustate lock will be required.
  4697. X *
  4698. X * ARGUMENTS
  4699. X *    up    - pointer to user structure
  4700. X */
  4701. X
  4702. Xvoid
  4703. Xuser_ustate_lock_prepare(up)
  4704. X    user_ty        *up;
  4705. X{
  4706. X    trace(("user_ustate_lock_prepare(up = %08lX)\n{\n"/*}*/, up));
  4707. X    lock_prepare_ustate(up->uid);
  4708. X    trace((/*{*/"}\n"));
  4709. X}
  4710. X
  4711. X
  4712. X/*
  4713. X * NAME
  4714. X *    user_default_change
  4715. X *
  4716. X * SYNOPSIS
  4717. X *    long user_default_change(user_ty *up);
  4718. X *
  4719. X * DESCRIPTION
  4720. X *    The user_default_change function is used to
  4721. X *    find the default change number.  The project is taken from the
  4722. X *    user structure's notion of the set project.
  4723. X *
  4724. X * ARGUMENTS
  4725. X *    up    - pointer to user structure
  4726. X *
  4727. X * RETURNS
  4728. X *    the change number.
  4729. X *
  4730. X * CAVEAT
  4731. X *    it is a fatal error if there is no default change number
  4732. X */
  4733. X
  4734. Xstatic long is_a_change_number _((char *));
  4735. X
  4736. Xstatic long
  4737. Xis_a_change_number(s)
  4738. X    char        *s;
  4739. X{
  4740. X    long        n;
  4741. X
  4742. X    while (isspace(*s))
  4743. X        ++s;
  4744. X    n = 0;
  4745. X    while (isdigit(*s))
  4746. X        n = n * 10 + *s++ - '0';
  4747. X    if (n < 1)
  4748. X        return 0;
  4749. X    while (isspace(*s))
  4750. X        ++s;
  4751. X    if (*s)
  4752. X        return 0;
  4753. X    return n;
  4754. X}
  4755. X
  4756. X
  4757. Xstatic long project_dot_change _((string_ty *, string_ty *));
  4758. X
  4759. Xstatic long
  4760. Xproject_dot_change(s, p)
  4761. X    string_ty    *s;
  4762. X    string_ty    *p;
  4763. X{
  4764. X    if
  4765. X    (
  4766. X        s->str_length > p->str_length + 1
  4767. X    &&
  4768. X        !memcmp(s->str_text, p->str_text, p->str_length)
  4769. X    &&
  4770. X        s->str_text[p->str_length] == '.'
  4771. X    )
  4772. X        return is_a_change_number(s->str_text + p->str_length + 1);
  4773. X    return 0;
  4774. X}
  4775. X
  4776. X
  4777. Xlong
  4778. Xuser_default_change(up)
  4779. X    user_ty        *up;
  4780. X{
  4781. X    long        change_number;
  4782. X    string_ty    *s1;
  4783. X    string_ty    *s2;
  4784. X    char        *cp;
  4785. X
  4786. X    trace(("user_default_change(up = %08lX)\n{\n"/*}*/, up));
  4787. X    change_number = 0;
  4788. X
  4789. X    /*
  4790. X     * check the AEGIS_CHANGE environment variable
  4791. X     */
  4792. X    s1 = str_format("%s_change", option_progname_get());
  4793. X    s2 = str_upcase(s1);
  4794. X    str_free(s1);
  4795. X    cp = getenv(s2->str_text);
  4796. X    if (cp)
  4797. X    {
  4798. X        change_number = is_a_change_number(cp);
  4799. X        if (!change_number)
  4800. X        {
  4801. X            fatal
  4802. X            (
  4803. X        "the %s environment variable must be a positive decimal number",
  4804. X                s2->str_text
  4805. X            );
  4806. X        }
  4807. X    }
  4808. X    str_free(s2);
  4809. X
  4810. X    /*
  4811. X     * check the $HOME/.aegisrc file
  4812. X     */
  4813. X    if (!change_number)
  4814. X    {
  4815. X        uconf        uconf_data;
  4816. X
  4817. X        uconf_data = user_uconf_get(up);
  4818. X        if (uconf_data->mask & uconf_default_change_number_mask)
  4819. X            change_number = uconf_data->default_change_number;
  4820. X    }
  4821. X
  4822. X    /*
  4823. X     * If the user is only working on one change within the given
  4824. X     * project, then that is the change.
  4825. X     */
  4826. X    if (!change_number)
  4827. X    {
  4828. X        ustate        ustate_data;
  4829. X        int        j;
  4830. X
  4831. X        ustate_data = user_ustate_get(up);
  4832. X        assert(ustate_data->own);
  4833. X        for (j = 0; j < ustate_data->own->length; ++j)
  4834. X        {
  4835. X            ustate_own    own_data;
  4836. X            string_ty    *project_name;
  4837. X    
  4838. X            own_data = ustate_data->own->list[j];
  4839. X            project_name = project_name_get(up->pp);
  4840. X            if (str_equal(own_data->project_name, project_name))
  4841. X            {
  4842. X                if (own_data->changes->length == 1)
  4843. X                    change_number = own_data->changes->list[0];
  4844. X                break;
  4845. X            }
  4846. X        }
  4847. X    }
  4848. X
  4849. X    /*
  4850. X     * examine the pathname of the current directory
  4851. X     * to see if we can extract the change number
  4852. X     *
  4853. X     * This only works if the development directory was created
  4854. X     * by aegis, and not specified by the -DIRectory option.
  4855. X     * It doesn't work at all for IntDir or BL.
  4856. X     */
  4857. X    if (!change_number)
  4858. X    {
  4859. X        string_ty    *cwd;
  4860. X        wlist        part;
  4861. X        long        j;
  4862. X
  4863. X        /*
  4864. X         * get the current directory
  4865. X         */
  4866. X        os_become_orig();
  4867. X        cwd = os_curdir();
  4868. X        os_become_undo();
  4869. X        assert(cwd);
  4870. X
  4871. X        /*
  4872. X         * break it into file names
  4873. X         */
  4874. X        str2wl(&part, cwd, "/");
  4875. X        str_free(cwd);
  4876. X
  4877. X        /*
  4878. X         * search for <proj>.<num>
  4879. X         */
  4880. X        for (j = 0; j < part.wl_nwords; ++j)
  4881. X        {
  4882. X            change_number =
  4883. X                project_dot_change
  4884. X                (
  4885. X                    part.wl_word[j],
  4886. X                    project_name_get(up->pp)
  4887. X                );
  4888. X            if (change_number)
  4889. X                break;
  4890. X        }
  4891. X        wl_free(&part);
  4892. X    }
  4893. X
  4894. X    /*
  4895. X     * It is an error if no change number has been given.
  4896. X     */
  4897. X    if (!change_number)
  4898. X        project_fatal(up->pp, "no -Change option specified");
  4899. X    trace(("return %d;\n", change_number));
  4900. X    trace((/*{*/"}\n"));
  4901. X    return change_number;
  4902. X}
  4903. X
  4904. X
  4905. X/*
  4906. X * NAME
  4907. X *    user_default_project
  4908. X *
  4909. X * SYNOPSIS
  4910. X *    void user_default_project(void);
  4911. X *
  4912. X * DESCRIPTION
  4913. X *    The user_default_project function is used to
  4914. X *    determine the default project of the user who invoked the program.
  4915. X *
  4916. X * RETURNS
  4917. X *    pointer to string containing project name
  4918. X *
  4919. X * CAVEAT
  4920. X *    it is a fatal error if there is no default project name
  4921. X */
  4922. X
  4923. Xstring_ty *
  4924. Xuser_default_project()
  4925. X{
  4926. X    string_ty    *result;
  4927. X    user_ty        *up;
  4928. X    string_ty    *s1;
  4929. X    string_ty    *s2;
  4930. X    char        *cp;
  4931. X
  4932. X    /*
  4933. X     * build a temporary user
  4934. X     */
  4935. X    trace(("user_default_project()\n{\n"/*}*/));
  4936. X    up = user_executing((project_ty *)0);
  4937. X    result = 0;
  4938. X
  4939. X    /*
  4940. X     * from the AEGIS_PROJECT environment variable.
  4941. X     */
  4942. X    s1 = str_format("%s_project", option_progname_get());
  4943. X    s2 = str_upcase(s1);
  4944. X    str_free(s1);
  4945. X    cp = getenv(s2->str_text);
  4946. X    str_free(s2);
  4947. X    if (cp && *cp)
  4948. X        result = str_from_c(cp);
  4949. X
  4950. X    /*
  4951. X     * From the $HOME/.aegisrc file.
  4952. X     */
  4953. X    if (!result)
  4954. X    {
  4955. X        uconf        uconf_data;
  4956. X
  4957. X        uconf_data = user_uconf_get(up);
  4958. X        if (uconf_data->default_project_name)
  4959. X            result = str_copy(uconf_data->default_project_name);
  4960. X    }
  4961. X
  4962. X    /*
  4963. X     * check the search path, see if we use just one
  4964. X     *
  4965. X     * else check the current dirctory to see if we are within one
  4966. X     *
  4967. X     * This only works if the development directory was created
  4968. X     * by aegis, and not specified by the -DIRectory option.
  4969. X     * It doesn't work at all for IntDir or BL.
  4970. X     */
  4971. X    if (!result)
  4972. X    {
  4973. X        wlist        name;
  4974. X
  4975. X        gonzo_project_list_user(up->name, &name);
  4976. X        if (name.wl_nwords == 1)
  4977. X            result = str_copy(name.wl_word[0]);
  4978. X        else
  4979. X        {
  4980. X            string_ty    *cwd;
  4981. X            wlist        part;
  4982. X            long        j;
  4983. X            long        k;
  4984. X            
  4985. X            /*
  4986. X             * get pathname of the current directory
  4987. X             */
  4988. X            os_become_orig();
  4989. X            cwd = os_curdir();
  4990. X            os_become_undo();
  4991. X            assert(cwd);
  4992. X
  4993. X            /*
  4994. X             * break into pieces
  4995. X             */
  4996. X            str2wl(&part, cwd, "/");
  4997. X            str_free(cwd);
  4998. X
  4999. X            /*
  5000. X             * search the path
  5001. X             * looking for <proj>.<num>
  5002. X             */
  5003. X            for (j = 0; j < part.wl_nwords && !result; ++j)
  5004. X            {
  5005. X                for (k = 0; k < name.wl_nwords; ++k)
  5006. X                {
  5007. X                    if
  5008. X                    (
  5009. X                        project_dot_change
  5010. X                        (
  5011. X                            part.wl_word[j],
  5012. X                            name.wl_word[k]
  5013. X                        )
  5014. X                    )
  5015. X                    {
  5016. X                        result =
  5017. X                              str_copy(name.wl_word[k]);
  5018. X                        break;
  5019. X                    }
  5020. X                }
  5021. X            }
  5022. X            wl_free(&part);
  5023. X        }
  5024. X        wl_free(&name);
  5025. X    }
  5026. X
  5027. X    /*
  5028. X     * It is an error if no project name has been given.
  5029. X     */
  5030. X    if (!result)
  5031. X        fatal("no -Project option specified");
  5032. X
  5033. X    /*
  5034. X     * clean up and go home
  5035. X     */
  5036. X    user_free(up);
  5037. X    trace(("return \"%s\";\n", result->str_text));
  5038. X    trace((/*{*/"}\n"));
  5039. X    return result;
  5040. X}
  5041. X
  5042. X
  5043. X/*
  5044. X * NAME
  5045. X *    user_default_development_directory_get
  5046. X *
  5047. X * SYNOPSIS
  5048. X *    string_ty *user_default_development_directory_get(user_ty *up);
  5049. X *
  5050. X * DESCRIPTION
  5051. X *    The user_default_development_directory_get function is used to
  5052. X *    determine the absolute path of the user's default development directory.
  5053. X *
  5054. X * ARGUMENTS
  5055. X *    up    - pointer to user structure
  5056. X *
  5057. X * RETURNS
  5058. X *    pointer to string containing path
  5059. X *
  5060. X * CAVEAT
  5061. X *    If the user has not explicitly set one,
  5062. X *    and the project does not have one set,
  5063. X *    the user's home directory will be returned.
  5064. X */
  5065. X
  5066. Xstring_ty *
  5067. Xuser_default_development_directory(up)
  5068. X    user_ty        *up;
  5069. X{
  5070. X    uconf        uconf_data;
  5071. X    string_ty    *path;
  5072. X
  5073. X    /*
  5074. X     * To cope with automounters, directories are stored as given,
  5075. X     * or are derived from the home directory in the passwd file.
  5076. X     * Within aegis, pathnames have their symbolic links resolved,
  5077. X     * and any comparison of paths is done on this "system idea"
  5078. X     * of the pathname.
  5079. X     */
  5080. X    uconf_data = user_uconf_get(up);
  5081. X    path = uconf_data->default_development_directory;
  5082. X    if (path)
  5083. X    {
  5084. X        if (path->str_text[0] == '/')
  5085. X            path = str_copy(path);
  5086. X        else
  5087. X            path = str_format("%S/%S", user_home(up), path);
  5088. X    }
  5089. X    else
  5090. X    {
  5091. X        path = project_default_development_directory(up->pp);
  5092. X        if (!path)
  5093. X            path = user_home(up);
  5094. X        path = str_copy(path);
  5095. X    }
  5096. X    return path;
  5097. X}
  5098. X
  5099. X
  5100. X/*
  5101. X * NAME
  5102. X *    user_default_project_directory
  5103. X *
  5104. X * SYNOPSIS
  5105. X *    void user_default_project_directory(void);
  5106. X *
  5107. X * DESCRIPTION
  5108. X *    The user_default_project_directory function is used to
  5109. X *    determine the absolute path for where to place new projects.
  5110. X *
  5111. X * ARGUMENTS
  5112. X *    up    - pointer to user structure
  5113. X *
  5114. X * RETURNS
  5115. X *    pointer to string containing path
  5116. X *
  5117. X * CAVEAT
  5118. X *    if the user has not explicitly set one,
  5119. X *    the user's home directory will be returned.
  5120. X */
  5121. X
  5122. Xstring_ty *
  5123. Xuser_default_project_directory(up)
  5124. X    user_ty        *up;
  5125. X{
  5126. X    uconf        uconf_data;
  5127. X    string_ty    *path;
  5128. X
  5129. X    /*
  5130. X     * To cope with automounters, directories are stored as given,
  5131. X     * or are derived from the home directory in the passwd file.
  5132. X     * Within aegis, pathnames have their symbolic links resolved,
  5133. X     * and any comparison of paths is done on this "system idea"
  5134. X     * of the pathname.
  5135. X     */
  5136. X    trace(("user_default_project_directory(up = %08lX)\n{\n"/*}*/, up));
  5137. X    uconf_data = user_uconf_get(up);
  5138. X    path = uconf_data->default_project_directory;
  5139. X    if (path)
  5140. X    {
  5141. X        if (path->str_text[0] == '/')
  5142. X            path = str_copy(path);
  5143. X        else
  5144. X            path = str_format("%S/%S", user_home(up), path);
  5145. X    }
  5146. X    else
  5147. X        path = str_copy(user_home(up));
  5148. X    trace(("return \"%s\";\n", path->str_text));
  5149. X    trace((/*{*/"}\n"));
  5150. X    return path;
  5151. X}
  5152. X
  5153. X
  5154. X/*
  5155. X * NAME
  5156. X *    user_uid_check
  5157. X *
  5158. X * SYNOPSIS
  5159. X *    int user_uid_check(char *name);
  5160. X *
  5161. X * DESCRIPTION
  5162. X *    The user_uid_check function is used to
  5163. X *    see if the named user exists, and if they do,
  5164. X *    wether they have a non-system uid (i.e. 100 or over).
  5165. X *
  5166. X * ARGUMENTS
  5167. X *    name    - string containing name to check
  5168. X *
  5169. X * RETURNS
  5170. X *    zero if the login name is a system login,
  5171. X *    non-zero if the login name is a mortal.
  5172. X *
  5173. X * CAVEAT
  5174. X *    it is a fatal error if the user does not exist
  5175. X */
  5176. X
  5177. Xint
  5178. Xuser_uid_check(name)
  5179. X    string_ty    *name;
  5180. X{
  5181. X    struct passwd    *pw;
  5182. X
  5183. X    pw = getpwnam(name->str_text);
  5184. X    if (!pw)
  5185. X        fatal("user \"%s\" unknown", name->str_text);
  5186. X    return (pw->pw_uid >= AEGIS_MIN_UID);
  5187. X}
  5188. X
  5189. X
  5190. X/*
  5191. X * NAME
  5192. X *    user_gid_check
  5193. X *
  5194. X * SYNOPSIS
  5195. X *    int user_gid_check(char *name);
  5196. X *
  5197. X * DESCRIPTION
  5198. X *    The user_gid_check function is used to
  5199. X *    see if the named group exists, and if it does,
  5200. X *    wether it is a non-system gid (i.e. 10 or over).
  5201. X *
  5202. X * ARGUMENTS
  5203. X *    name    - string containing name to check
  5204. X *
  5205. X * RETURNS
  5206. X *    zero if the name is a system group,
  5207. X *    non-zero if the group is a mortal.
  5208. X *
  5209. X * CAVEAT
  5210. X *    it is a fatal error if the group does not exist
  5211. X */
  5212. X
  5213. Xint
  5214. Xuser_gid_check(name)
  5215. X    string_ty    *name;
  5216. X{
  5217. X    struct group    *gr;
  5218. X
  5219. X    gr = getgrnam(name->str_text);
  5220. X    if (!gr)
  5221. X        fatal("group \"%s\" unknown", name->str_text);
  5222. X    return (gr->gr_gid >= AEGIS_MIN_GID);
  5223. X}
  5224. X
  5225. X
  5226. X/*
  5227. X * NAME
  5228. X *    user_become
  5229. X *
  5230. X * SYNOPSIS
  5231. X *    void user_become(user_ty *up);
  5232. X *
  5233. X * DESCRIPTION
  5234. X *    The user_become function is used to
  5235. X *    set user and group to the given user.
  5236. X *
  5237. X * ARGUMENTS
  5238. X *    up    - pointer to user structure
  5239. X *
  5240. X * CAVEAT
  5241. X *    cancel with user_become_undo() when finished imitating this user.
  5242. X */
  5243. X
  5244. Xvoid
  5245. Xuser_become(up)
  5246. X    user_ty        *up;
  5247. X{
  5248. X    trace(("user_become(up = %08lX)\n{\n"/*}*/, up));
  5249. X    os_become(up->uid, up->gid, up->umask);
  5250. X    trace((/*{*/"}\n"));
  5251. X}
  5252. X
  5253. Xvoid
  5254. Xuser_become_undo()
  5255. X{
  5256. X    trace(("user_become_undo()\n{\n"/*}*/));
  5257. X    os_become_undo();
  5258. X    trace((/*{*/"}\n"));
  5259. X}
  5260. END_OF_FILE
  5261. if test 30882 -ne `wc -c <'aegis/user.c'`; then
  5262.     echo shar: \"'aegis/user.c'\" unpacked with wrong size!
  5263. fi
  5264. # end of 'aegis/user.c'
  5265. fi
  5266. if test -f 'doc/c7.1.so' -a "${1}" != "-c" ; then 
  5267.   echo shar: Will not clobber existing file \"'doc/c7.1.so'\"
  5268. else
  5269. echo shar: Extracting \"'doc/c7.1.so'\" \(29686 characters\)
  5270. sed "s/^X//" >'doc/c7.1.so' <<'END_OF_FILE'
  5271. X.\"
  5272. X.\"    aegis - project change supervisor
  5273. X.\"    Copyright (C) 1991, 1992, 1993 Peter Miller.
  5274. X.\"    All rights reserved.
  5275. X.\"
  5276. X.\"    This program is free software; you can redistribute it and/or modify
  5277. X.\"    it under the terms of the GNU General Public License as published by
  5278. X.\"    the Free Software Foundation; either version 2 of the License, or
  5279. X.\"    (at your option) any later version.
  5280. X.\"
  5281. X.\"    This program is distributed in the hope that it will be useful,
  5282. X.\"    but WITHOUT ANY WARRANTY; without even the implied warranty of
  5283. X.\"    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  5284. X.\"    GNU General Public License for more details.
  5285. X.\"
  5286. X.\"    You should have received a copy of the GNU General Public License
  5287. X.\"    along with this program; if not, write to the Free Software
  5288. X.\"    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  5289. X.\"
  5290. X.\" MANIFEST: User Guide, How Aegis Works, The Model
  5291. X.\"
  5292. X.nh 2 "The Model"
  5293. X.LP
  5294. XThe model of the software development process
  5295. Xused by aegis was not plucked from thin air in some ivory tower.
  5296. XIt evolved and grew with time in a real-live software development environment,
  5297. Xand it has continued to be used and developed.
  5298. XUnfortunately,
  5299. Xthis environment was the largest experienced by the author to date,
  5300. Xand consisted of only about forty software engineers hammering away
  5301. Xat a project.\**
  5302. X.FS
  5303. X"Unfortunately,"
  5304. Xbecause many real-world projects are far larger,
  5305. Xbut the author has yet to experience,
  5306. Xand understand,
  5307. Xthe issues and procedures required.
  5308. X(Think of problems,
  5309. Xyes;
  5310. Xthink of real-world solutions,
  5311. Xno.)
  5312. X.FE
  5313. X.nh 3 "The Baseline"
  5314. X.LP
  5315. XMost CASE systems revolve around a repository.
  5316. XA place where
  5317. X.I stuff
  5318. Xis kept.
  5319. XThis
  5320. X.I stuff
  5321. Xis the raw data that is chewed over to produce the final
  5322. Xproduct,
  5323. Xwhatever that may be.
  5324. XThis
  5325. X.I stuff
  5326. Xis the preferred form for
  5327. Xediting or composing or whatever.
  5328. X.LP
  5329. XIn the aegis program,
  5330. Xthe repository is known as the
  5331. X.I baseline
  5332. Xand the units of
  5333. X.I stuff
  5334. Xare 
  5335. X.UX
  5336. Xfiles.
  5337. XThe aegis program makes no distinction between
  5338. Xtext and binary files,
  5339. Xso both are supported.
  5340. X.LP
  5341. XThe history mechanism which must be included in any repository function is not
  5342. Xprovided by the aegis program.
  5343. XIt is instead provided by some other
  5344. Xper-project configurable software,
  5345. Xsuch as RCS.
  5346. XThis means that the
  5347. Xuser may select the history tool most suited to any given project.
  5348. XIt also means that aegis is that much smaller to test and maintain.
  5349. XYou will not be able to have binary files in your baseline if the
  5350. Xhistory tool of your choice can't cope with them.
  5351. X.LP
  5352. XThe structure of the baseline is dictated by the nature of each project,
  5353. Xwith some minor exceptions.
  5354. XThe aegis program attempts to make as few
  5355. Xarbitrary rules as possible.
  5356. XThere is one mandatory file in the
  5357. Xbaseline,
  5358. Xand one mandatory directory.
  5359. XThe file is called
  5360. X.I config ,
  5361. Xand
  5362. Xcontains the per-project configuration information;
  5363. Xthe directory is
  5364. Xcalled
  5365. X.I test ,
  5366. Xand contains all of the the tests.
  5367. XThe contents and structure of the
  5368. X.I test
  5369. Xdirectory are also dictated by aegis.
  5370. XTests are treated just like any other source file,
  5371. Xand are subject to the same process.
  5372. X.LP
  5373. XThe baseline in aegis has one particular attribute:
  5374. Xit always works.
  5375. XIt is
  5376. Xalways there to show off to visiting big-wigs,
  5377. Xit is always there to
  5378. Xgrab a copy of and ship a "pre-release snapshot" to some overly anxious
  5379. Xcustomer,
  5380. Xit is always there to let upper management "touch and feel"
  5381. Xthe progress being made towards the next release.
  5382. X.LP
  5383. XYou may claim that "works" is comfortably fuzzy,
  5384. Xbut it is not.
  5385. XThe baseline contains not only the source of a project,
  5386. Xbut also the tests for a project.
  5387. XTests are treated just like any other source file,
  5388. Xand are subject to the same process.
  5389. XA baseline is defined to "work" if and only if it passes all
  5390. Xof its own tests.
  5391. XThe aegis program has mandatory testing,
  5392. Xto ensure
  5393. Xthat all changes to the baseline are accompanied by tests,
  5394. Xand that
  5395. Xthose tests have been run and are known to pass.
  5396. XThis means that no
  5397. Xchange to the baseline may result in the baseline ceasing to work\**.
  5398. X.FS
  5399. XWell,
  5400. Xmostly.
  5401. XIt is possible for this restriction to be relaxed if you feel
  5402. Xthere are special circumstances for a particular change.
  5403. XThe danger is that a change
  5404. Xwill be integrated with the baseline when that change
  5405. Xis not actually of acceptable quality.
  5406. X.FE
  5407. X.LP
  5408. XThe model may be summarised briefly:
  5409. Xit consists of a
  5410. X.I baseline
  5411. X(master source),
  5412. Xupdated through the agency of an
  5413. X.I integrator ,
  5414. Xwho is in turn fed
  5415. X.I changes
  5416. Xby a team of
  5417. X.I developers .
  5418. XThese terms will be explained in the following sections.
  5419. XSee figure 1 for a picture of how files flow around the system.
  5420. X.KF
  5421. X.PS
  5422. Xdown
  5423. XB1: box "baseline"
  5424. Xmove left down
  5425. XB2: box "development" "directory"
  5426. Xmove right down
  5427. XE1: ellipse "integrator"
  5428. Xarrow "integrate " rjust "begin " rjust
  5429. XB3: box "integration" "directory"
  5430. XS1: spline -> from B3.s then down then right 1.25 then up then to B1.n right 1.25 then to B1.n up right 1.25 then to B1.n up then to B1.n
  5431. X"integrate " rjust "pass " rjust at S1.c+(1.25,-0.75)
  5432. Xarrow from B2.s to E1.nw
  5433. Xarrow from 1/3<B1.sw,B1.se> to B2.n
  5434. X
  5435. Xmove to B1.s
  5436. Xmove right down
  5437. XB4: box "development" "directory"
  5438. Xarrow from 1/3<B1.se,B1.sw> to B4.n
  5439. Xarrow from B4.s to E1.ne
  5440. X.PE
  5441. X.ce 1
  5442. X\fBFigure 1:\fP Flow of Files through the Model
  5443. X.sp
  5444. X\|
  5445. X.KE
  5446. X.LP
  5447. XThe baseline is a set of files
  5448. Xincluding the source files for a projects,
  5449. Xand also all derived files (such as generated code,
  5450. Xbinary files from the compiler,
  5451. Xetc),
  5452. Xand all of the tests.
  5453. XTests are treated just like any other source file,
  5454. Xand are subject to the same process.
  5455. XAll files in the baseline are consistent with each other.
  5456. X.LP
  5457. XThus the baseline may be considered to the the 
  5458. X.I closure
  5459. Xof the source files,
  5460. Xin mathematical terms.
  5461. XThat is,
  5462. Xit is the source files
  5463. Xand all implications flowing from those source files,
  5464. Xsuch as object files and executables.
  5465. XAll files in the baseline are consistent with each other;
  5466. Xthis means that development builds may expect to be able to take
  5467. Xobjects files from the baseline rather than rebuild them
  5468. Xwithin the development directory.
  5469. X.LP
  5470. XThe baseline is readable by all staff,
  5471. Xand usually writable by none.
  5472. X.LP
  5473. XIn many ways,
  5474. Xthe baseline may be thought of as a database,
  5475. Xand all derived files are projections (views) of the source files.
  5476. XPassing its own tests may be thought of as input validation of fields.
  5477. XThis is a powerful concept,
  5478. Xand indeed the implementation of the aegis program performs many
  5479. Xof the locking and synchronization tasks demanded of a database
  5480. Xengine.
  5481. X.LP
  5482. XAll of the files forming this database are text files.
  5483. XThis means that they may be repaired with an ordinary text editor,
  5484. Xshould remedial action be necessary.
  5485. XThe format is documented in section 5 of the reference manual.
  5486. XShould you wish to perform some query not yet available in aegis,
  5487. Xthe files are readily accessible to members of the appropriate
  5488. X.UX
  5489. Xgroup.
  5490. X.LP
  5491. XTests are treated just like any other source file,
  5492. Xand are subject to the same process.
  5493. X.nh 3 "The Change Mechanism"
  5494. X.LP
  5495. XAny changes to the baseline are made by atomic increments,
  5496. Xknown (unoriginally)
  5497. Xas "changes".
  5498. XA change is a collection of files to be added to,
  5499. Xmodified in,
  5500. Xor deleted from,
  5501. Xthe baseline.
  5502. XThese files must all be so altered
  5503. Xsimultaneously for the baseline to continue to "work".\**
  5504. X.FS
  5505. XWhether to allow several logically independent changes to be included in the
  5506. Xone change is a policy decision for individual projects to make,
  5507. Xand is not dictated by the aegis program.
  5508. XIt is a responsibility of reviewers
  5509. Xto ensure that all new and changed functionality is tested and
  5510. Xdocumented.
  5511. X.FE
  5512. X.LP
  5513. XFor example,
  5514. Xif the calling interface to a function were changed in one file,
  5515. Xall calls to that function in any other file must also change for the
  5516. Xbaseline to continue to work.
  5517. XAll of the files must be changed simultaneously,
  5518. Xand thus must all be included in the one change.
  5519. XOther
  5520. Xfiles which would logically be included in such an change include the
  5521. Xreference manual entry for the function,
  5522. Xthe design document relating
  5523. Xto that area of functionality,
  5524. Xthe relevant user documentation,
  5525. Xtests
  5526. Xwould have to be included for the functionality,
  5527. Xand existing tests may
  5528. Xneed to be revised.
  5529. X.LP
  5530. XChanges must be accompanied by tests.
  5531. XThese tests will either establish that a bug has been fixed
  5532. X(in the case of a bug fix)
  5533. Xor will establish that new functionality works
  5534. X(in the case of an enhancement).
  5535. X.LP
  5536. XTests are shell scripts,
  5537. Xand as such are capable of testing anything which has functionality
  5538. Xaccessable from the command line.
  5539. Xthe ability to run background processes allows even client-server
  5540. Xmodels to be tested.
  5541. XTests are thus text files, and are treated as source files;
  5542. Xthey may be modified by the same process as any other source file.
  5543. XTests usually need to be revised as a project grows and adapts
  5544. Xto changing requirements,
  5545. Xor to be extended as functionality is extended.
  5546. XTests can even be deleted if the functionality they tests has been deleted;
  5547. Xtests are deleted by the same process as any other source file.
  5548. X.nh 3 "Change States"
  5549. X.LP
  5550. XAs a change is developed using aegis,
  5551. Xit passes through six states.
  5552. XMany aegis
  5553. Xcommands relate to transitions between these states,
  5554. Xand aegis performs
  5555. Xany validation at these times.
  5556. X.LP
  5557. XThe six states of a change are described as follows,
  5558. Xalthough the various state
  5559. Xtransitions,
  5560. Xand their conditions,
  5561. Xwill be described later.
  5562. X.nh 4 "Awaiting Development"
  5563. X.LP
  5564. XA change is in this state after it has been created,
  5565. Xbut before it has been assigned to a developer.
  5566. XThis state can't be skipped:
  5567. Xa change can't be immediately assigned to a developer
  5568. Xby an administrator,
  5569. Xbecause this disempowers the staff.
  5570. X.LP
  5571. XThe aegis program is not a progress tracking tool,
  5572. Xnor is it a work scheduling tool;
  5573. Xplenty of both already exist.
  5574. X.nh 4 "Being Developed"
  5575. X.LP
  5576. XA change is in this state after it has been assigned to a developer,
  5577. Xby the developer.
  5578. XThis is the coal face:
  5579. Xall development is carried out in this state.
  5580. XFiles can be edited in no other state,
  5581. Xthis particularly means that only developers can develop,
  5582. Xreviewers and integrators only have the power to veto a change.
  5583. XStaff roles will be described more fully in a later section.
  5584. X.LP
  5585. XTo advance to the next state,
  5586. Xthe change must build successfully,
  5587. Xit must have tests,
  5588. Xand it must pass those tests.\**
  5589. X.FS
  5590. XIt is possible for these testing requirements to be waived on either a
  5591. Xper-project or per-change basis.
  5592. XHow is described in a later section.
  5593. XThe power to waive this requirement is not automatically granted to developers,
  5594. Xas experience has shown that it is usually abused.
  5595. X.FE
  5596. X.LP
  5597. XThe new tests must also
  5598. X.I fail
  5599. Xagainst the baseline;
  5600. Xthis is to establish that tests for bug-fixes actually reproduce the bug
  5601. Xand then demonstrate that it is gone.
  5602. XNew functionality added by a change
  5603. Xwill naturally fail when tested in the old baseline,
  5604. Xbecause it is not there.
  5605. X.LP
  5606. XWhen these conditions are met,
  5607. Xthe aegis program marks all of the changes files as locked,
  5608. Xsimultaneously.
  5609. XIf any one of them is already locked,
  5610. Xyou can't leave the
  5611. X.I "being developed"
  5612. Xstate,
  5613. Xbecause the file is part of a change which is somewhere
  5614. Xbetween
  5615. X.I "being reviewed"
  5616. Xand
  5617. X.I "being integrated" .
  5618. X.LP
  5619. XIf any one of them is out-of-date with respect to the baseline,
  5620. Xthe lock is not taken,
  5621. Xeither.
  5622. XLocking the files at this state transition means that popular files may be
  5623. Xmodified simultaneously in many changes,
  5624. Xbut that only differences to the latest version are ever submitted for
  5625. Xintegration.
  5626. XThe aegis program provides a mechanism,
  5627. Xdescribed later,
  5628. Xfor bringing out-of-date files in changes up-to-date without losing the
  5629. Xedits made by the developer.
  5630. X.nh 4 "Being Reviewed"
  5631. X.LP
  5632. XA change is in this state after a developer has indicated that development is
  5633. Xcomplete.
  5634. XThe change is inspected,
  5635. Xusually by a second party (or parties),
  5636. Xto ensure that it matches the  what it is meant to be doing,
  5637. Xand meets other project or company standards you may have.
  5638. X.LP
  5639. XThe style of review,
  5640. Xand who may review,
  5641. Xis not dictated by the aegis program.
  5642. XA number of alternative have been observed:
  5643. X.LP
  5644. X\(bu 
  5645. XYou may have a single person who coordinates review panels of,
  5646. Xsay,
  5647. X4 peers,
  5648. Xwith this coordinator the only person allowed to sign-off review passes or
  5649. Xfails.
  5650. X.LP
  5651. X\(bu
  5652. XYou may allow any of the developers to review any other developer's changes.
  5653. X.LP
  5654. X\(bu
  5655. XYou may require that only senior staff,
  5656. Xfamiliar with large portions of the code,
  5657. Xbe allowed to review.
  5658. X.LP
  5659. XThe aegis program enforces that a developer may not review their own code.
  5660. XThis ensures that at least one person other than the developer
  5661. Xhas scrutinized the code,
  5662. Xand eliminates a rather obvious conflict of interest.
  5663. XIt is possible to turn this requirement off on a per-project basis,
  5664. Xbut this is only desirable for projects with a one person team (or maybe two).
  5665. XThe aegis program has no way of knowing that the user
  5666. Xpassing a review has actually looked at,
  5667. Xand understood,
  5668. Xthe code.
  5669. X.LP
  5670. XThe reviewer knows certain things about a change for it to reach this state:
  5671. Xit has passed all of the conditions required to reach this state.
  5672. XThe change compiles,
  5673. Xit has tests and it passes those tests,
  5674. Xand the changes are to the current version of the baseline.
  5675. XThe reviewer may thus concentrate on issues of completeness,
  5676. Xdesign,
  5677. Xand standards - to name only a few.
  5678. XCheck lists can be very helpful.
  5679. X.nh 4 "Awaiting Integration"
  5680. X.LP
  5681. XA change is in this state after a reviewer has indicated that a change is
  5682. Xacceptable to the reviewer(s).
  5683. XThis is essentially a queue,
  5684. Xas there may be many developers,
  5685. Xbut only one integration may proceed at any one time.
  5686. X.LP
  5687. XThe issue of one integration at a time is a philosophical one:
  5688. Xall of the changes in the queue are physically independent;
  5689. Xbecause of the
  5690. X.I "Develop End"
  5691. Xlocking rules they do not have
  5692. Xintersecting sets of files.
  5693. XThe problem comes when one change would break another,
  5694. Xin these cases the integrator needs to know which to bounce
  5695. Xand which to accept.
  5696. XIntegrating one change at a time greatly simplifies this,
  5697. Xand enforces the "only change one thing at a time" maxim,
  5698. Xoccasionally at the expense of integrator throughput.
  5699. X.nh 4 "Being Integrated"
  5700. X.LP
  5701. XA change is in this state when the integration of the change back into the
  5702. Xbaseline is commenced.
  5703. XA (logical) copy of the baseline is taken,
  5704. Xand the change is applied to that copy.
  5705. XIn this state,
  5706. Xthe change is compiled and tested once again.
  5707. X.LP
  5708. XThe additional compilation has two purposes:
  5709. Xit ensures that the successful compile performed by the developer was
  5710. Xnot a fluke of the developer's environment,
  5711. Xand it also allows the baseline to be the closure of the sources files.
  5712. XThat is,
  5713. Xall of the implications flowing from the source files,
  5714. Xsuch as object files and linked programs or libraries.
  5715. XIt is not possible for aegis to know which files these are
  5716. Xin the development directory,
  5717. Xbecause aegis is decoupled from the build
  5718. Xmechanism (this will discussed later).
  5719. X.LP
  5720. XTo advance to the next state,
  5721. Xthe integration copy must have been compiled,
  5722. Xand the tests included in the change must have been run and passed.
  5723. X.LP
  5724. XThe integrator also has the power of veto.
  5725. XA change may fail an integration because it fails to build or fails tests,
  5726. Xand also just because the integrator says so.
  5727. XThis allows the
  5728. X.I "being integrated"
  5729. Xstate to be another review state,
  5730. Xif desired.
  5731. XThe
  5732. X.I "being integrated"
  5733. Xstate is also the place to monitor the quality
  5734. Xof reviews and reviewers.
  5735. X.LP
  5736. XShould a faulty change manage to reach this point,
  5737. Xit is to be hoped that the integration process,
  5738. Xand the integrator's sharp eyes,
  5739. Xwill detect it.
  5740. X.LP
  5741. XWhile most of this task is automated,
  5742. Xthis step is necessary to ensure that some strange
  5743. Xquirk of the developer's environment was not
  5744. Xresponsible for the change reaching this stage.
  5745. XThe change is built once more,
  5746. Xand tested once more.
  5747. XIf a change fails to build or test,
  5748. Xit is returned to the developer for further work;
  5749. Xthe integrator may also choose to fail it for other reasons.
  5750. XIf the integrator passes that change,
  5751. Xthe integrated version becomes the new baseline.
  5752. X.nh 4 "Completed"
  5753. X.LP
  5754. XA change reaches this state when integration is complete.
  5755. XThe (logical) copy of the baseline used during integration has replaced the
  5756. Xprevious copy of the baseline,
  5757. Xand the file histories have been updated.
  5758. XOnce in this state,
  5759. Xa change may never leave it,
  5760. Xunlike all other states.
  5761. X.LP
  5762. XIf you wish to remove a change
  5763. Xwhich is in this state
  5764. Xfrom the baseline,
  5765. Xyou will have to submit another change.
  5766. X.nh 3 "The Software Engineers"
  5767. X.LP
  5768. XThe model of software development used by aegis has four different roles
  5769. Xfor software engineers to fill.
  5770. XThese four roles may be overlapping sets of people,
  5771. Xor be distinct,
  5772. Xas appropriate for your project.
  5773. X.nh 4 "Developer"
  5774. X.LP
  5775. XThis is the coal-face.
  5776. XThis role is where almost everything is done.
  5777. XThis is
  5778. Xthe only role allowed to edit a source file of a project.
  5779. X.LP
  5780. XMost staff will be developers.
  5781. XThere is nothing stopping a developer from also
  5782. Xbeing an administrator,
  5783. Xexcept for the possible conflict of interests
  5784. Xwith respect to testing exemptions.
  5785. X.LP
  5786. XA developer may edit many of the attributes of a change while it is being
  5787. Xdeveloped.
  5788. XThis is mostly useful to update the description of the
  5789. Xchange to say why it was done and what was actually done.
  5790. XA developer
  5791. Xmay not grant testing exemptions (but they may be relinquished).
  5792. X.nh 4 "Reviewer"
  5793. X.LP
  5794. XThe role of the reviewer is to check a developer's work.
  5795. XThis review may
  5796. Xconsist of a peer examining the code,
  5797. Xor it may be handled by a single
  5798. Xmember of staff setting up and scheduling multi-person review panels.
  5799. XThe aegis program does not mandate what style of review,
  5800. Xit only
  5801. Xrequires that a reviewer pass or fail each change.
  5802. XIf it passes,
  5803. Xan
  5804. Xintegrator will handle it next,
  5805. Xotherwise it is returned to the
  5806. Xdeveloper for further work.
  5807. X.LP
  5808. XIn a large team,
  5809. Xthe reviewers are usually selected from the more senior
  5810. Xmembers of the team,
  5811. Xbecause of their depth of experience at spotting
  5812. Xproblems,
  5813. Xbut also because this is an opportunity for more senior
  5814. Xmembers of staff to coach juniors on the finer points of the art.
  5815. X.LP
  5816. XThe aegis programs makes some of the reviewer's task easier,
  5817. Xbecause the
  5818. Xreviewer knows several specific things about a change before it comes
  5819. Xup for review:
  5820. Xit builds,
  5821. Xit has tests,
  5822. Xand they have run successfully.
  5823. XThere is also optional (per project) additional conditions imposed at
  5824. Xthe end of development,
  5825. Xsuch as line length limits,
  5826. Xor anything else
  5827. Xwhich is automatically testable.
  5828. XThe aegis program also provides a
  5829. Xdifference listing to the reviewer,
  5830. Xso that each and every edit,
  5831. Xto each and every file,
  5832. Xcan be pointed out to the reviewer.
  5833. X.LP
  5834. XThere is nothing stopping a reviewer from being either an administrator or a
  5835. Xdeveloper.
  5836. XThe aegis program specifically prevents a developer from
  5837. Xreviewing his own work,
  5838. Xto avoid conflicts of interest.
  5839. X(It is possible for this
  5840. Xrestriction to be waived,
  5841. Xbut that only makes sense for one person
  5842. Xprojects.)
  5843. X.LP
  5844. XIt will occasionally be necessary to arbitrate between a developer and a
  5845. Xreviewer.
  5846. XThe appropriate person to do this would have line
  5847. Xresponsibility above both staff involved.
  5848. XThus it is desirable that
  5849. Xsupervisors/managers not be reviewers,
  5850. Xexcept in very small teams.
  5851. X.nh 4 "Integrator"
  5852. X.LP
  5853. XThe role of the integrator is to take a change which has already been reviewed
  5854. Xand integrate it with the baseline,
  5855. Xto form a new baseline.
  5856. XThe
  5857. Xintegrator is thus the last line of defence for the baseline.
  5858. X.LP
  5859. XThere is nothing preventing an administrator from being an administrator,
  5860. Xa
  5861. Xdeveloper or a reviewer.
  5862. XThe aegis program specifically prevents a
  5863. Xdeveloper or reviewer from integrating his own work,
  5864. Xeliminating any conflict of interests.
  5865. X(It is possible for this restriction to be waived,
  5866. Xbut that
  5867. Xonly makes sense for one and two person projects.)
  5868. X.LP
  5869. XIt will occasionally be necessary to arbitrate between an integrator and a
  5870. Xreviewer and/or a developer.
  5871. XThe appropriate person to do this would
  5872. Xhave line responsibility above all of the staff involved.
  5873. XThus it is
  5874. Xdesirable that supervisors/mangers not be integrators,
  5875. Xexcept in very
  5876. Xsmall teams.
  5877. X.LP
  5878. XThe baseline is readable by all developers,
  5879. Xbut not writable.
  5880. XAll updates of the baseline to reflect changes produced by developers
  5881. Xare performed through the agency of the integrator.
  5882. X.nh 4 "Administrator"
  5883. X.LP
  5884. XThe project administrator has the following duties:
  5885. X.LP
  5886. X\(bu Create new changes.
  5887. XThese may be the result of some customer bug
  5888. Xreporting mechanism,
  5889. Xit may be the result of new functionality being
  5890. Xrequested.
  5891. X.LP
  5892. X\(bu Grant testing exemptions.
  5893. XBy default,
  5894. Xaegis insists that all changes be
  5895. Xaccompanied by tests.
  5896. XThe project administrator may grant case-by-case
  5897. Xexemptions,
  5898. Xor a project-wide exemption.
  5899. X.LP
  5900. X\(bu Add or remove staff.
  5901. XThe four roles described in this section may be
  5902. Xassigned to,
  5903. Xor removed from,
  5904. Xspecific 
  5905. X.UX
  5906. Xlogins by the project
  5907. Xadministrator.
  5908. X.LP
  5909. X\(bu Edit project attributes.
  5910. XThere are many attributes attached to a project,
  5911. Xonly a project administrator may alter them.
  5912. X.LP
  5913. X\(bu Edit change attributes.
  5914. XThere are many attributes attached to a change,
  5915. Xonly a project administrator may alter all of them.
  5916. X.LP
  5917. XA project usually has only one or two administrators at any one time.
  5918. X.nh 3 "The Change Process"
  5919. X.LP
  5920. XThis section will examine the progression of a change through the six change
  5921. Xstates.
  5922. XMost of the attention will be given to the conditions which
  5923. Xmust be met in order to progress from one state to the next,
  5924. Xas this is
  5925. Xwhere the software development model employed by aegis is most often
  5926. Xexpressed.
  5927. X.LP
  5928. XSee figure 2 for a picture of how all of the states and
  5929. Xtransitions fit together.
  5930. X.KF
  5931. X.PS
  5932. Xboxwid = 1
  5933. Xdown
  5934. XS0: arrow " new" ljust " change" ljust
  5935. XS1: box "awaiting" "development"
  5936. Xarrow " develop" ljust " begin" ljust
  5937. XS2: box ht 1 "being" "developed"
  5938. Xarrow " develop" ljust " end" ljust
  5939. XS3: box "being" "reviewed"
  5940. Xarrow " review" ljust " pass" ljust
  5941. XS4: box "awaiting" "integration"
  5942. Xarrow " integrate" ljust " begin" ljust
  5943. XS5: box "being" "integrated"
  5944. Xarrow " integrate" ljust " pass" ljust
  5945. XS6: box "completed"
  5946. Xspline -> from 1/3<S1.nw,S1.sw> then left 0.75 then up 2/3 "new" "change" "undo"
  5947. XT1: spline -> from 1/2<S2.nw,S2.w> then left 0.75 then up 11/12 \
  5948. X    then to 1/3<S1.sw,S1.nw>
  5949. X" develop" ljust " begin" ljust " undo" ljust at T1.c - (0.75,0)
  5950. XT2: spline -> from S3.w then left 0.5 then up 1 \
  5951. X    then to 1/2<S2.sw,S2.w>
  5952. X" develop" ljust " end" ljust " undo" ljust at T2.c - (0.5,0)
  5953. XT3: spline -> from 1/3<S4.nw,S4.sw> then left 1 then up 2.25-1/12 \
  5954. X    then to S2.w
  5955. X"" "" " develop" ljust " end" ljust " undo" ljust at T3.c - (1,0)
  5956. XT4: spline -> from S5.w then left 0.75 then up 11/12 then to 1/3<S4.sw,S4.nw>
  5957. X" integrate" ljust " begin" ljust " undo" ljust at T4.c - (0.75,0)
  5958. XT5: spline -> from 1/3<S3.ne,S3.se> then right 0.5 then up 1 then to 1/3<S2.se,S2.ne>
  5959. X"review " rjust "fail " rjust at T5.c + (0.5,0)
  5960. XT6: spline -> from S4.e then right 0.5 then up 11/12 then to 1/3<S3.se,S3.ne>
  5961. X"review " rjust "pass " rjust "undo " rjust at T6.c + (0.5,0)
  5962. XT7: spline -> from S5.e then right 1 then up 3.5-1/12 then to 1/3<S2.ne,S2.se>
  5963. X"integrate " rjust "fail " rjust "" "" at T7.c + (1,0)
  5964. X.PE
  5965. X.ce 1
  5966. X\fBFigure 2:\fP Change States and Transitions
  5967. X.sp 2
  5968. X.KE
  5969. X.nh 4 "New Change"
  5970. X.LP
  5971. XA project administrator creates a change.
  5972. XThis change will consist mostly of a
  5973. Xdescription at this time.
  5974. XThe project administrator is not able
  5975. X(through aegis) to assign it to a specific developer.
  5976. X.LP
  5977. XThe change is awaiting development;
  5978. Xit is in the awaiting development state.
  5979. X.nh 4 "New Change Undo"
  5980. X.LP
  5981. XIt is possible to abandon a change if it is in the
  5982. X.I "awaiting development"
  5983. Xstate.
  5984. XAll record of the change,
  5985. Xincluding its description,
  5986. Xwill be deleted.
  5987. X.LP
  5988. XIt is called
  5989. X.I "new change undo"
  5990. Xto emphasize the state it must be in
  5991. Xto delete it.
  5992. X.nh 4 "Develop Begin"
  5993. X.LP
  5994. XA developer,
  5995. Xfor whatever reason,
  5996. Xscans the list of changes awaiting
  5997. Xdevelopment.
  5998. XHaving selected a change,
  5999. Xthe developer then assigns
  6000. Xthat change to herself.
  6001. X.LP
  6002. XThe change is now being developed;
  6003. Xit is in the being developed state.
  6004. X.LP
  6005. XA number of aegis commands only work in this state,
  6006. Xincluding commands to
  6007. Xinclude files and tests in the change (be they new files to be added to
  6008. Xthe baseline,
  6009. Xfiles in the baseline to be modified,
  6010. Xor files to be
  6011. Xdeleted from the baseline),
  6012. Xcommands to build the change,
  6013. Xcommands to
  6014. Xtest the change,
  6015. Xand commands to difference the change.
  6016. X.LP
  6017. XThe process of taking sources files,
  6018. Xthe preferred form for editing of a
  6019. Xproject,
  6020. Xand transforming them,
  6021. Xthrough various manipulations and
  6022. Xtranslations,
  6023. Xinto a "finished" product is known as building.
  6024. XIn
  6025. Xthe 
  6026. X.UX
  6027. Xworld this usually means things like compiling and linking a
  6028. Xprogram,
  6029. Xbut as fancy graphical programs become more wide-spread,
  6030. Xthe
  6031. Xsource files could a binary output from a graphical
  6032. XEntity-Relationship-Diagram editor,
  6033. Xwhich would then be run through a
  6034. Xdatabase schema generator.
  6035. X.LP
  6036. XThe process of testing a change has three aspects.
  6037. XThe most intuitive is that
  6038. Xa test must be run to determine of the functionality works.
  6039. XThe second
  6040. Xrequirement is that the test be run against the baseline and fail;
  6041. Xthis
  6042. Xis to ensure that bugs are not just fixed,
  6043. Xbut reproduced as well.
  6044. XThe
  6045. Xthird requirement is optional:
  6046. Xall or some of the tests already in the
  6047. Xbaseline may also be run.
  6048. XTests consist of 
  6049. X.UX
  6050. Xshell scripts -
  6051. Xanything that can be done in a shell script can be tested.
  6052. X.LP
  6053. XIn preparation for review,
  6054. Xa change is differenced.
  6055. XThis usually consists of
  6056. Xautomatically comparing the present contents of the baseline with what
  6057. Xthe change proposes to do to the baseline,
  6058. Xon a file-by-file basis.
  6059. XThe
  6060. Xresults of the difference,
  6061. Xsuch as 
  6062. X.UX
  6063. X.I "diff -c"
  6064. Xoutput,
  6065. Xis kept
  6066. Xin a difference file,
  6067. Xfor examination by the reviewer(s).
  6068. XThe benefit
  6069. Xof this procedure is that reviewers may examine these file to see every
  6070. Xchange the developer made,
  6071. Xrather than only the obvious ones.
  6072. XThe
  6073. Xdifferencing commands are per-project configurable,
  6074. Xand other
  6075. Xvalidations,
  6076. Xsuch as line length restrictions,
  6077. Xmay also be imposed at
  6078. Xthis time.
  6079. X.LP
  6080. XTo leave this state,
  6081. Xthe change must have source files,
  6082. Xit must have tests,
  6083. Xit
  6084. Xmust have built successfully,
  6085. Xit must have passed all its own tests,
  6086. Xand
  6087. Xit must have been differenced.
  6088. X.nh 4 "Develop Begin Undo"
  6089. X.LP
  6090. XIt is possible to return a change from the being developed state to the
  6091. Xawaiting development state if it has no source files and has no tests.
  6092. XThis is usually desired if a developer selected the wrong
  6093. Xchange by mistake.
  6094. X.nh 4 "Develop End"
  6095. X.LP
  6096. XWhen the conditions for the end of development
  6097. Xhave been met (the change must have source files,
  6098. Xit must have tests,
  6099. Xit must have built successfully,
  6100. Xit must have passed all its own tests,
  6101. Xand it must have been
  6102. Xdifferenced) the developer may cause the change to leave the being
  6103. Xdeveloped state and enter the being reviewed state.
  6104. XThe aegis program
  6105. Xwill check to see that all the conditions are met at this time.
  6106. XThere
  6107. Xis no history kept of unsuccessful develop end attempts.
  6108. X.nh 4 "Develop End Undo"
  6109. X.LP
  6110. XThere are many times when a developer thinks that a change is completed,
  6111. Xand goes hunting for a reviewer.
  6112. XHalf way down the hall,
  6113. Xshe thinks of something that should have been included.
  6114. X.LP
  6115. XIt is possible for a developer to rescind a
  6116. X.I "Develop End"
  6117. Xto allow further work on a change.
  6118. XNo reason need be given.
  6119. XThis request may be issued to a change in either the
  6120. X.I "being reviewed"
  6121. Xor
  6122. X.I "awaiting integration"
  6123. Xstates.
  6124. X.nh 4 "Review Pass"
  6125. X.LP
  6126. XThis event is used to notify aegis that the change has been examined,
  6127. Xby a method unspecified as discussed above,
  6128. Xand has been found to be acceptable.
  6129. X.nh 4 "Review Pass Undo"
  6130. X.LP
  6131. XThe reviewer of a change may rescind a
  6132. X.I "Review Pass"
  6133. Xwhile the change remains in the
  6134. X.I "awaiting integration"
  6135. Xstate.
  6136. XNo reason must be supplied.
  6137. XThe change will be returned to the
  6138. X.I "being reviewed"
  6139. Xstate.
  6140. X.nh 4 "Review Fail"
  6141. X.LP
  6142. XThis event is used to notify aegis that the change has been examined,
  6143. Xby a method unspecified as discussed above,
  6144. Xand has been found to be unacceptable.
  6145. X.LP
  6146. XA file containing a brief summary of the problems must be given,
  6147. Xand will be included in the change's history.
  6148. X.LP
  6149. XThe change will be returned to the
  6150. X.I "being developed"
  6151. Xstate for further work.
  6152. X.LP
  6153. XIt is not the responsibility of any reviewer to fix a defective change.
  6154. X.nh 4 "Integrate Begin"
  6155. X.LP
  6156. XThis command is used to
  6157. Xcommence integration of a change into the project baseline.
  6158. X.LP
  6159. XWhether a physical copy of the baseline is used,
  6160. Xor a logical copy using hard links,
  6161. Xis controlled by the project configuration file.
  6162. XThe change is then applied to this copy.
  6163. X.LP
  6164. XThe integrator must then issue build and test commands as appropriate.
  6165. XThis is not automated as some integrator tasks may be required in and around
  6166. Xthese commands.
  6167. X.nh 4 "Integrate Begin Undo"
  6168. X.LP
  6169. XThis command is used to
  6170. Xreturn a change to the integration queue,
  6171. Xwith out prejudice.
  6172. XNo reason need be given.
  6173. X.LP
  6174. XThis is usually done when a particularly important change is in the queue,
  6175. Xand the current integration is expected to take a long time.
  6176. X.nh 4 "Integrate Pass"
  6177. X.LP
  6178. XThis command is used to
  6179. Xnotify aegis that the change being integrated is acceptable.
  6180. X.LP
  6181. XThe current baseline is replaced with the integration copy,
  6182. Xand the history is updated.
  6183. X.nh 4 "Integrate Fail"
  6184. X.LP
  6185. XThis command is used to
  6186. Xnotify aegis that an integration is unacceptable,
  6187. Xusually because it failed to build or test in some way,
  6188. Xor sometimes because the integrator found a deficiency.
  6189. X.LP
  6190. XA file containing a
  6191. X.I brief
  6192. Xsummary of the problems must be given,
  6193. Xand the summary will be included in the change's history.
  6194. X.LP
  6195. XThe change will be returned to the
  6196. X.I "being developed"
  6197. Xstate for further work.
  6198. XThe integration copy of the baseline is deleted,
  6199. Xleaving the original baseline unchanged.
  6200. X.LP
  6201. XIt is not the responsibility of any integrator to fix a defective change,
  6202. Xor even diagnose what the defect may be.
  6203. END_OF_FILE
  6204. if test 29686 -ne `wc -c <'doc/c7.1.so'`; then
  6205.     echo shar: \"'doc/c7.1.so'\" unpacked with wrong size!
  6206. fi
  6207. # end of 'doc/c7.1.so'
  6208. fi
  6209. echo shar: End of archive 14 \(of 19\).
  6210. cp /dev/null ark14isdone
  6211. MISSING=""
  6212. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; do
  6213.     if test ! -f ark${I}isdone ; then
  6214.     MISSING="${MISSING} ${I}"
  6215.     fi
  6216. done
  6217. if test "${MISSING}" = "" ; then
  6218.     echo You have unpacked all 19 archives.
  6219.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  6220. else
  6221.     echo You still need to unpack the following archives:
  6222.     echo "        " ${MISSING}
  6223. fi
  6224. ##  End of shell archive.
  6225. exit 0
  6226.