Example #1
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);
}
Example #2
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);
}