Ejemplo n.º 1
0
int read_passwd_list()
{
    char *filename;
    FILE *fp;
    char *linebuf = NULL;
    size_t linebuf_len;
    passwd_entry *passnode;

    filename = (char*)xmalloc(strlen(current_parsed_root->directory)
                              + strlen("/CVSROOT/passwd")
                              + 1);
    strcpy (filename, current_parsed_root->directory);
    strcat (filename, "/CVSROOT/passwd");

    init_passwd_list();

    fp = CVS_FOPEN (filename, "r");
    if (fp != NULL)
    {
        while (getline (&linebuf, &linebuf_len, fp) >= 0)
        {
            char *user;
            if(linebuf[strlen(linebuf)-1]=='\n')
                linebuf[strlen(linebuf)-1]='\0';
            if(linebuf[0]=='#')
                continue;
            user = cvs_strtok(linebuf,":");
            if(!user || !*user)
                continue;
            passnode = new_passwd_entry();
            passnode->username=xstrdup(user);
            passnode->password=xstrdup(cvs_strtok(NULL,":"));
            passnode->real_username=xstrdup(cvs_strtok(NULL,":"));

            xfree (linebuf);
            linebuf = NULL;
        }
        if (ferror (fp))
        {
            error (1, errno, "cannot read %s", filename);
        }
        if (fclose (fp) < 0)
            error (0, errno, "cannot close %s", filename);
    }
    return 0;
}
Ejemplo n.º 2
0
Archivo: myndbm.c Proyecto: rdebath/cvs
void
mydbm_close (DBM *db)
{
    if (db->modified)
    {
	FILE *fp;
	fp = CVS_FOPEN (db->name, FOPEN_BINARY_WRITE);
	if (fp == NULL)
	    error (1, errno, "cannot write %s", db->name);
	walklist (db->dbm_list, write_item, fp);
	if (fclose (fp) < 0)
	    error (0, errno, "cannot close %s", db->name);
    }
    free (db->name);
    dellist (&db->dbm_list);
    free (db);
}
Ejemplo n.º 3
0
/*
 * Builds a temporary file using setup_tmpfile() and invokes the user's
 * editor on the file.  The header garbage in the resultant file is then
 * stripped and the log message is stored in the "message" argument.
 *
 * If REPOSITORY is non-NULL, process rcsinfo for that repository; if it
 * is NULL, use the CVSADM_TEMPLATE file instead.  REPOSITORY should be
 * NULL when running in client mode.
 *
 * GLOBALS
 *   Editor     Set to a default value by configure and overridable using the
 *              -e option to the CVS executable.
 */
void
do_editor (const char *dir, char **messagep, const char *repository,
           List *changes)
{
    static int reuse_log_message = 0;
    char *line;
    int line_length;
    size_t line_chars_allocated;
    char *fname;
    struct stat pre_stbuf, post_stbuf;
    int retcode = 0;

    assert (!current_parsed_root->isremote != !repository);

    if (noexec || reuse_log_message)
        return;

    /* Abort before creation of the temp file if no editor is defined. */
    if (strcmp (Editor, "") == 0)
        error(1, 0, "no editor defined, must use -e or -m");

again:
    /* Create a temporary file.  */
    if( ( fp = cvs_temp_file( &fname ) ) == NULL )
        error( 1, errno, "cannot create temporary file" );

    if (*messagep)
    {
        (void) fputs (*messagep, fp);

        if ((*messagep)[0] == '\0' ||
                (*messagep)[strlen (*messagep) - 1] != '\n')
            (void) fprintf (fp, "\n");
    }
    else
        (void) fprintf (fp, "\n");

    if (repository != NULL)
        /* tack templates on if necessary */
        (void) Parse_Info (CVSROOTADM_RCSINFO, repository, rcsinfo_proc,
                           PIOPT_ALL, NULL);
    else
    {
        FILE *tfp;
        char buf[1024];
        size_t n;
        size_t nwrite;

        /* Why "b"?  */
        tfp = CVS_FOPEN (CVSADM_TEMPLATE, "rb");
        if (tfp == NULL)
        {
            if (!existence_error (errno))
                error (1, errno, "cannot read %s", CVSADM_TEMPLATE);
        }
        else
        {
            while (!feof (tfp))
            {
                char *p = buf;
                n = fread (buf, 1, sizeof buf, tfp);
                nwrite = n;
                while (nwrite > 0)
                {
                    n = fwrite (p, 1, nwrite, fp);
                    nwrite -= n;
                    p += n;
                }
                if (ferror (tfp))
                    error (1, errno, "cannot read %s", CVSADM_TEMPLATE);
            }
            if (fclose (tfp) < 0)
                error (0, errno, "cannot close %s", CVSADM_TEMPLATE);
        }
    }

    (void) fprintf (fp,
                    "%s----------------------------------------------------------------------\n",
                    CVSEDITPREFIX);
    (void) fprintf (fp,
                    "%sEnter Log.  Lines beginning with `%.*s' are removed automatically\n%s\n",
                    CVSEDITPREFIX, CVSEDITPREFIXLEN, CVSEDITPREFIX,
                    CVSEDITPREFIX);
    if (dir != NULL && *dir)
        (void) fprintf (fp, "%sCommitting in %s\n%s\n", CVSEDITPREFIX,
                        dir, CVSEDITPREFIX);
    if (changes != NULL)
        setup_tmpfile (fp, CVSEDITPREFIX, changes);
    (void) fprintf (fp,
                    "%s----------------------------------------------------------------------\n",
                    CVSEDITPREFIX);

    /* finish off the temp file */
    if (fclose (fp) == EOF)
        error (1, errno, "%s", fname);
    if (stat (fname, &pre_stbuf) == -1)
        pre_stbuf.st_mtime = 0;

    /* run the editor */
    run_setup (Editor);
    run_add_arg (fname);
    if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY,
                             RUN_NORMAL | RUN_SIGIGNORE | RUN_UNSETXID)) != 0)
        error (0, retcode == -1 ? errno : 0, "warning: editor session failed");

    /* put the entire message back into the *messagep variable */

    fp = xfopen (fname, "r");

    if (*messagep)
        free (*messagep);

    if (stat (fname, &post_stbuf) != 0)
        error (1, errno, "cannot find size of temp file %s", fname);

    if (post_stbuf.st_size == 0)
        *messagep = NULL;
    else
    {
        /* On NT, we might read less than st_size bytes, but we won't
           read more.  So this works.  */
        *messagep = (char *) xmalloc (post_stbuf.st_size + 1);
        (*messagep)[0] = '\0';
    }

    line = NULL;
    line_chars_allocated = 0;

    if (*messagep)
    {
        size_t message_len = post_stbuf.st_size + 1;
        size_t offset = 0;
        while (1)
        {
            line_length = getline (&line, &line_chars_allocated, fp);
            if (line_length == -1)
            {
                if (ferror (fp))
                    error (0, errno, "warning: cannot read %s", fname);
                break;
            }
            if (strncmp (line, CVSEDITPREFIX, CVSEDITPREFIXLEN) == 0)
                continue;
            if (offset + line_length >= message_len)
                expand_string (messagep, &message_len,
                               offset + line_length + 1);
            (void) strcpy (*messagep + offset, line);
            offset += line_length;
        }
    }
    if (fclose (fp) < 0)
        error (0, errno, "warning: cannot close %s", fname);

    /* canonicalize emply messages */
    if (*messagep != NULL &&
            (**messagep == '\0' || strcmp (*messagep, "\n") == 0))
    {
        free (*messagep);
        *messagep = NULL;
    }

    if (pre_stbuf.st_mtime == post_stbuf.st_mtime || *messagep == NULL)
    {
        for (;;)
        {
            (void) printf ("\nLog message unchanged or not specified\n");
            (void) printf ("a)bort, c)ontinue, e)dit, !)reuse this message unchanged for remaining dirs\n");
            (void) printf ("Action: (abort) ");
            (void) fflush (stdout);
            line_length = getline (&line, &line_chars_allocated, stdin);
            if (line_length < 0)
            {
                error (0, errno, "cannot read from stdin");
                if (unlink_file (fname) < 0)
                    error (0, errno,
                           "warning: cannot remove temp file %s", fname);
                error (1, 0, "aborting");
            }
            else if (line_length == 0
                     || *line == '\n' || *line == 'a' || *line == 'A')
            {
                if (unlink_file (fname) < 0)
                    error (0, errno, "warning: cannot remove temp file %s", fname);
                error (1, 0, "aborted by user");
            }
            if (*line == 'c' || *line == 'C')
                break;
            if (*line == 'e' || *line == 'E')
                goto again;
            if (*line == '!')
            {
                reuse_log_message = 1;
                break;
            }
            (void) printf ("Unknown input\n");
        }
    }
    if (line)
        free (line);
    if (unlink_file (fname) < 0)
        error (0, errno, "warning: cannot remove temp file %s", fname);
    free (fname);
}
Ejemplo n.º 4
0
Archivo: repos.cpp Proyecto: acml/cvsnt
char *Name_Repository (const char *dir, const char *update_dir)
{
    FILE *fpin;
    const char *xupdate_dir;
    char *repos = NULL;
    size_t repos_allocated = 0;
    char *tmp=NULL;
    char *cp;

    TRACE(3,"Name_Repository(%s,%s)",PATCH_NULL(dir),PATCH_NULL(update_dir));
    if (update_dir && *update_dir)
		xupdate_dir = update_dir;
    else
		xupdate_dir = ".";

    if (dir != NULL)
    {
		size_t tmpsz=strlen (dir) + sizeof (CVSADM_REP) + 10;
		tmp = (char*)xmalloc (tmpsz+sizeof(CVSADM_VIRTREPOS));
		sprintf (tmp, "%s/%s", dir, CVSADM_VIRTREPOS);
		size_t tmplen1 = strlen(tmp);
		if(!isfile(tmp))
			sprintf(tmp, "%s/%s", dir, CVSADM_REP);
		size_t tmplen2 = strlen(tmp);
	    TRACE(3,"Name_Repository allocate tmp of size %d, len1=%d, len2=%d",(int)tmpsz,(int)tmplen1,(int)tmplen2);
    }
    else
	{
		tmp = xstrdup (CVSADM_VIRTREPOS);
	    TRACE(3,"Name_Repository dup tmp is len",(int)strlen(tmp));
		if(!isfile(tmp))
		{
			xfree(tmp);
			tmp=NULL;
			tmp=xstrdup(CVSADM_REP);
		    TRACE(3,"Name_Repository dup tmp is now len",(int)strlen(tmp));
		}
	}
    /*
     * The assumption here is that the repository is always contained in the
     * first line of the "Repository" file.
     */
    TRACE(3,"Name_Repository open %s",tmp);
    fpin = CVS_FOPEN (tmp, "r");

    if (fpin == NULL)
    {
	int save_errno = errno;
	char *cvsadm=NULL;

	if (dir != NULL)
	{
	    cvsadm = (char*)xmalloc (strlen (dir) + sizeof (CVSADM) + 100);
	    (void) sprintf (cvsadm, "%s/%s", dir, CVSADM);
	}
	else
	    cvsadm = xstrdup (CVSADM);
    TRACE(3,"Name_Repository failed to open %s so try %s",tmp,cvsadm);
	if (!isdir (cvsadm))
	{
	    error (0, 0, "in directory %s:", xupdate_dir);
	    error (1, 0, "there is no version here; do '%s checkout' first",
		   program_name);
	}
	xfree (cvsadm);
	cvsadm=NULL;

	if (existence_error (save_errno))
	{
	    /* FIXME: This is a very poorly worded error message.  It
	       occurs at least in the case where the user manually
	       creates a directory named CVS, so the error message
	       should be more along the lines of "CVS directory found
	       without administrative files; use CVS to create the CVS
	       directory, or rename it to something else if the
	       intention is to store something besides CVS
	       administrative files".  */
	    error (0, 0, "in directory %s:", xupdate_dir);
	    error (1, 0, "CVS directory without administration files present.  Cannot continue until this directory is deleted or renamed.");
	}

	error (1, save_errno, "cannot open %s", tmp);
    }

    TRACE(3,"Name_Repository opened %s ok so read a line",tmp);
    if (getline (&repos, &repos_allocated, fpin) < 0)
    {
	/* FIXME: should be checking for end of file separately.  */
	error (0, 0, "in directory %s:", xupdate_dir);
	error (1, errno, "cannot read %s", tmp);
    }
    if (fclose (fpin) < 0)
	error (0, errno, "cannot close %s", tmp);
    TRACE(3,"Name_Repository closed %s",tmp);
    xfree (tmp);
	tmp=NULL;

    TRACE(3,"Name_Repository read 1 %s",repos);
    if ((cp = strrchr (repos, '\n')) != NULL)
	*cp = '\0';			/* strip the newline */
    TRACE(3,"Name_Repository read 2 %s",repos);

    /*
     * If this is a relative repository pathname, turn it into an absolute
     * one by tacking on the CVSROOT environment variable. If the CVSROOT
     * environment variable is not set, die now.
     */
    TRACE(3,"Name_Repository isabsolute( %s )?",repos);
	char *newrepos=NULL;
    if (! isabsolute(repos))
    {

    TRACE(3,"Name_Repository isabsolute( %s )!",repos);
	if (current_parsed_root == NULL)
	{
	    error (0, 0, "in directory %s:", xupdate_dir);
	    error (0, 0, "must set the CVSROOT environment variable\n");
	    error (0, 0, "or specify the '-d' option to %s.", program_name);
	    error (1, 0, "illegal repository setting");
	}
	if (pathname_levels (repos) > 0)
	{
	    error (0, 0, "in directory %s:", xupdate_dir);
	    error (0, 0, "`..'-relative repositories are not supported.");
	    error (1, 0, "illegal source repository");
	}
	newrepos = (char*)xmalloc (strlen (current_parsed_root->directory) + strlen (repos) + 2);
	(void) sprintf (newrepos, "%s/%s", current_parsed_root->directory, repos);
#ifndef HAVE_GETLINE
	xfree (repos);
#else
	free(repos);
#endif
	repos = newrepos;
    }
	else
	{
    TRACE(3,"Name_Repository not isabsolute( %s )",repos);
	newrepos = (char*)xmalloc (strlen (repos) + 2);
	(void) sprintf (newrepos, "%s", repos);
#ifndef HAVE_GETLINE
	xfree (repos);
#else
	free(repos);
#endif
	repos = newrepos;
	}

    TRACE(3,"Name_Repository Sanitize_Repository_Name( %s )!",repos);
	Sanitize_Repository_Name (&repos);

    TRACE(3,"Name_Repository return ( %s )!",repos);
    return repos;
}
Ejemplo n.º 5
0
/* There are at least four functions for generating temporary
 * filenames.  We use mkstemp (BSD 4.3) if possible, else tempnam (SVID 3),
 * else mktemp (BSD 4.3), and as last resort tmpnam (POSIX).  Reason is that
 * mkstemp, tempnam, and mktemp both allow to specify the directory in which
 * the temporary file will be created.
 *
 * And the _correct_ way to use the deprecated functions probably involves
 * opening file descriptors using O_EXCL & O_CREAT and even doing the annoying
 * NFS locking thing, but until I hear of more problems, I'm not going to
 * bother.
 */
FILE *cvs_temp_file (char **filename)
{
    char *fn;
    FILE *fp;

    /* FIXME - I'd like to be returning NULL here in noexec mode, but I think
     * some of the rcs & diff functions which rely on a temp file run in
     * noexec mode too.
     */

    assert (filename != NULL);

#ifdef HAVE_MKSTEMP

    {
        int fd;

        fn = (char*)xmalloc (strlen (Tmpdir) + 11);
        sprintf (fn, "%s/%s", Tmpdir, "cvsXXXXXX" );
        fd = mkstemp (fn);

        /* a NULL return will be interpreted by callers as an error and
         * errno should still be set
         */
        if (fd == -1) fp = NULL;
        else if ((fp = CVS_FDOPEN (fd, "w+")) == NULL)
        {
            /* attempt to close and unlink the file since mkstemp returned sucessfully and
             * we believe it's been created and opened
             */
            int save_errno = errno;
            if (close (fd))
                error (0, errno, "Failed to close temporary file %s", fn_root(fn));
            if (CVS_UNLINK (fn))
                error (0, errno, "Failed to unlink temporary file %s", fn_root(fn));
            errno = save_errno;
        }

        if (fp == NULL) xfree (fn);
        /* mkstemp is defined to open mode 0600 using glibc 2.0.7+ */
        /* FIXME - configure can probably tell us which version of glibc we are
         * linking to and not chmod for 2.0.7+
         */
        else chmod (fn, 0600);

    }

#elif HAVE_TEMPNAM

    /* tempnam has been deprecated due to under-specification */

    fn = tempnam (Tmpdir, "cvs");
    if (fn == NULL) fp = NULL;
    else if ((fp = CVS_FOPEN (fn, "w+")) == NULL) xfree (fn);
    else chmod (fn, 0600);

    /* tempnam returns a pointer to a newly malloc'd string, so there's
     * no need for a xstrdup
     */

#elif HAVE_MKTEMP

    /* mktemp has been deprecated due to the BSD 4.3 specification specifying
     * that XXXXXX will be replaced by a PID and a letter, creating only 26
     * possibilities, a security risk, and a race condition.
     */

    {
        char *ifn;

        ifn = xmalloc (strlen (Tmpdir) + 11);
        sprintf (ifn, "%s/%s", Tmpdir, "cvsXXXXXX" );
        fn = mktemp (ifn);

        if (fn == NULL) fp = NULL;
        else fp = CVS_FOPEN (fn, "w+");

        if (fp == NULL) xfree (ifn);
        else chmod (fn, 0600);

    }

#else	/* use tmpnam if all else fails */

    /* tmpnam is deprecated */

    {
        char ifn[L_tmpnam + 1];

        fn = tmpnam (ifn);

        if (fn == NULL) fp = NULL;
        else if ((fp = CVS_FOPEN (ifn, "w+")) != NULL)
        {
            fn = xstrdup (ifn);
            chmod (fn, 0600);
        }

    }

#endif

    *filename = fn;
    return fp;
}
Ejemplo n.º 6
0
/* Rebuild the checked out administrative files in directory DIR.  */
int mkmodules (char *dir)
{
    struct saved_cwd cwd;
    char *temp;
    char *cp, *last, *fname;
#ifdef MY_NDBM
    DBM *db;
#endif
    FILE *fp;
    char *line = NULL;
    size_t line_allocated = 0;
    const struct admin_file *fileptr;
    mode_t mode;

    if (noexec)
	return 0;

    if (save_cwd (&cwd))
	error_exit ();

    if ( CVS_CHDIR (dir) < 0)
	error (1, errno, "cannot chdir to %s", dir);

    /*
     * First, do the work necessary to update the "modules" database.
     */
    temp = make_tempfile ();
    switch (checkout_file (CVSROOTADM_MODULES, dir, temp, NULL))
    {

	case 0:			/* everything ok */
#ifdef MY_NDBM
	    /* open it, to generate any duplicate errors */
	    if ((db = dbm_open (temp, O_RDONLY, 0666)) != NULL)
		dbm_close (db);
#else
	    write_dbmfile (temp);
	    rename_dbmfile (temp);
#endif
	    rename_rcsfile (temp, CVSROOTADM_MODULES);
	    break;

	default:
	    error (0, 0,
		"'cvs checkout' is less functional without a %s file",
		CVSROOTADM_MODULES);
	    break;
    }					/* switch on checkout_file() */

    if (unlink_file (temp) < 0
	&& !existence_error (errno))
	error (0, errno, "cannot remove %s", temp);
    xfree (temp);

    /* Checkout the files that need it in CVSROOT dir */
    for (fileptr = filelist; fileptr && fileptr->filename; fileptr++) {
	if (fileptr->errormsg == NULL)
	    continue;
	temp = make_tempfile ();
	if (checkout_file (fileptr->filename, dir, temp, NULL) == 0)
	    rename_rcsfile (temp, fileptr->filename);

	if (unlink_file (temp) < 0
	    && !existence_error (errno))
	    error (0, errno, "cannot remove %s", temp);
	xfree (temp);
    }

    fp = CVS_FOPEN (CVSROOTADM_CHECKOUTLIST, "r");
    if (fp)
    {
	/*
	 * File format:
	 *  [<whitespace>]<filename><whitespace><error message><end-of-line>
	 *
	 * comment lines begin with '#'
	 */
	while (getline (&line, &line_allocated, fp) >= 0)
	{
	    /* skip lines starting with # */
	    if (line[0] == '#')
		continue;

	    if ((last = strrchr (line, '\n')) != NULL)
		*last = '\0';			/* strip the newline */

	    /* Skip leading white space. */
	    for (fname = line;
		 *fname && isspace ((unsigned char) *fname);
		 fname++)
		;

	    /* Find end of filename. */
	    for (cp = fname; *cp && !isspace ((unsigned char) *cp); cp++)
		;
	    *cp = '\0';

	    if(isabsolute(fname) || pathname_levels(fname)>0 || !fncmp(fname,"checkoutlist"))
		{
			error(0,0,"Invalid filename '%s' in checkoutlist", fname);
			continue;
		}

	    temp = make_tempfile ();
	    if (checkout_file (fname, dir, temp, &mode) == 0)
	    {
	        chmod(temp,mode);
		rename_rcsfile (temp, fname);
	    }
	    else
	    {
		for (cp++;
		     cp < last && *last && isspace ((unsigned char) *last);
		     cp++)
		    ;
		if (cp < last && *cp)
		    error (0, 0, cp, fname);
	    }
	    if (unlink_file (temp) < 0
		&& !existence_error (errno))
		error (0, errno, "cannot remove %s", temp);
	    xfree (temp);
	}
	if (line)
	    xfree (line);
	if (ferror (fp))
	    error (0, errno, "cannot read %s", CVSROOTADM_CHECKOUTLIST);
	if (fclose (fp) < 0)
	    error (0, errno, "cannot close %s", CVSROOTADM_CHECKOUTLIST);
    }
    else
    {
	/* Error from CVS_FOPEN.  */
	if (!existence_error (errno))
	    error (0, errno, "cannot open %s", CVSROOTADM_CHECKOUTLIST);
    }

    if (restore_cwd (&cwd, NULL))
	error_exit ();
    free_cwd (&cwd);

    return (0);
}
Ejemplo n.º 7
0
/*
 * Builds a temporary file using setup_tmpfile() and invokes the user's
 * editor on the file.  The header garbage in the resultant file is then
 * stripped and the log message is stored in the "message" argument.
 * 
 * If REPOSITORY is non-NULL, process rcsinfo for that repository; if it
 * is NULL, use the CVSADM_TEMPLATE file instead.
 */
void do_editor (const char *dir, char **messagep, const char *repository, List *changes)
{
    static int reuse_log_message = 0;
    char *line;
    int line_length;
    size_t line_chars_allocated;
    char *fname;
    struct stat pre_stbuf, post_stbuf;
    int retcode = 0;

    if (noexec || reuse_log_message)
		return;

    /* Abort creation of temp file if no editor is defined */
    if (strcmp (Editor, "") == 0)
		error(1, 0, "no editor defined, must use -e or -m");

	/* Create a temporary file */
	/* FIXME - It's possible we should be relying on cvs_temp_file to open
	* the file here - we get race conditions otherwise.
	*/
	fname = cvs_temp_name ();
again:
	if ((fp = CVS_FOPEN (fname, "w+")) == NULL)
		error (1, 0, "cannot create temporary file %s", fname);

	if (*messagep)
	{
		fprintf (fp, "%s", *messagep);

		if ((*messagep)[0] == '\0' ||
			(*messagep)[strlen (*messagep) - 1] != '\n')
			fprintf (fp, "\n");
	}

    if (repository != NULL)
	{
		rcsinfo_param_t args;
		args.directory = Short_Repository(repository);
		args.message=NULL;
		/* tack templates on if necessary */
		TRACE(3,"run rcsinfo trigger");
		if(!run_trigger(&args,rcsinfo_proc) && args.message)
		{
			fprintf(fp,"%s",args.message);
			if (args.message[0] == '\0' || args.message[strlen(args.message) - 1] != '\n')
				fprintf (fp, "\n");
		}
	}
    else
    {
		FILE *tfp;
		char buf[1024];
		size_t n;
		size_t nwrite;

		/* Why "b"?  */
		tfp = CVS_FOPEN (CVSADM_TEMPLATE, "rb");
		if (tfp == NULL)
		{
			if (!existence_error (errno))
			error (1, errno, "cannot read %s", CVSADM_TEMPLATE);
		}
		else
		{
			while (!feof (tfp))
			{
				char *p = buf;
				n = fread (buf, 1, sizeof buf, tfp);
				nwrite = n;
				while (nwrite > 0)
				{
					n = fwrite (p, 1, nwrite, fp);
					nwrite -= n;
					p += n;
				}
				if (ferror (tfp))
					error (1, errno, "cannot read %s", CVSADM_TEMPLATE);
			}
			if (fclose (tfp) < 0)
				error (0, errno, "cannot close %s", CVSADM_TEMPLATE);
		}
	}

	fprintf (fp,"%s----------------------------------------------------------------------\n",CVSEDITPREFIX);
#ifdef _WIN32
#if (CVSNT_SPECIAL_BUILD_FLAG != 0)
	if (strcasecmp(CVSNT_SPECIAL_BUILD,"Suite")!=0)
#endif
	{
	fprintf (fp,"%s     Committed on the Free edition of March Hare Software CVSNT Server\n",CVSEDITPREFIX);
	fprintf (fp,"%s     Upgrade to CVS Suite for more features and support:\n",CVSEDITPREFIX);
	fprintf (fp,"%s           http://march-hare.com/cvsnt/\n",CVSEDITPREFIX);
	fprintf (fp,"%s----------------------------------------------------------------------\n",CVSEDITPREFIX);
    }
#endif
	fprintf (fp,"%sEnter Log.  Lines beginning with `%.*s' are removed automatically\n%s\n",
				CVSEDITPREFIX, CVSEDITPREFIXLEN, CVSEDITPREFIX,CVSEDITPREFIX);
	if (dir != NULL && *dir)
		fprintf (fp, "%sCommitting in %s\n%s\n", CVSEDITPREFIX,	dir, CVSEDITPREFIX);
	if (changes != NULL)
		setup_tmpfile (fp, CVSEDITPREFIX, changes);
	fprintf (fp,"%s----------------------------------------------------------------------\n",
				CVSEDITPREFIX);

	/* finish off the temp file */
	if (fclose (fp) == EOF)
		error (1, errno, "%s", fname);
	if ( CVS_STAT (fname, &pre_stbuf) == -1)
		pre_stbuf.st_mtime = 0;

	/* run the editor */
	run_setup (Editor);
	run_arg (fname);
	if ((retcode = run_exec (true)) != 0)
		error (0, retcode == -1 ? errno : 0, "warning: editor session failed");

	/* put the entire message back into the *messagep variable */
	fp = open_file (fname, "r");

	if (*messagep)
		xfree (*messagep);

	if ( CVS_STAT (fname, &post_stbuf) != 0)
		error (1, errno, "cannot find size of temp file %s", fname);

	if (post_stbuf.st_size == 0)
		*messagep = NULL;
	else
	{
		/* On NT, we might read less than st_size bytes, but we won't
		read more.  So this works.  */
		*messagep = (char *) xmalloc (post_stbuf.st_size + 1);
 		*messagep[0] = '\0';
	}

	line = NULL;
	line_chars_allocated = 0;

	if (*messagep)
	{
		size_t message_len = post_stbuf.st_size + 1;
		size_t offset = 0;
		while (1)
		{
			line_length = getline (&line, &line_chars_allocated, fp);
			if (line_length == -1)
			{
				if (ferror (fp))
					error (0, errno, "warning: cannot read %s", fname);
				break;
			}
			if (strncmp (line, CVSEDITPREFIX, CVSEDITPREFIXLEN) == 0)
				continue;
			if (offset + line_length >= message_len)
			expand_string (messagep, &message_len, offset + line_length + 1);
			strcpy (*messagep + offset, line);
			offset += line_length;
		}
	}
	
	if (fclose (fp) < 0)
		error (0, errno, "warning: cannot close %s", fname);

	if (pre_stbuf.st_mtime == post_stbuf.st_mtime || *messagep == NULL || strcmp (*messagep, "\n") == 0)
	{
		for (;;)
		{
			printf ("\nLog message unchanged or not specified\n");
			printf ("a)bort, c)ontinue, e)dit, !)reuse this message unchanged for remaining dirs\n");
			printf ("Action: (continue) ");
			fflush (stderr);
			fflush (stdout);
			line_length = getline (&line, &line_chars_allocated, stdin);
			if (line_length < 0)
			{
				error (0, errno, "cannot read from stdin");
				if (unlink_file (fname) < 0)
					error (0, errno, "warning: cannot remove temp file %s", fname);
				error (1, 0, "aborting");
			}
			else if (line_length == 0 || *line == '\n' || *line == 'c' || *line == 'C')
				break;
			if (*line == 'a' || *line == 'A')
			{
				if (unlink_file (fname) < 0)
					error (0, errno, "warning: cannot remove temp file %s", fname);
				error (1, 0, "aborted by user");
			}
			if (*line == 'e' || *line == 'E')
				goto again;
			if (*line == '!')
			{
				reuse_log_message = 1;
				break;
			}
			printf ("Unknown input\n");
		}
    }
    if (line)
		xfree (line);
    if (unlink_file (fname) < 0)
		error (0, errno, "warning: cannot remove temp file %s", fname);
    xfree (fname);
}