diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 52aedca459c2001bb490c2b77e9e8f88a7a2f692..982d864cb35b60fd4f844eb481734b78b0bc9a79 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -3314,7 +3314,7 @@ main(int argc, char *argv[])
 		fprintf(stderr, "%s", authwarning);
 
 	/* Get directory specification used to start this executable */
-	strcpy(bin_dir, argv[0]);
+	strlcpy(bin_dir, argv[0], sizeof(bin_dir));
 	get_parent_directory(bin_dir);
 
 	printf(_("\nSuccess. You can now start the database server using:\n\n"
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 092678604ed37a60ba12d263cb2d8af34598eb30..c5ec9819ea1e9bcbcb5f289107c3a79d05f79f75 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -2052,10 +2052,10 @@ process_file(char *filename, bool single_txn, bool use_relative_path)
 		 * relative pathname, then prepend all but the last pathname component
 		 * of the current script to this pathname.
 		 */
-		if (use_relative_path && pset.inputfile && !is_absolute_path(filename)
-			&& !has_drive_prefix(filename))
+		if (use_relative_path && pset.inputfile &&
+			!is_absolute_path(filename) && !has_drive_prefix(filename))
 		{
-			snprintf(relpath, MAXPGPATH, "%s", pset.inputfile);
+			strlcpy(relpath, pset.inputfile, sizeof(relpath));
 			get_parent_directory(relpath);
 			join_path_components(relpath, relpath, filename);
 			canonicalize_path(relpath);
diff --git a/src/port/path.c b/src/port/path.c
index 738b5cc547c183eff96b56de82063616bcab0397..4b4b56d3ad973b17032e990f7edd4a3f25d748a6 100644
--- a/src/port/path.c
+++ b/src/port/path.c
@@ -170,6 +170,8 @@ make_native_path(char *filename)
 /*
  * join_path_components - join two path components, inserting a slash
  *
+ * We omit the slash if either given component is empty.
+ *
  * ret_path is the output area (must be of size MAXPGPATH)
  *
  * ret_path can be the same as head, but not the same as tail.
@@ -182,38 +184,22 @@ join_path_components(char *ret_path,
 		strlcpy(ret_path, head, MAXPGPATH);
 
 	/*
-	 * Remove any leading "." and ".." in the tail component, adjusting head
-	 * as needed.
+	 * Remove any leading "." in the tail component.
+	 *
+	 * Note: we used to try to remove ".." as well, but that's tricky to get
+	 * right; now we just leave it to be done by canonicalize_path() later.
 	 */
-	for (;;)
-	{
-		if (tail[0] == '.' && IS_DIR_SEP(tail[1]))
-		{
-			tail += 2;
-		}
-		else if (tail[0] == '.' && tail[1] == '\0')
-		{
-			tail += 1;
-			break;
-		}
-		else if (tail[0] == '.' && tail[1] == '.' && IS_DIR_SEP(tail[2]))
-		{
-			trim_directory(ret_path);
-			tail += 3;
-		}
-		else if (tail[0] == '.' && tail[1] == '.' && tail[2] == '\0')
-		{
-			trim_directory(ret_path);
-			tail += 2;
-			break;
-		}
-		else
-			break;
-	}
+	while (tail[0] == '.' && IS_DIR_SEP(tail[1]))
+		tail += 2;
+
 	if (*tail)
+	{
+		/* only separate with slash if head wasn't empty */
 		snprintf(ret_path + strlen(ret_path), MAXPGPATH - strlen(ret_path),
-		/* only add slash if there is something already in head */
-				 "%s%s", head[0] ? "/" : "", tail);
+				 "%s%s",
+				 (*(skip_drive(head)) != '\0') ? "/" : "",
+				 tail);
+	}
 }
 
 
@@ -705,6 +691,15 @@ get_home_path(char *ret_path)
  *
  * Modify the given string in-place to name the parent directory of the
  * named file.
+ *
+ * If the input is just a file name with no directory part, the result is
+ * an empty string, not ".".  This is appropriate when the next step is
+ * join_path_components(), but might need special handling otherwise.
+ *
+ * Caution: this will not produce desirable results if the string ends
+ * with "..".  For most callers this is not a problem since the string
+ * is already known to name a regular file.  If in doubt, apply
+ * canonicalize_path() first.
  */
 void
 get_parent_directory(char *path)