src/bin/psql/command.c
static bool
do_edit(const char *filename_arg, PQExpBuffer query_buf,
		int lineno, bool discard_on_quit, bool *edited)
{
	char		fnametmp[MAXPGPATH];
	FILE	   *stream = NULL;
	const char *fname;
	bool		error = false;
	int			fd;
	struct stat before,
				after;

	if (filename_arg)
		fname = filename_arg;
	else
	{
		/* make a temp file to edit */
#ifndef WIN32
		const char *tmpdir = getenv("TMPDIR");

		if (!tmpdir)
			tmpdir = "/tmp";
#else
		char		tmpdir[MAXPGPATH];
		int			ret;

		ret = GetTempPath(MAXPGPATH, tmpdir);
		if (ret == 0 || ret > MAXPGPATH)
		{
			pg_log_error("could not locate temporary directory: %s",
						 !ret ? strerror(errno) : "");
			return false;
		}
#endif

		/*
		 * No canonicalize_path() here. EDIT.EXE run from CMD.EXE prepends the
		 * current directory to the supplied path unless we use only
		 * backslashes, so we do that.
		 */
#ifndef WIN32
		snprintf(fnametmp, sizeof(fnametmp), "%s%spsql.edit.%d.sql", tmpdir,
				 "/", (int) getpid());
#else
		snprintf(fnametmp, sizeof(fnametmp), "%s%spsql.edit.%d.sql", tmpdir,
				 "" /* trailing separator already present */ , (int) getpid());
#endif

		fname = (const char *) fnametmp;

		fd = open(fname, O_WRONLY | O_CREAT | O_EXCL, 0600);
		if (fd != -1)
			stream = fdopen(fd, "w");

		if (fd == -1 || !stream)
		{
			pg_log_error("could not open temporary file \"%s\": %m", fname);
			error = true;
		}
		else
		{
			unsigned int ql = query_buf->len;

			/* force newline-termination of what we send to editor */
			if (ql > 0 && query_buf->data[ql - 1] != '\n')
			{
				appendPQExpBufferChar(query_buf, '\n');
				ql++;
			}

			if (fwrite(query_buf->data, 1, ql, stream) != ql)
			{
				pg_log_error("%s: %m", fname);

				if (fclose(stream) != 0)
					pg_log_error("%s: %m", fname);

				if (remove(fname) != 0)
					pg_log_error("%s: %m", fname);

				error = true;
			}
			else if (fclose(stream) != 0)
			{
				pg_log_error("%s: %m", fname);
				if (remove(fname) != 0)
					pg_log_error("%s: %m", fname);
				error = true;
			}
			else
			{
				struct utimbuf ut;

				/*
				 * Try to set the file modification time of the temporary file
				 * a few seconds in the past.  Otherwise, the low granularity
				 * (one second, or even worse on some filesystems) that we can
				 * portably measure with stat(2) could lead us to not
				 * recognize a modification, if the user typed very quickly.
				 *
				 * This is a rather unlikely race condition, so don't error
				 * out if the utime(2) call fails --- that would make the cure
				 * worse than the disease.
				 */
				ut.modtime = ut.actime = time(NULL) - 2;
				(void) utime(fname, &ut);
			}
		}
	}

	if (!error && stat(fname, &before) != 0)
	{
		pg_log_error("%s: %m", fname);
		error = true;
	}

	/* call editor */
	if (!error)
		error = !editFile(fname, lineno);

	if (!error && stat(fname, &after) != 0)
	{
		pg_log_error("%s: %m", fname);
		error = true;
	}

	/* file was edited if the size or modification time has changed */
	if (!error &&
		(before.st_size != after.st_size ||
		 before.st_mtime != after.st_mtime))
	{
		stream = fopen(fname, PG_BINARY_R);
		if (!stream)
		{
			pg_log_error("%s: %m", fname);
			error = true;
		}
		else
		{
			/* read file back into query_buf */
			char		line[1024];

			resetPQExpBuffer(query_buf);
			while (fgets(line, sizeof(line), stream) != NULL)
				appendPQExpBufferStr(query_buf, line);

			if (ferror(stream))
			{
				pg_log_error("%s: %m", fname);
				error = true;
				resetPQExpBuffer(query_buf);
			}
			else if (edited)
			{
				*edited = true;
			}

			fclose(stream);
		}
	}
	else
	{
		/*
		 * If the file was not modified, and the caller requested it, discard
		 * the query buffer.
		 */
		if (discard_on_quit)
			resetPQExpBuffer(query_buf);
	}

	/* remove temp file */
	if (!filename_arg)
	{
		if (remove(fname) == -1)
		{
			pg_log_error("%s: %m", fname);
			error = true;
		}
	}

	return !error;
}
/*
 * do_edit -- handler for \e
 *
 * If you do not specify a filename, the current query buffer will be copied
 * into a temporary file.
 *
 * After this function is done, the resulting file will be copied back into the
 * query buffer.  As an exception to this, the query buffer will be emptied
 * if the file was not modified (or the editor failed) and the caller passes
 * "discard_on_quit" = true.
 *
 * If "edited" isn't NULL, *edited will be set to true if the query buffer
 * is successfully replaced.
 */
- handler for \e
If you do not specify a filename, the current query buffer will be copied
into a temporary file.

After this function is done, the resulting file will be copied back into the
query buffer.  As an exception to this, the query buffer will be emptied
if the file was not modified (or the editor failed) and the caller passes
"discard_on_quit" = true.

If "edited" isn't NULL, *edited will be set to true if the query buffer
is successfully replaced.
- handler for \ e If you do not specify a filename the current query buffer will be copied into a temporary file . After this function is done the resulting file will be copied back into the query buffer . As an exception to this the query buffer will be emptied if the file was not modified ( or the editor failed ) and the caller passes " discard_on_quit " = true . If " edited " isn ' t NULL * edited will be set to true if the query buffer is successfully replaced .
static bool
do_edit(const char *filename_arg, PQExpBuffer query_buf,
		int lineno, bool discard_on_quit, bool *edited)
{
	char		fnametmp[MAXPGPATH];
	FILE	   *stream = NULL;
	const char *fname;
	bool		error = false;
	int			fd;
	struct stat before,
				after;
	if (filename_arg)
		fname = filename_arg;
	else
	{
#ifndef WIN32
		const char *tmpdir = getenv("TMPDIR");
		if (!tmpdir)
			tmpdir = "/tmp";
#else
		char		tmpdir[MAXPGPATH];
		int			ret;
		ret = GetTempPath(MAXPGPATH, tmpdir);
		if (ret == 0 || ret > MAXPGPATH)
		{
			pg_log_error("could not locate temporary directory: %s",
						 !ret ? strerror(errno) : "");
			return false;
		}
#endif
#ifndef WIN32
		snprintf(fnametmp, sizeof(fnametmp), "%s%spsql.edit.%d.sql", tmpdir,
				 "/", (int) getpid());
#else
		snprintf(fnametmp, sizeof(fnametmp), "%s%spsql.edit.%d.sql", tmpdir,
				 ""  , (int) getpid());
#endif
		fname = (const char *) fnametmp;
		fd = open(fname, O_WRONLY | O_CREAT | O_EXCL, 0600);
		if (fd != -1)
			stream = fdopen(fd, "w");
		if (fd == -1 || !stream)
		{
			pg_log_error("could not open temporary file \"%s\": %m", fname);
			error = true;
		}
		else
		{
			unsigned int ql = query_buf->len;
			if (ql > 0 && query_buf->data[ql - 1] != '\n')
			{
				appendPQExpBufferChar(query_buf, '\n');
				ql++;
			}
			if (fwrite(query_buf->data, 1, ql, stream) != ql)
			{
				pg_log_error("%s: %m", fname);
				if (fclose(stream) != 0)
					pg_log_error("%s: %m", fname);
				if (remove(fname) != 0)
					pg_log_error("%s: %m", fname);
				error = true;
			}
			else if (fclose(stream) != 0)
			{
				pg_log_error("%s: %m", fname);
				if (remove(fname) != 0)
					pg_log_error("%s: %m", fname);
				error = true;
			}
			else
			{
				struct utimbuf ut;
				ut.modtime = ut.actime = time(NULL) - 2;
				(void) utime(fname, &ut);
			}
		}
	}
	if (!error && stat(fname, &before) != 0)
	{
		pg_log_error("%s: %m", fname);
		error = true;
	}
	if (!error)
		error = !editFile(fname, lineno);
	if (!error && stat(fname, &after) != 0)
	{
		pg_log_error("%s: %m", fname);
		error = true;
	}
	if (!error &&
		(before.st_size != after.st_size ||
		 before.st_mtime != after.st_mtime))
	{
		stream = fopen(fname, PG_BINARY_R);
		if (!stream)
		{
			pg_log_error("%s: %m", fname);
			error = true;
		}
		else
		{
			char		line[1024];
			resetPQExpBuffer(query_buf);
			while (fgets(line, sizeof(line), stream) != NULL)
				appendPQExpBufferStr(query_buf, line);
			if (ferror(stream))
			{
				pg_log_error("%s: %m", fname);
				error = true;
				resetPQExpBuffer(query_buf);
			}
			else if (edited)
			{
				*edited = true;
			}
			fclose(stream);
		}
	}
	else
	{
		if (discard_on_quit)
			resetPQExpBuffer(query_buf);
	}
	if (!filename_arg)
	{
		if (remove(fname) == -1)
		{
			pg_log_error("%s: %m", fname);
			error = true;
		}
	}
	return !error;
}
static bool do_edit ( const char * filename_arg , PQExpBuffer query_buf , int lineno , bool discard_on_quit , bool * edited ) { char fnametmp [ MAXPGPATH ] ; FILE * stream = NULL ; const char * fname ; bool error = false ; int fd ; struct stat before , after ; if ( filename_arg ) fname = filename_arg ; else { #ifndef WIN32 const char * tmpdir = getenv ( " " ) ; if ( ! tmpdir ) tmpdir = " " ; #else char tmpdir [ MAXPGPATH ] ; int ret ; ret = GetTempPath ( MAXPGPATH , tmpdir ) ; if ( ret == 0 || ret > MAXPGPATH ) { pg_log_error ( " " , ! ret ? strerror ( errno ) : " " ) ; return false ; } #endif #ifndef WIN32 snprintf ( fnametmp , sizeof ( fnametmp ) , " " , tmpdir , " " , ( int ) getpid ( ) ) ; #else snprintf ( fnametmp , sizeof ( fnametmp ) , " " , tmpdir , " " , ( int ) getpid ( ) ) ; #endif fname = ( const char * ) fnametmp ; fd = open ( fname , O_WRONLY | O_CREAT | O_EXCL , 0600 ) ; if ( fd != -1 ) stream = fdopen ( fd , " " ) ; if ( fd == -1 || ! stream ) { pg_log_error ( " \" \" " , fname ) ; error = true ; } else { unsigned int ql = query_buf -> len ; if ( ql > 0 && query_buf -> data [ ql - 1 ] != ' \n ' ) { appendPQExpBufferChar ( query_buf , ' \n ' ) ; ql ++ ; } if ( fwrite ( query_buf -> data , 1 , ql , stream ) != ql ) { pg_log_error ( " " , fname ) ; if ( fclose ( stream ) != 0 ) pg_log_error ( " " , fname ) ; if ( remove ( fname ) != 0 ) pg_log_error ( " " , fname ) ; error = true ; } else if ( fclose ( stream ) != 0 ) { pg_log_error ( " " , fname ) ; if ( remove ( fname ) != 0 ) pg_log_error ( " " , fname ) ; error = true ; } else { struct utimbuf ut ; ut . modtime = ut . actime = time ( NULL ) - 2 ; ( void ) utime ( fname , & ut ) ; } } } if ( ! error && stat ( fname , & before ) != 0 ) { pg_log_error ( " " , fname ) ; error = true ; } if ( ! error ) error = ! editFile ( fname , lineno ) ; if ( ! error && stat ( fname , & after ) != 0 ) { pg_log_error ( " " , fname ) ; error = true ; } if ( ! error && ( before . st_size != after . st_size || before . st_mtime != after . st_mtime ) ) { stream = fopen ( fname , PG_BINARY_R ) ; if ( ! stream ) { pg_log_error ( " " , fname ) ; error = true ; } else { char line [ 1024 ] ; resetPQExpBuffer ( query_buf ) ; while ( fgets ( line , sizeof ( line ) , stream ) != NULL ) appendPQExpBufferStr ( query_buf , line ) ; if ( ferror ( stream ) ) { pg_log_error ( " " , fname ) ; error = true ; resetPQExpBuffer ( query_buf ) ; } else if ( edited ) { * edited = true ; } fclose ( stream ) ; } } else { if ( discard_on_quit ) resetPQExpBuffer ( query_buf ) ; } if ( ! filename_arg ) { if ( remove ( fname ) == -1 ) { pg_log_error ( " " , fname ) ; error = true ; } } return ! error ; }
do_edit -- handler for \e
 If you do not specify a filename, the current query buffer will be copied
 into a temporary file.
do_edit -- handler for \ e If you do not specify a filename the current query buffer will be copied into a temporary file .
/* make a temp file to edit */ /*
		 * No canonicalize_path() here. EDIT.EXE run from CMD.EXE prepends the
		 * current directory to the supplied path unless we use only
		 * backslashes, so we do that.
		 */ /* trailing separator already present */ /* force newline-termination of what we send to editor */ /*
				 * Try to set the file modification time of the temporary file
				 * a few seconds in the past.  Otherwise, the low granularity
				 * (one second, or even worse on some filesystems) that we can
				 * portably measure with stat(2) could lead us to not
				 * recognize a modification, if the user typed very quickly.
				 *
				 * This is a rather unlikely race condition, so don't error
				 * out if the utime(2) call fails --- that would make the cure
				 * worse than the disease.
				 */ /* call editor */ /* file was edited if the size or modification time has changed */ /* read file back into query_buf */ /*
		 * If the file was not modified, and the caller requested it, discard
		 * the query buffer.
		 */ /* remove temp file */