home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OS9000 / APPS / rcs.lzh / rcs1 / ci.c < prev    next >
Text File  |  1996-04-20  |  45KB  |  1,208 lines

  1. /* Copyright (C) 1982, 1988, 1989 Walter Tichy
  2.    Distributed under license by the Free Software Foundation, Inc.
  3.  
  4. This file is part of RCS.
  5.  
  6. RCS is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 1, or (at your option)
  9. any later version.
  10.  
  11. RCS is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with RCS; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  19.  
  20. Report problems and direct all questions to:
  21.  
  22.     rcs-bugs@cs.purdue.edu
  23.  
  24. */
  25.  
  26. /*
  27.  *                     RCS checkin operation
  28.  */
  29. #ifndef lint
  30.  static char rcsid[]=
  31.  "$Id: ci.c_v 1.2 96/04/06 23:42:49 hiro Exp $ Purdue CS";
  32. #endif
  33. /*******************************************************************
  34.  *                       check revisions into RCS files
  35.  *******************************************************************
  36.  */
  37.  
  38.  
  39.  
  40. /* $Log:    ci.c_v $
  41.  * Revision 1.2  96/04/06  23:42:49  hiro
  42.  * Ported to OS-9000.
  43.  * 
  44.  * Revision 1.1  93/04/02  01:14:45  hiro
  45.  * Initial revision
  46.  * 
  47.  * Revision 1.1  93/03/24  02:48:06  hiro11
  48.  * Initial revision
  49.  * 
  50.  * Revision 1.3  90/07/27  01:37:52  momo
  51.  * minor change
  52.  * 
  53.  * Revision 1.2  90/07/23  10:22:13  momo
  54.  * Minor change
  55.  * 
  56.  * Revision 1.1  90/07/19  15:19:20  momo
  57.  * Initial revision
  58.  * 
  59.  * revision 5.1 koya 90/01/24 06:10:56
  60.  * Initial revision
  61.  * 
  62.  * Revision 4.9  89/05/01  15:10:54  narten
  63.  * changed copyright header to reflect current distribution rules
  64.  * 
  65.  * Revision 4.8  88/11/08  13:38:23  narten
  66.  * changes from root@seismo.CSS.GOV (Super User)
  67.  * -d with no arguments uses the mod time of the file it is checking in
  68.  * 
  69.  * Revision 4.7  88/11/08  10:59:04  narten
  70.  * changes from eggert
  71.  * 
  72.  * Revision 4.7  88/08/09  19:12:07  eggert
  73.  * Make sure workfile is a regular file; use its mode if RCSfile doesn't have one.
  74.  * Use execv(), not system(); allow cc -R; remove lint.
  75.  * isatty(fileno(stdin)) -> ttystdin()
  76.  * 
  77.  * Revision 4.6  87/12/18  11:34:41  narten
  78.  * lint cleanups (from Guy Harris)
  79.  * 
  80.  * Revision 4.5  87/10/18  10:18:48  narten
  81.  * Updating version numbers. Changes relative to revision 1.1 are actually
  82.  * relative to 4.3
  83.  * 
  84.  * Revision 1.3  87/09/24  13:57:19  narten
  85.  * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  86.  * warnings)
  87.  * 
  88.  * Revision 1.2  87/03/27  14:21:33  jenkins
  89.  * Port to suns
  90.  * 
  91.  * Revision 1.1  84/01/23  14:49:54  kcs
  92.  * Initial revision
  93.  * 
  94.  * Revision 4.3  83/12/15  12:28:54  wft
  95.  * ci -u and ci -l now set mode of working file properly.
  96.  * 
  97.  * Revision 4.2  83/12/05  13:40:54  wft
  98.  * Merged with 3.9.1.1: added calls to clearerr(stdin).
  99.  * made rewriteflag external.
  100.  * 
  101.  * Revision 4.1  83/05/10  17:03:06  wft
  102.  * Added option -d and -w, and updated assingment of date, etc. to new delta.
  103.  * Added handling of default branches.
  104.  * Option -k generates std. log message; fixed undef. pointer in reading of log.
  105.  * Replaced getlock() with findlock(), link--unlink with rename(),
  106.  * getpwuid() with getcaller().
  107.  * Moved all revision number generation to new routine addelta().
  108.  * Removed calls to stat(); now done by pairfilenames().
  109.  * Changed most calls to catchints() with restoreints().
  110.  * Directed all interactive messages to stderr.
  111.  * 
  112.  * Revision 3.9.1.1  83/10/19  04:21:03  lepreau
  113.  * Added clearerr(stdin) to getlogmsg() for re-reading stdin.
  114.  * 
  115.  * Revision 3.9  83/02/15  15:25:44  wft
  116.  * 4.2 prerelease
  117.  * 
  118.  * Revision 3.9  83/02/15  15:25:44  wft
  119.  * Added call to fastcopy() to copy remainder of RCS file.
  120.  *
  121.  * Revision 3.8  83/01/14  15:34:05  wft
  122.  * Added ignoring of interrupts while new RCS file is renamed;
  123.  * Avoids deletion of RCS files by interrupts.
  124.  *
  125.  * Revision 3.7  82/12/10  16:09:20  wft
  126.  * Corrected checking of return code from diff.
  127.  *
  128.  * Revision 3.6  82/12/08  21:34:49  wft
  129.  * Using DATEFORM to prepare date of checked-in revision;
  130.  * Fixed return from addbranch().
  131.  *
  132.  * Revision 3.5  82/12/04  18:32:42  wft
  133.  * Replaced getdelta() with gettree(), SNOOPDIR with SNOOPFILE. Updated
  134.  * field lockedby in removelock(), moved getlogmsg() before calling diff.
  135.  *
  136.  * Revision 3.4  82/12/02  13:27:13  wft
  137.  * added option -k.
  138.  *
  139.  * Revision 3.3  82/11/28  20:53:31  wft
  140.  * Added mustcheckin() to check for redundant checkins.
  141.  * Added xpandfile() to do keyword expansion for -u and -l;
  142.  * -m appends linefeed to log message if necessary.
  143.  * getlogmsg() suppresses prompt if stdin is not a terminal.
  144.  * Replaced keeplock with lockflag, fclose() with ffclose(),
  145.  * %02d with %.2d, getlogin() with getpwuid().
  146.  *
  147.  * Revision 3.2  82/10/18  20:57:23  wft
  148.  * An RCS file inherits its mode during the first ci from the working file,
  149.  * otherwise it stays the same, except that write permission is removed.
  150.  * Fixed ci -l, added ci -u (both do an implicit co after the ci).
  151.  * Fixed call to getlogin(), added call to getfullRCSname(), added check
  152.  * for write error.
  153.  * Changed conflicting identifiers.
  154.  *
  155.  * Revision 3.1  82/10/13  16:04:59  wft
  156.  * fixed type of variables receiving from getc() (char -> int).
  157.  * added include file dbm.h for getting BYTESIZ. This is used
  158.  * to check the return code from diff portably.
  159.  */
  160.  
  161. #include "rcsbase.h"
  162. #ifndef lint
  163. static char rcsbaseid[] = RCSBASE;
  164. #endif
  165. #ifdef OSK
  166. #    include "time.h"
  167. #    include "stat.h"
  168. #    define GNUDIFF 1
  169. #else
  170. #    include <sys/types.h>
  171. #    include <sys/stat.h>
  172. #    include "time.h"
  173. #endif
  174.  
  175. extern int    rename();                /*rename files                       */
  176. extern char * getcaller();             /*login of caller                    */
  177. extern struct hshentry * genrevs();    /*generate delta numbers             */
  178. extern quietflag;                      /*suppresses diagnostics if true     */
  179. extern int  nerror;                    /*counter for errors                 */
  180. extern char * buildrevision();         /*constructs desired revision        */
  181. extern char * checkid();               /*check identifiers                  */
  182. extern int    partime();               /*parse free-format date/time        */
  183. extern long   maketime();              /*convert parsed time to unix time.  */
  184. #ifndef _time
  185. extern long   time();                  /*get date and time                  */
  186. extern struct tm * localtime();        /*convert unixtime into tm-structure */
  187. #endif
  188. extern char * getdate();               /*formates current date  (forward)   */
  189. extern char * mktempfile();            /*temporary file name generator      */
  190. extern struct lock * addlock();        /*adds a new lock                    */
  191. extern char * getlogmsg();             /*obtains log message; forward       */
  192. extern struct hshentry * removelock(); /*finds a caller's lock  (forward)   */
  193. extern struct hshentry * findlock();   /*finds a lock                       */
  194. extern char * xpandfile();             /*perform keyword expansion; forward */
  195.  
  196. extern char prevauthor[];
  197. extern char prevdate[];
  198. extern char prevrev[];
  199. extern char prevstate [];
  200. extern FILE * finptr;                  /* RCS input file                    */
  201. extern FILE * frewrite;                /* new RCS file                      */
  202. extern int    rewriteflag;             /* indicates whether input should be */
  203.                        /* echoed to frewrite                */
  204.  
  205. char * newRCSfilename, * diffilename;
  206. char * RCSfilename,*workfilename,*expfilename,*newworkfilename;
  207.  
  208. #if defined(MSDOS) || defined(OSK)
  209. char tmpdir[NCPPN];
  210. extern char *gettmpdir();
  211. #endif /* MSDOS OSK */
  212. #ifdef OSK
  213. extern int errno;
  214. extern char **environ;
  215. #endif
  216. extern struct stat RCSstat, workstat; /* file status of RCS and work file   */
  217. extern int  haveRCSstat, haveworkstat;/* status indicators                  */
  218.  
  219.  
  220. int    copyflag;    /* indicates whether a string should be copied into memory*/
  221.  
  222. char * rev, * state, *msg;
  223.  
  224. int initflag, rcsinitflag;
  225. int lockflag, keepworkingfile,keepflag;
  226. int forceciflag;                      /* forces check in                    */
  227. int symrebindflag; char * symbol;
  228. int textflag; char * textfile;
  229. char * caller;                        /* caller's login;                    */
  230. char * author;                        /* alternate author for -w option     */
  231. char altdate[datelength];             /* alternate date for -d              */
  232. int usestatdate;              /* use mod time of file for -d         */
  233. struct hshentry * targetdelta;        /* old delta to be generated          */
  234. char   * olddeltanum;                 /* number of old delta                */
  235. struct hshentry * gendeltas[hshsize]; /* stores deltas to be generated      */
  236. char   newdelnum[revlength];          /* holds new revision number          */
  237. int    newdnumlength;                 /* actual length of new rev. num.     */
  238. char   branchpointnum[revlength];     /* number of branchpoint              */
  239. struct hshentry newdelta;             /* new delta to be inserted           */
  240. struct branchhead newbranch;          /* new branch to be inserted          */
  241. char   logmsg[logsize];               /* buffer for log message             */
  242.  
  243. main (argc, argv)
  244. int argc;
  245. char * argv[];
  246. {
  247.     char * nametest;
  248.         char * cmdusage;         /* holds command format                    */
  249.         struct stat filestatus;  /* used for getting the mode               */
  250.         int  msglen;             /* length of message given by -m           */
  251.     int exit_stats;         /* return code for command invocations     */
  252.     int newRCSmode;          /* mode for RCS file                       */
  253.     long unixtime;
  254.     struct tm  parseddate, *ftm;
  255.  
  256.     catchints();
  257.     cmdid = "ci";
  258.     cmdusage = "command format:\nci -r[rev] -l[rev] -u[rev] -f[rev] -k[rev] -q[rev] -mmsg -nname -Nname -sstate -t[txtfile] file ...";
  259.     rev = state = msg = symbol = textfile = nil;
  260.         initflag= rcsinitflag= symrebindflag= textflag= quietflag= false;
  261.         forceciflag= lockflag= keepworkingfile= keepflag= false;
  262.     caller = getcaller(); author = nil; /* author may be reset by -w */
  263.     altdate[0]= '\0'; /* empty alternate date for -d */
  264.     usestatdate=false;
  265.  
  266.         while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) {
  267.                 switch ((*argv)[1]) {
  268.  
  269.                 case 'r':
  270.                         lockflag=false;
  271.                 revno:  if ((*argv)[2]!='\0') {
  272.                                 if (rev!=nil)
  273.                                     warn("Redefinition of revision number");
  274.                                 rev = (*argv)+2;
  275.                         }
  276.                         break;
  277.  
  278.                 case 'l':
  279.                         keepworkingfile=lockflag=true;
  280.                         goto revno;
  281.  
  282.                 case 'u':
  283.                         keepworkingfile=true; lockflag=false;
  284.                         goto revno;
  285.  
  286.                 case 'q':
  287.                         quietflag=true;
  288.                         goto revno;
  289.  
  290.                 case 'f':
  291.                         forceciflag=true;
  292.                         goto revno;
  293.  
  294.                 case 'k':
  295.                         keepflag=true;
  296.                         goto revno;
  297.  
  298.                 case 'm':
  299.                         if ((*argv)[2]!='\0'){
  300.                                 if (msg!=nil)
  301.                                     warn("Redefinition of -m option");
  302.                                 msg = (*argv)+2;
  303.                                 msglen=strlen(msg);
  304.                                 if (msglen >= logsize) {
  305.                                     warn("log message truncated to %d characters",
  306.                                         logsize);
  307.                                    msg[logsize-2]='\n';
  308.                                    msg[logsize-1]='\0';
  309.                                 }
  310.                                 if (msg[msglen-1]!='\n') {
  311.                                    /*append linefeed*/
  312.                                    VOID strcpy(logmsg,msg);msg=logmsg;
  313.                                    msg[msglen]  = '\n';
  314.                                    msg[++msglen]= '\0';
  315.                                 }
  316.                         } else
  317.                             warn("Missing message for -m option");
  318.                         break;
  319.  
  320.                 case 'n':
  321.                         symrebindflag=false;
  322.                         if ((*argv)[2]!='\0'){
  323.                                 if (symbol!=nil)
  324.                                     warn("Redefinition of symbolic name");
  325.                                 symbol = (*argv)+2;
  326.                 if (!(nametest=checkid(symbol,' '))||*nametest)
  327.                     faterror("Name %s must be one word",symbol);
  328.                         } else
  329.                     warn("Missing name for -n option");
  330.                         break;
  331.  
  332.                 case 'N':
  333.                         symrebindflag=true;
  334.                         if ((*argv)[2]!='\0'){
  335.                                 if (symbol!=nil)
  336.                                     warn("Redefinition of symbolic name");
  337.                                 symbol = (*argv)+2;
  338.                 if (!(nametest=checkid(symbol,' '))||*nametest)
  339.                     faterror("Name %s must be one word",symbol);
  340.                         } else
  341.                             warn("Missing name for -N option");
  342.                         break;
  343.  
  344.                 case 's':
  345.                         if ((*argv)[2]!='\0'){
  346.                                 if (state!=nil)
  347.                                     warn("Redefinition of -s option");
  348.                                 state = (*argv)+2;
  349.                                 VOID checkid(state,' ');
  350.                         } else
  351.                                     warn("Missing state for -s option");
  352.                         break;
  353.  
  354.                 case 't':
  355.                         textflag=true;
  356.                         if ((*argv)[2]!='\0'){
  357.                                 if (textfile!=nil)
  358.                                     warn("Redefinition of -t option");
  359.                                 textfile = (*argv)+2;
  360.                         }
  361.                         break;
  362.  
  363.         case 'd':
  364.                         if ((*argv)[2]!='\0'){
  365.                 if (altdate[0]!='\0' || usestatdate==true)
  366.                     warn("Redefinition of -d option");
  367.                 /* process the date */
  368.                 if ( partime((*argv)+2, &parseddate) == 0) {
  369.                     faterror("Can't parse date/time: %s", (*argv)+2);
  370.                     break;
  371.                 }
  372.                 if ( (unixtime = maketime(&parseddate)) == 0L) {
  373.                     faterror("Inconsistent date/time: %s",(*argv)+2);
  374.                     break;
  375.                 }
  376.                 ftm = localtime(&unixtime);
  377.                 VOID sprintf(altdate,DATEFORM,
  378.                 ftm->tm_year,ftm->tm_mon+1,ftm->tm_mday,ftm->tm_hour,ftm->tm_min,ftm->tm_sec);
  379.             } else 
  380.                 usestatdate++;
  381.                         break;
  382.  
  383.         case 'w':
  384.                         if ((*argv)[2]!='\0'){
  385.                 if (author!=nil)warn("Redefinition of -w option");
  386.                 author = (*argv)+2;
  387.                 VOID checkid(author,' ');
  388.             } else warn("Missing author for -w option");
  389.                         break;
  390.  
  391.  
  392.  
  393.  
  394.                 default:
  395.                         faterror("unknown option: %s\n%s", *argv,cmdusage);
  396.                 };
  397.         }  /* end processing of options */
  398.  
  399.         if (argc<1) faterror("No input file\n%s",cmdusage);
  400.  
  401.         if (!ttystdin() && msg==nil && textflag && textfile==nil) {
  402.                 /* would need both log message and descriptive text from a file */
  403.                 faterror("Can't take both log and description from redirected stdin; use -ttextfile");
  404.         }
  405.         /* now handle all filenames */
  406. #if defined(MSDOS) || defined(OSK)
  407.     strcpy( tmpdir, gettmpdir());
  408. #endif /* MSDOS */
  409.         do {
  410.         gendeltas[0] = nil;
  411.         copyflag=rewriteflag=false;
  412.         finptr=frewrite=NULL;
  413.         targetdelta=nil;
  414.         olddeltanum=nil;
  415.  
  416.         switch (pairfilenames(argc,argv,false,false)) {
  417.  
  418.         case -1:                /* New RCS file */
  419.                 initflag=true; rcsinitflag=false;
  420.                 break;
  421.  
  422.         case 0:                 /* Error */
  423.                 continue;
  424.  
  425.         case 1:                 /* Normal checkin with prev . RCS file */
  426.                 initflag=false; rcsinitflag=(Head==nil);
  427.         }
  428.  
  429.         /* now RCSfilename contains the name of the RCS file, and
  430.          * workfilename contains the name of the working file.
  431.          * if !initflag, finptr contains the file descriptor for the
  432.          * RCS file. The admin node is initialized.
  433.          * workstat and RCSstat are set.
  434.          */
  435.  
  436.         diagnose("%s  <--  %s", RCSfilename,workfilename);
  437. #ifndef OSK
  438.         if (access(workfilename,0x01)!=0) {    /* S_IREAD */
  439. #else
  440.         if (access(workfilename,S_IREAD)!=0) {
  441. #endif
  442.                 error("working file %s not readable or nonexistent",
  443.                        workfilename);
  444.                 continue;
  445.         }
  446.  
  447.         /*
  448.          * make sure workfile is a regular file.
  449.          */
  450.         VOID stat(workfilename, &filestatus);
  451. #ifdef OSK
  452.         if (filestatus.st_mode & S_IFDIR) {
  453. #else
  454.         if ((filestatus.st_mode & S_IFMT) != S_IFREG) {
  455. #endif
  456.                 error("working file %s isn't a regular file", workfilename);
  457.                 continue;
  458.         }
  459.         /*
  460.          * if RCSfile doesn't exist, use mode from workfile, otherwise
  461.          * keep the one from the RCSfile.
  462.          */
  463.         if (! (initflag || rcsinitflag))
  464.             VOID fstat(fileno(finptr), &filestatus);
  465.  
  466.         if (!trydiraccess(RCSfilename)) continue; /* give up */
  467.         if (!initflag && !checkaccesslist(caller))   continue; /* give up */
  468.         if (!trysema(RCSfilename,true)) continue; /* give up */
  469.  
  470.         if (keepflag) {
  471.                 /* get keyword values from working file */
  472.                 if (!getoldkeys(workfilename)) continue;
  473.                 if (rev==nil && *(rev=prevrev)=='\0') {
  474.                         error("Can't find a revision number in %s",workfilename);
  475.                         continue;
  476.                 }
  477.         if (*prevdate=='\0' && *altdate=='\0' && usestatdate==false)
  478.             warn("Can't find a date in %s",workfilename);
  479.         if (*prevauthor=='\0' && author==nil)
  480.                         warn("Can't find an author in %s", workfilename);
  481.         if (*prevstate=='\0' && state==nil)
  482.                         warn("Can't find a state in %s", workfilename);
  483.         } /* end processing keepflag */
  484.  
  485.         gettree(); /* reads in the delta tree.*/
  486.  
  487.         /* expand symbolic revision number */
  488.         if (!expandsym(rev,newdelnum)) continue;
  489.  
  490.         /* splice new delta into tree */
  491.         if (!addelta()) continue;
  492.  
  493.         if (initflag||rcsinitflag) {
  494.                 diagnose("initial revision: %s",newdelnum);
  495.         } else  diagnose("new revision: %s; previous revision: %s",
  496.                 newdelnum,olddeltanum);
  497.  
  498.         newdelta.num=newdelnum;
  499.         newdelta.branches=nil;
  500.         newdelta.log=nil;
  501.         newdelta.lockedby=nil; /*might be changed by addlock() */
  502.     /* set author */
  503.     if (author!=nil)
  504.         newdelta.author=author;     /* set author given by -w         */
  505.     elsif (keepflag && *prevauthor!='\0')
  506.         newdelta.author=prevauthor; /* preserve old author of possible*/
  507.     else    newdelta.author=caller;     /* otherwise use caller's id      */
  508.     if (state!=nil)
  509.         newdelta.state=state;       /* set state given by -s          */
  510.     elsif (keepflag && *prevstate!='\0')
  511.         newdelta.state=prevstate;   /* preserve old state if possilbe */
  512.     else    newdelta.state=DEFAULTSTATE;/* otherwise use default state    */
  513.     if (usestatdate==true) {
  514.         if(haveworkstat<0) {
  515.         error("can't stat %s",workfilename);
  516.         continue;
  517.         } 
  518.         ftm = localtime(&workstat.st_mtime);
  519.         VOID sprintf(altdate,DATEFORM,ftm->tm_year,ftm->tm_mon+1,
  520.         ftm->tm_mday,ftm->tm_hour,ftm->tm_min,ftm->tm_sec);
  521.     }
  522.     if (*altdate!='\0')
  523.         newdelta.date=altdate;      /* set date given by -d           */
  524.     elsif (keepflag && *prevdate!='\0') /* preserve old date if possible  */
  525.                 newdelta.date  =prevdate;
  526.     else
  527.         newdelta.date = getdate();  /* use current date               */
  528.     /* now check validity of date -- needed because of -d and -k          */
  529.     if (targetdelta!=nil &&
  530.         cmpnum(newdelta.date,targetdelta->date)<=0) {
  531.         error("Date %s is not later than %s in existing revision %s",
  532.                newdelta.date,targetdelta->date, targetdelta->num);
  533.         continue;
  534.     }
  535.  
  536.  
  537.         if (lockflag && !addlock(&newdelta,caller)) continue;
  538.         if (symbol && !addsymbol(&newdelta,symbol,symrebindflag)) continue;
  539.  
  540.         /* prepare for rewriting the RCS file */
  541.         newRCSfilename=mktempfile(RCSfilename,NEWRCSFILE);
  542.         if ((frewrite=fopen(newRCSfilename, "w"))==NULL) {
  543.                 error("Can't open file %s",newRCSfilename);
  544.                 continue;
  545.         }
  546.         putadmin(frewrite);
  547.         puttree(Head,frewrite);
  548.         putdesc(initflag,textflag,textfile,quietflag);
  549.  
  550.  
  551.         /* build rest of file */
  552.         if (initflag||rcsinitflag) {
  553.                 /* get logmessage */
  554.                 newdelta.log=getlogmsg();
  555.                 if(!putdtext(newdelnum,newdelta.log,workfilename,frewrite)) continue;
  556.                 ffclose(frewrite); frewrite=NULL;
  557.         } else {
  558. #if defined(MSDOS) || defined(OSK)
  559.                 diffilename=mktempfile(tmpdir,DIFFILE);
  560. #else
  561.                 diffilename=mktempfile("/tmp/",DIFFILE);
  562. #endif /* MSDOS */
  563.                 if (&newdelta==Head) {
  564.                         /* prepend new one */
  565.                         rewriteflag=false;
  566.                         if (!(expfilename=
  567. #if defined(MSDOS) || defined(OSK)
  568.                               buildrevision(gendeltas,targetdelta,tmpdir,false))) continue;
  569. #else
  570.                               buildrevision(gendeltas,targetdelta,"/tmp/",false))) continue;
  571. #endif /* MSDOS */
  572.                         if (!mustcheckin(expfilename,targetdelta)) continue;
  573.                                 /* don't check in files that aren't different, unless forced*/
  574.                         newdelta.log=getlogmsg();
  575.                         exit_stats = run((char*)nil, diffilename,
  576. #if defined(MSDOS) || defined(OSK)
  577.                 DIFF,"-n","-a",workfilename,expfilename, (char*)nil);
  578. #else
  579.                 DIFF,"-n",workfilename,expfilename, (char*)nil);
  580. #endif /* MSDOS */
  581. #if ( defined(MSDOS) || defined(OSK) ) && defined(GNUDIFF) 
  582.                         if (exit_stats != 0 && exit_stats != 1 )
  583. #else
  584.                         if (exit_stats != 0 && exit_stats != (1 << BYTESIZ))
  585. #endif /* MSDOS && GNUDIFF */
  586.                             faterror ("diff failed");
  587.                         /* diff returns 2 in the upper byte on failure */
  588.                         if(!putdtext(newdelnum,newdelta.log,workfilename,frewrite)) continue;
  589.                         if(!putdtext(olddeltanum,targetdelta->log,diffilename,frewrite)) continue;
  590.                 } else {
  591.                         /* insert new delta text */
  592.                         rewriteflag=true;
  593.                         if (!(expfilename=
  594. #if defined(MSDOS) || defined(OSK)
  595.                               buildrevision(gendeltas,targetdelta,tmpdir,false))) continue;
  596. #else
  597.                               buildrevision(gendeltas,targetdelta,"/tmp/",false))) continue;
  598. #endif /* MSDOS */
  599.                         if (!mustcheckin(expfilename,targetdelta)) continue;
  600.                                 /* don't check in files that aren't different, unless forced*/
  601.                         newdelta.log=getlogmsg();
  602.                         exit_stats = run((char*)nil, diffilename,
  603. #if defined(MSDOS) || defined(OSK)
  604.                 DIFF,"-n","-a", expfilename,workfilename, (char*)nil);
  605. #else
  606.                 DIFF,"-n",expfilename,workfilename, (char*)nil);
  607. #endif /* MSDOS */
  608. #if ( defined(MSDOS) || defined(OSK) ) && defined(GNUDIFF)
  609.                         if (exit_stats != 0 && exit_stats != 1 )
  610. #else
  611.                         if (exit_stats != 0 && exit_stats != (1 << BYTESIZ))
  612. #endif /* MSDOS && GNUDIFF */
  613.                             faterror ("diff failed");
  614.                         if(!putdtext(newdelnum,newdelta.log,diffilename,frewrite)) continue;
  615.                 }
  616.  
  617.                 /* rewrite rest of RCS file */
  618.                 fastcopy(finptr,frewrite);
  619.                 ffclose(frewrite); frewrite=NULL;
  620.         }
  621.     ignoreints();
  622. #ifdef OSK
  623.     fclose(    finptr ); finptr=NULL;
  624. #endif
  625. #ifdef MSDOS
  626.         if (Rename(newRCSfilename,RCSfilename)<0) {
  627. #else
  628. #ifdef _UCC
  629.         if (_rename(newRCSfilename,RCSfilename)<0) {
  630. #else
  631.         if (rename(newRCSfilename,RCSfilename)<0) {
  632. #endif
  633. #endif /* MSDOS */
  634.                 error("Can't write new RCS file %s; saved in %s",
  635.                       RCSfilename,newRCSfilename);
  636.                 newRCSfilename[0]='\0'; /* avoid deletion by cleanup*/
  637.                 restoreints();
  638.                 VOID cleanup();
  639.                 break;
  640.         }
  641.         newRCSfilename[0]='\0'; /* avoid re-unlinking by cleanup()*/
  642.  
  643. #ifdef MSDOS
  644.     newRCSmode= (initflag|rcsinitflag?workstat.st_mode:RCSstat.st_mode)& ~S_IWRITE;
  645. #else
  646. #ifdef OSK
  647. #ifdef _OS9000
  648. #if 0
  649.     newRCSmode = (initflag|rcsinitflag?workstat.st_mode:RCSstat.st_mode)&
  650.                 ~(S_IWRITE|S_IGWRITE|S_IOWRITE);
  651. #else
  652.     newRCSmode = S_IREAD|S_IGREAD|S_IOREAD;
  653. #endif
  654. #else
  655. #if 0
  656.     newRCSmode = (initflag|rcsinitflag?workstat.st_mode:RCSstat.st_mode)& ~0x12;
  657. #else
  658.     newRCSmode = S_IREAD|S_IOREAD;
  659. #endif
  660. #endif
  661. #else
  662.     newRCSmode= (initflag|rcsinitflag?workstat.st_mode:RCSstat.st_mode)& ~0222;
  663. #endif /* OSK */
  664. #endif /* MSDOS */
  665.     /* newRCSmode is also used to adjust mode of working file for -u and -l */
  666.     if (chmod(RCSfilename,newRCSmode)<0)
  667.         warn("Can't set mode of %s",RCSfilename);
  668.  
  669.         restoreints();
  670. #       ifdef SNOOPFILE
  671.         logcommand("ci",&newdelta,gendeltas,caller);
  672. #       endif
  673.  
  674.         if (!keepworkingfile) {
  675.                 VOID unlink(workfilename); /* get rid of old file */
  676.         } else {
  677.                 /* expand keywords in file */
  678.                 newworkfilename=
  679.                 xpandfile(workfilename,workfilename /*for directory*/,&newdelta);
  680.                 if (!newworkfilename) continue; /* expand failed */
  681.         ignoreints();
  682. #ifdef MSDOS
  683.         if (Rename(newworkfilename,workfilename) <0) {
  684. #else
  685. #ifdef _UCC
  686.         if (_rename(newworkfilename,workfilename) <0) {
  687. #else
  688.         if (rename(newworkfilename,workfilename) <0) {
  689. #endif
  690. #endif /* MSDOS */
  691.                     error("Can't expand keywords in %s",workfilename);
  692.                     restoreints();
  693.                     continue;
  694.                 }
  695.         newworkfilename[0]='\0'; /* avoid re-unlink by cleanup */
  696. #ifdef OSK
  697. #ifdef _OS9000
  698.         if (chmod(workfilename, WORKMODE(newRCSmode)&0x777) !=0 )
  699. #else
  700.         if (chmod(workfilename, WORKMODE(newRCSmode)&0x7f) !=0 )
  701. #endif
  702. #else
  703.         if (chmod(workfilename, WORKMODE(newRCSmode))<0)
  704. #endif
  705.                 warn("Can't adjust mode of %s ",workfilename);
  706.                 restoreints();
  707.         }
  708.         diagnose("done");
  709.  
  710.         } while (cleanup(),
  711.                  ++argv, --argc >=1);
  712.  
  713.         exit(nerror!=0);
  714.     /*NOTREACHED*/
  715. }       /* end of main (ci) */
  716. /*****************************************************************/
  717. /* the rest are auxiliary routines                               */
  718.  
  719.  
  720. int addelta()
  721. /* Function: Appends a delta to the delta tree, whose number is
  722.  * given by newdelnum[]. Updates Head, newdelnum, newdenumlength,
  723.  * olddeltanum and the links in newdelta.
  724.  * Retruns false on error, true on success.
  725.  */
  726. {
  727.         register char * sp, * tp;
  728.         register int i;
  729.  
  730.         newdnumlength=countnumflds(newdelnum);
  731.  
  732.         if (initflag || rcsinitflag ) {
  733.                 /* this covers non-existing RCS file and a file initialized with rcs -i */
  734.         if ((newdnumlength==0)&&(Dbranch!=nil)) {
  735.             VOID strcpy(newdelnum,Dbranch->num);
  736.             newdnumlength=countnumflds(newdelnum);
  737.         }
  738.                 if (newdnumlength==0) VOID strcpy(newdelnum,"1.1");
  739.                 elsif (newdnumlength==1) VOID strcat(newdelnum,".1");
  740.                 elsif (newdnumlength>2) {
  741.                     error("Branch point does not exist for %s",newdelnum);
  742.                     return false;
  743.                 } /* newdnumlength == 2 is OK;  */
  744.                 olddeltanum=nil;
  745.                 Head = &newdelta;
  746.                 newdelta.next=nil;
  747.                 return true;
  748.         }
  749.         if (newdnumlength==0) {
  750.                 /* derive new revision number from locks */
  751.         targetdelta=findlock(caller,true); /*find and delete it*/
  752.                 if (targetdelta) {
  753.                     /* found an old lock */
  754.                     olddeltanum=targetdelta->num;
  755.                     /* check whether locked revision exists */
  756.                     if (!genrevs(olddeltanum,(char *)nil,(char *)nil,(char *)nil,gendeltas)) return false;
  757.                     if (targetdelta==Head) {
  758.                         /* make new head */
  759.                         newdelta.next=Head;
  760.                         Head= &newdelta;
  761.                         incnum(olddeltanum, newdelnum);
  762.                     } elsif ((targetdelta->next==nil)&&(countnumflds(olddeltanum)>2)) {
  763.                         /* new tip revision on side branch */
  764.                         targetdelta->next= &newdelta;
  765.                         newdelta.next = nil;
  766.                         incnum(olddeltanum, newdelnum);
  767.                     } else {
  768.                         /* middle revision; start a new branch */
  769.                         newdelnum[0]='\0';
  770.                         if (!addbranch(targetdelta,newdelnum)) return false;
  771.                     }
  772.                     return true; /* successfull use of existing lock */
  773.                 } else {
  774.                     /* no existing lock; try Dbranch */
  775.                     /* update newdelnum */
  776. #ifdef MSDOS
  777.                     if (!(StrictLocks==false) ) {
  778. #else
  779. #ifdef OSK
  780.                     if (!((StrictLocks==false)
  781.                         && ( getuid() == RCSstat.st_gid*0x10000+RCSstat.st_uid ))) {
  782. #else
  783.                     if (!((StrictLocks==false) && (getuid() == RCSstat.st_uid))) {
  784. #endif /* OSK */
  785. #endif /* MSDOS */
  786.                         error("no lock set by %s",caller);
  787.                         return false;
  788.                     }
  789.                     if (Dbranch) {
  790.                         VOID strcpy(newdelnum,Dbranch->num);
  791.                     } else {
  792.                         incnum(Head->num,newdelnum);
  793.                     }
  794.                     newdnumlength=countnumflds(newdelnum);
  795.                     /* now fall into next statement */
  796.                 }
  797.         }
  798.         if (newdnumlength<=2) {
  799.                 /* add new head per given number */
  800.                 olddeltanum=Head->num;
  801.                 if(newdnumlength==1) {
  802.                     /* make a two-field number out of it*/
  803.                     if (cmpnumfld(newdelnum,olddeltanum,1)==0)
  804.                           incnum(olddeltanum,newdelnum);
  805.                     else  VOID strcat(newdelnum, ".1");
  806.                 }
  807.                 if (cmpnum(newdelnum,olddeltanum) <= 0) {
  808.                     error("deltanumber %s too low; must be higher than %s",
  809.                           newdelnum,Head->num);
  810.                     return false;
  811.                 }
  812.                 if (!(targetdelta=removelock(caller,Head))) return false;
  813.                 if (!(genrevs(olddeltanum,(char *)nil,(char *)nil,(char *)nil,gendeltas))) return false;
  814.                 newdelta.next=Head;
  815.                 Head= &newdelta;
  816.         } else {
  817.                 /* put new revision on side branch */
  818.                 /*first, get branch point */
  819.                 tp=branchpointnum; sp=newdelnum;
  820.                 for(i=newdnumlength-(newdnumlength%2==1?1:2);i>0;i--) {
  821.                     while (*sp != '.') *tp++ = *sp++; /*copy field*/
  822.                     *tp++ = *sp++;                    /*copy dot  */
  823.                 }
  824.                 *(tp-1) = '\0'; /* kill final dot */
  825.                 olddeltanum=branchpointnum; /*temporary old delta*/
  826.                 if (!(targetdelta=genrevs(branchpointnum,(char *)nil,(char *)nil,(char *)nil,gendeltas)))
  827.                      return false;
  828.                 if (cmpnum(targetdelta->num,branchpointnum)!=0) {
  829.                     error("Cannot find branchpoint %s",branchpointnum);
  830.                     return false;
  831.                 }
  832.                 if (!addbranch(targetdelta,newdelnum)) return false;
  833.         }
  834.         return true;
  835. }
  836.  
  837.  
  838.  
  839. int addbranch(branchpoint,num)
  840. struct hshentry * branchpoint;
  841. char * num;
  842. /* adds a new branch and branch delta at branchpoint.
  843.  * If num is the null string, appends the new branch, incrementing
  844.  * the highest branch number (initially 1), and setting the level number to 1.
  845.  * the new delta and branchhead are in globals newdelta and newbranch, resp.
  846.  * the new number is placed into num.
  847.  * returns false on error.
  848.  */
  849. {
  850.         struct branchhead * bhead, * btrail;
  851.         char branchnum[revlength];
  852.         int numlength, result, field;
  853.  
  854.         numlength = countnumflds(num);
  855.  
  856.         if (branchpoint->branches==nil) {
  857.                 /* start first branch */
  858.                 branchpoint->branches = &newbranch;
  859.                 if (numlength==0) {
  860.                         VOID strcpy(num, branchpoint->num);
  861.                         VOID strcat(num,".1.1");
  862.                 } elsif(countnumflds(num)%2 == 1)
  863.                         VOID strcat(num, ".1");
  864.                 newbranch.nextbranch=nil;
  865.  
  866.         } elsif (numlength==0) {
  867.                 /* append new branch to the end */
  868.                 bhead=branchpoint->branches;
  869.                 while (bhead->nextbranch) bhead=bhead->nextbranch;
  870.                 bhead->nextbranch = &newbranch;
  871.                 getbranchno(bhead->hsh->num,branchnum);
  872.                 incnum(branchnum,num);
  873.                 VOID strcat(num,".1");
  874.                 newbranch.nextbranch=nil;
  875.         } else {
  876.                 /* place the branch properly */
  877.                 field = numlength - (numlength%2 ==1?0:1);
  878.                 /* field of branch number */
  879.                 bhead=branchpoint->branches;
  880.                 while ((bhead!=nil) &&
  881.                        ((result=cmpnumfld(num,bhead->hsh->num,field))>0)) {
  882.                         btrail=bhead;
  883.                         bhead=bhead->nextbranch;
  884.                 }
  885.                 if (bhead==nil || result<0) {
  886.                         /* insert/append new branchhead */
  887.                         if (bhead==branchpoint->branches)
  888.                                 branchpoint->branches= &newbranch;
  889.                         else    btrail->nextbranch= &newbranch;
  890.                         newbranch.nextbranch=bhead;
  891.                         if (numlength%2 ==1) VOID strcat(num,".1");
  892.                 } else {
  893.                         /* branch exists; append to end */
  894.                         getbranchno(num,branchnum);
  895.                         if (!(targetdelta=genrevs(branchnum,(char *)nil,(char *)nil,(char *)nil,
  896.                                 gendeltas))) return false;
  897.                         olddeltanum=targetdelta->num;
  898.                         if (cmpnum(num,olddeltanum) <= 0) {
  899.                                 error("deltanumber %s too low; must be higher than %s",
  900.                                       num,olddeltanum);
  901.                                 return false;
  902.                         }
  903.                         if (!removelock(caller,targetdelta)) return false;
  904.                         if (numlength%2==1) incnum(olddeltanum,num);
  905.                         targetdelta->next= &newdelta;
  906.                         newdelta.next=nil;
  907.                         return true; /* Don't do anything to newbranch */
  908.                 }
  909.         }
  910.         newbranch.hsh = &newdelta;
  911.         newdelta.next=nil;
  912.         return true;
  913. }
  914.  
  915.  
  916.  
  917. struct hshentry * removelock(who,delta)
  918. char * who; struct hshentry * delta;
  919. /* function: Finds the lock held by who on delta,
  920.  * removes it, and returns a pointer to the delta.
  921.  * Prints an error message and returns nil if there is no such lock.
  922.  * An exception is if StrictLocks==false, and who is the owner of
  923.  * the RCS file. If who does not have a lock in this case,
  924.  * delta is returned.
  925.  */
  926. {
  927.         register struct lock * next, * trail;
  928.         char * num;
  929.         struct lock dummy;
  930.         int whomatch, nummatch;
  931.  
  932.         num=delta->num;
  933.         dummy.nextlock=next=Locks;
  934.         trail = &dummy;
  935.         while (next!=nil) {
  936.                 whomatch=strcmp(who,next->login);
  937.                 nummatch=strcmp(num,next->delta->num);
  938.                 if ((whomatch==0) && (nummatch==0)) break;
  939.                      /*found a lock on delta by who*/
  940.                 if ((whomatch!=0)&&(nummatch==0)) {
  941.                     error("revision %s locked by %s",num,next->login);
  942.                     return nil;
  943.                 }
  944.                 trail=next;
  945.                 next=next->nextlock;
  946.         }
  947.         if (next!=nil) {
  948.                 /*found one; delete it */
  949.                 trail->nextlock=next->nextlock;
  950.                 Locks=dummy.nextlock;
  951.                 next->delta->lockedby=nil; /* reset locked-by */
  952.                 return next->delta;
  953.         } else {
  954. #ifdef MSDOS
  955.                 if (!(StrictLocks==false) ) {
  956. #else
  957. #ifdef OSK
  958.                 if (!((StrictLocks==false)
  959.                         && ( getuid() == RCSstat.st_gid*0x10000+RCSstat.st_uid ))) {
  960. #else
  961.                 if (!((StrictLocks==false) && (getuid() == RCSstat.st_uid))) {
  962. #endif    /* OSK */
  963. #endif /* MSDOS */
  964.                     error("no lock set by %s for revision %s",who,num);
  965.                     return nil;
  966.                 } else {
  967.                         return delta;
  968.                 }
  969.         }
  970. }
  971.  
  972.  
  973.  
  974. char * getdate()
  975. /* Function: returns a pointer to the current date in the form
  976.  * YY.MM.DD.hh.mm.ss\0
  977.  */
  978. {
  979.         long clock;
  980.         struct tm * tm;
  981.     static char buffer[datelength]; /* date buffer */
  982.  
  983.         clock=time((long *)0);
  984.         tm=localtime(&clock);
  985.         VOID sprintf(buffer, DATEFORM,
  986.                 tm->tm_year, tm->tm_mon+1, tm->tm_mday,
  987.                 tm->tm_hour, tm->tm_min, tm->tm_sec);
  988.         return buffer;
  989. }
  990.  
  991.  
  992. char * xpandfile (unexfname,dir,delta)
  993. char * unexfname, * dir;
  994. struct hshentry * delta;
  995. /* Function: Reads file unexpfname and copies it to a
  996.  * file in dir, performing keyword substitution with data from delta.
  997.  * returns the name of the expanded file if successful, nil otherwise.
  998.  */
  999. {       char * targetfname;
  1000.         FILE * unexfile, *exfile;
  1001.  
  1002.         targetfname=mktempfile(dir,TMPFILE3);
  1003.         if ((unexfile=fopen(unexfname,  "r" ))==NULL ||
  1004.             (exfile  =fopen(targetfname,"w"))==NULL) {
  1005.                 error("Can't expand file %s",unexfname);
  1006.                 return nil;
  1007.         }
  1008.         while (expandline(unexfile,exfile,delta,false,false)); /*expand*/
  1009.         ffclose(unexfile);ffclose(exfile);
  1010.         return targetfname;
  1011. }
  1012.  
  1013.  
  1014. mustcheckin (unexfname,delta)
  1015. char * unexfname; struct hshentry * delta;
  1016. /* Function: determines whether checkin should proceed.
  1017.  * Compares the wrkfilename with unexfname, disregarding keywords.
  1018.  * If the 2 files differ, returns true. If they do not differ, asks the user
  1019.  * whether to return true or false (i.e., whether to checkin the file anyway.
  1020.  * If the files do not differ, and quietflag==true, returns false.
  1021.  * Shortcut: If forceciflag==true, mustcheckin() always returns true.
  1022.  */
  1023. {       register int c;
  1024.         int response, result;
  1025.  
  1026.         if (forceciflag) return true;
  1027.  
  1028.         if (!rcsfcmp(workfilename,unexfname,delta)) return true;
  1029.         /* If files are different, must check them in. */
  1030.  
  1031.         /* files are the same */
  1032.         diagnose("File %s is unchanged with respect to revision %s",
  1033.                 workfilename,delta->num);
  1034.         if (quietflag || !ttystdin()) {
  1035.                 /* Files are the same, but can't ask, so don't checkin*/
  1036.                 result=false;
  1037.         } else {
  1038.                 /* ask user whether to check in */
  1039.                 VOID fputs("checkin anyway? [ny](n): ",stderr);
  1040. #ifdef OSK
  1041.                 fflush(stderr);
  1042. #endif
  1043.                 response=c=getchar();
  1044.                 while (!(c==EOF || c=='\n')) c=getchar();/*skip to end of line*/
  1045. #ifdef OSK
  1046.                 if ( c==EOF && ttystdin() ) {
  1047.                     cleareof(stdin); clearerr(stdin);
  1048.                 }
  1049. #endif
  1050.                 result=(response=='y'||response=='Y');
  1051.         }
  1052.         if (result==false) {
  1053.                 if (quietflag) {
  1054.                     warn("checkin aborted since %s was not changed; %s %sdeleted.",
  1055.                              workfilename,workfilename,keepworkingfile?"not ":"");
  1056.                 } else {
  1057.                     diagnose("checkin aborted; %s %sdeleted.",
  1058.                              workfilename,keepworkingfile?"not ":"");
  1059.                 }
  1060.                 if (!keepworkingfile) VOID unlink(workfilename);
  1061.         }
  1062.         return result;
  1063. }
  1064.  
  1065.  
  1066.  
  1067.  
  1068. /* --------------------- G E T L O G M S G --------------------------------*/
  1069. extern int stdinread; /* is >0 if redirected stdin has been read once.     */
  1070.  
  1071.  
  1072. char * getlogmsg()
  1073. /* Function: obtains a log message and returns a pointer to it.
  1074.  * If a log message is given via the -m option, a pointer to that
  1075.  * string is returned.
  1076.  * If this is the initial revision, a standard log message is returned.
  1077.  * Otherwise, reads a character string from the terminal.
  1078.  * The string must be terminated with a control-d or a single '.' on a
  1079.  * line. getlogmsg prompts the first time it is called for the
  1080.  * log message; during all later calls it asks whether the previous
  1081.  * log message can be reused.
  1082.  * returns a pointer to the character string; the pointer is always non-nil.
  1083.  */
  1084. {
  1085.         static logyet;      /*indicates whether previous log present*/
  1086.         static char emptylog[]  = "*** empty log message ***\n";
  1087.         static char initiallog[]= "Initial revision\n";
  1088.         char response;
  1089.     int cin;
  1090.         register char c, old1, old2, * tp;
  1091.  
  1092.         if (msg) return msg;
  1093.  
  1094.         if ((olddeltanum==nil)&&
  1095.         ((cmpnum(newdelnum,"1.1")==0)||(cmpnum(newdelnum,"1.0")==0))) {
  1096.                 return initiallog;
  1097.     }
  1098.     if (keepflag) {
  1099.         /* generate std. log message */
  1100.         VOID sprintf(logmsg, "checked in with -k by %s at %s.\n",caller,getdate());
  1101.         return(logmsg);
  1102.     }
  1103.         if (logyet) {
  1104.                 /*previous log available*/
  1105.                 if (!ttystdin()) return logmsg; /* reuse if stdin is not a terminal*/
  1106.                 /* otherwise ask */
  1107. #ifdef OSK
  1108.         cleareof(stdin); clearerr(stdin);    /* reset EOF status */
  1109. #else
  1110.         clearerr(stdin);        /* reset EOF ptr */
  1111. #endif
  1112.         VOID fputs("reuse log message of previous file? [yn](y): ",stderr);
  1113. #ifdef OSK
  1114.         fflush(stderr);
  1115. #endif
  1116.         cin=getchar();
  1117.         response=cin;
  1118.         while (!(cin==EOF || cin=='\n')) cin=getchar();/*skip to end of line*/
  1119. #ifdef OSK
  1120.         if ( cin==EOF && ttystdin() ) {
  1121.             cleareof(stdin); clearerr(stdin);
  1122.         }
  1123. #endif
  1124.             if (response=='\n'||response=='y'||response=='Y')
  1125.                         return logmsg;
  1126.                 else
  1127.                         logmsg[0]='\0'; /*kill existing log message */
  1128.         }
  1129.  
  1130.         /* now read string from stdin */
  1131.         if (ttystdin()) {
  1132. #ifdef MSDOS
  1133.             VOID fputs("enter log message:\n(terminate with ^Z or single '.')\n>> ",stderr);
  1134. #else
  1135. #ifdef OSK
  1136.             VOID fputs("enter log message:\n(terminate with ESC or single '.')\n>> ",stderr);
  1137.             fflush(stderr);
  1138. #else
  1139.             VOID fputs("enter log message:\n(terminate with ^D or single '.')\n>> ",stderr);
  1140. #endif
  1141. #endif /* MSDOS */
  1142.         } else {  /* redirected stdin */
  1143.                 if (stdinread>0)
  1144.                     faterror("Can't reread redirected stdin for log message; use -m");
  1145.                 stdinread++;
  1146.         }
  1147.  
  1148.     tp=logmsg; old1='\n'; old2=' ';
  1149.     if (feof(stdin))
  1150. #ifdef OSK
  1151.         cleareof(stdin); clearerr(stdin);
  1152. #else
  1153.         clearerr(stdin);
  1154. #endif
  1155.         for (;;) {
  1156.             cin=getchar();
  1157.             if (cin==EOF) {
  1158.                 if(ttystdin()) {
  1159.                     VOID printf("\n");
  1160. #ifdef OSK
  1161.                     cleareof(stdin); clearerr(stdin);
  1162. #else
  1163.                     clearerr(stdin);
  1164. #endif
  1165.             }
  1166.             if ((tp==logmsg)||(*(tp-1)!='\n')) *tp++ = '\n'; /* append newline */
  1167.                         *tp = '\0'; /*terminate*/
  1168.                         break;
  1169.                 }
  1170.                 if (cin=='\n' && old1=='.' && old2=='\n') {
  1171.                         *(tp-1) = '\0'; /*kill last period */
  1172.                         break;
  1173.                 }
  1174.                 if (tp>=logmsg+logsize-2) { /* overflow */
  1175.                         if (!ttystdin()) {
  1176.                                 warn("log message truncated to %d characters",logsize);
  1177.                                 logmsg[logsize-2]='\n';logmsg[logsize-1]='\0';
  1178.                                 return logmsg;
  1179.                         }
  1180.                         VOID fprintf(stderr,"log message too long. Maximum: %d\n",logsize);
  1181.                         VOID fputs("reenter log message:\n>> ",stderr);
  1182.                         fflush(stderr);
  1183.                         tp=logmsg; old1='\n'; old2=' ';
  1184.                         while (cin!='\n') cin=getchar(); /*skip line */
  1185.                         continue;
  1186.                 }
  1187.                 if (cin=='\n' && ttystdin()) VOID fputs(">> ",stderr);
  1188.                 fflush(stderr);
  1189.                 *tp++ = cin; old2=old1; old1=cin; /* this is the actual work!*/
  1190.                 /*SDELIM will be changed to double SDELIM by putdtext*/
  1191.         } /* end for */
  1192. #ifdef OSK
  1193.         if ( ttystdin() && ferror(stdin) ) {
  1194.             cleareof(stdin); clearerr(stdin);
  1195.         }
  1196. #endif
  1197.         /* now check whether the log message is not empty */
  1198.         tp=logmsg;
  1199.         while ((c= *tp++)==' '||c=='\t'||c=='\n'||c=='\f');
  1200.         if (*tp=='\0') {
  1201.                 logyet=false;
  1202.                 return emptylog;
  1203.         } else {
  1204.                 logyet=true;
  1205.                 return logmsg;
  1206.         }
  1207. }
  1208.