Index: src/apps/packageinstaller/PackageItem.cpp
===================================================================
--- src/apps/packageinstaller/PackageItem.cpp	(wersja 31688)
+++ src/apps/packageinstaller/PackageItem.cpp	(kopia robocza)
@@ -19,6 +19,8 @@
 #include <fs_info.h>
 #include "zlib.h"
 
+#include <string.h>
+
 // Macro reserved for later localization
 #define T(x) x
 
@@ -160,25 +162,6 @@
 }
 
 
-int32
-PackageItem::ItemExists(const char *name)
-{
-	// TODO: this function doesn't really fit in, the GUI should be separated
-	// from the package engine completely
-
-	BString alertString = "The ";
-
-	alertString << ItemKind() << " named \'" << name << "\' ";
-	alertString << T("already exists in the given path. Should I replace "
-		"the existing file with the one from this package?");
-
-	BAlert *alert = new BAlert(T("file_exists"), alertString.String(),
-		T("Yes"), T("No"), T("Abort"));
-
-	return alert->Go();
-}
-
-
 status_t
 PackageItem::InitPath(const char *path, BPath *destination)
 {
@@ -452,22 +435,24 @@
 
 
 status_t
-PackageDirectory::WriteToPath(const char *path, BPath *final)
+PackageDirectory::WriteToPath(const char *path, ItemState *state)
 {
-	BPath destination;
+	BPath *destination = &(state->destination);
 	status_t ret;
 	parser_debug("Directory: %s WriteToPath() called!\n", fPath.String());
 
-	ret = InitPath(path, &destination);
+	ret = InitPath(path, destination);
+	parser_debug("Ret: %d %s\n", ret, strerror(ret));
 	if (ret != B_OK)
 		return ret;
 
 	// Since Haiku is single-user right now, we give the newly
 	// created directory default permissions
-	ret = create_directory(destination.Path(), kDefaultMode);
+	ret = create_directory(destination->Path(), kDefaultMode);
+	parser_debug("Create dir ret: %d %s\n", ret, strerror(ret));
 	if (ret != B_OK)
 		return ret;
-	BDirectory dir(destination.Path());
+	BDirectory dir(destination->Path());
 	parser_debug("Directory created!\n");
 
 	if (fCreationTime)
@@ -479,11 +464,9 @@
 	// Since directories can only have attributes in the offset section,
 	// we can check here whether it is necessary to continue
 	if (fOffset)
-		ret = HandleAttributes(&destination, &dir, "FoDa");
-	
-	if (final)
-		*final = destination;
+		ret = HandleAttributes(destination, &dir, "FoDa");
 
+	parser_debug("Ret: %d %s\n", ret, strerror(ret));
 	return ret;
 }
 
@@ -513,43 +496,56 @@
 
 
 status_t
-PackageFile::WriteToPath(const char *path, BPath *final)
+PackageFile::WriteToPath(const char *path, ItemState *state)
 {
-	BPath destination;
-	status_t ret;
+	BFile file;
+
+	if (state == NULL)
+		return B_ERROR;
+
+	BPath *destination = &(state->destination);
+	status_t ret = B_OK;
 	parser_debug("File: %s WriteToPath() called!\n", fPath.String());
 
-	ret = InitPath(path, &destination);
-	if (ret != B_OK)
-		return ret;
+	if (state->status == B_NO_INIT || destination->InitCheck() != B_OK) {
+		ret = InitPath(path, destination);
+		if (ret != B_OK)
+			return ret;
 
-	BFile file(destination.Path(),
-		B_WRITE_ONLY | B_CREATE_FILE | B_FAIL_IF_EXISTS);
-	ret = file.InitCheck();
-	if (ret == B_FILE_EXISTS) {
-		int32 selection = ItemExists(destination.Leaf());
-		switch (selection) {
-			case 0:
-				ret = file.SetTo(destination.Path(),
+		ret = file.SetTo(destination->Path(),
+			B_WRITE_ONLY | B_CREATE_FILE | B_FAIL_IF_EXISTS);
+		if (ret == B_ENTRY_NOT_FOUND) {
+			BPath directory;
+			destination->GetParent(&directory);
+			if (create_directory(directory.Path(), kDefaultMode) != B_OK)
+				return B_ERROR;
+
+			ret = file.SetTo(destination->Path(), B_WRITE_ONLY | B_CREATE_FILE);
+		} else if (ret == B_FILE_EXISTS)
+			state->status = B_FILE_EXISTS;
+
+		if (ret != B_OK)
+			return ret;
+	}
+
+	if (state->status == B_FILE_EXISTS) {
+		switch (state->policy) {
+			case P_EXISTS_OVERWRITE:
+				ret = file.SetTo(destination->Path(), 
 					B_WRITE_ONLY | B_ERASE_FILE);
-				if (ret != B_OK)
-					return ret;
 				break;
-			case 1:
+
+			case P_EXISTS_NONE:
+			case P_EXISTS_ASK:
+				ret = B_FILE_EXISTS;
+				break;
+
+			case P_EXISTS_SKIP:
 				return B_OK;
-			default:
-				return B_FILE_EXISTS;
 		}
-	} else if (ret == B_ENTRY_NOT_FOUND) {
-		BPath directory;
-		destination.GetParent(&directory);
-		if (create_directory(directory.Path(), kDefaultMode) != B_OK)
-			return B_ERROR;
+	}
 
-		ret = file.SetTo(destination.Path(), B_WRITE_ONLY | B_CREATE_FILE);
-		if (ret != B_OK)
-			return ret;
-	} else if (ret != B_OK)
+	if (ret != B_OK)
 		return ret;
 
 	parser_debug(" File created!\n");
@@ -637,9 +633,6 @@
 		delete[] temp;
 	}
 
-	if (final)
-		*final = destination;
-
 	return ret;
 }
 
@@ -665,73 +658,86 @@
 
 
 status_t
-PackageLink::WriteToPath(const char *path, BPath *final)
+PackageLink::WriteToPath(const char *path, ItemState *state)
 {
+	if (state == NULL)
+		return B_ERROR;
+
+	status_t ret = B_OK;
+	BSymLink symlink;
 	parser_debug("Symlink: %s WriteToPath() called!\n", fPath.String());
 
-	BPath destination;
-	status_t ret = InitPath(path, &destination);
-	if (ret != B_OK)
-		return ret;
+	BPath *destination = &(state->destination);
+	BDirectory *dir = &(state->parent);
 
-	BString linkName(destination.Leaf());
-	parser_debug("%s:%s:%s\n", fPath.String(), destination.Path(),
-		linkName.String());
+	if (state->status == B_NO_INIT || destination->InitCheck() != B_OK
+		|| dir->InitCheck() != B_OK) {
+		// Not yet initialized
+		ret = InitPath(path, destination);
+		if (ret != B_OK)
+			return ret;
 
-	BPath dirPath;
-	ret = destination.GetParent(&dirPath);
-	BDirectory dir(dirPath.Path());
+		BString linkName(destination->Leaf());
+		parser_debug("%s:%s:%s\n", fPath.String(), destination->Path(),
+			linkName.String());
 
-	ret = dir.InitCheck();
-	if (ret == B_ENTRY_NOT_FOUND) {
-		if ((ret = create_directory(dirPath.Path(), kDefaultMode)) != B_OK) {
-			parser_debug("create_directory()) failed\n");
-			return B_ERROR;
+		BPath dirPath;
+		ret = destination->GetParent(&dirPath);
+		ret = dir->SetTo(dirPath.Path());
+
+		if (ret == B_ENTRY_NOT_FOUND) {
+			if ((ret = create_directory(dirPath.Path(), kDefaultMode)) != B_OK) {
+				parser_debug("create_directory()) failed\n");
+				return B_ERROR;
+			}
 		}
+		if (ret != B_OK) {
+			parser_debug("destination InitCheck failed %s for %s\n", strerror(ret), dirPath.Path());
+			return ret;
+		}
+
+		ret = dir->CreateSymLink(destination->Path(), fLink.String(), &symlink);
+		if (ret == B_FILE_EXISTS) {
+			// We need to check if the existing symlink is pointing at the same path
+			// as our new one - if not, let's prompt the user
+			symlink.SetTo(destination->Path());
+			BPath oldLink;
+
+			ret = symlink.MakeLinkedPath(dir, &oldLink);
+			chdir(dirPath.Path());
+
+			if (ret == B_BAD_VALUE || oldLink != fLink.String())
+				state->status = ret = B_FILE_EXISTS;
+			else
+				ret = B_OK;
+		}
 	}
-	if (ret != B_OK) {
-		parser_debug("destination InitCheck failed %s for %s\n", strerror(ret), dirPath.Path());
-		return ret;
-	}
 
-	BSymLink symlink;
-	ret = dir.CreateSymLink(destination.Path(), fLink.String(), &symlink);
-	if (ret == B_FILE_EXISTS) {
-		// We need to check if the existing symlink is pointing at the same path
-		// as our new one - if not, let's prompt the user
-		symlink.SetTo(destination.Path());
-		BPath oldLink;
+	if (state->status == B_FILE_EXISTS) {
+		switch (state->policy) {
+			case P_EXISTS_OVERWRITE:
+			{
+				BEntry entry;
+				ret = entry.SetTo(destination->Path());
+				if (ret != B_OK)
+					return ret;
 
-		ret = symlink.MakeLinkedPath(&dir, &oldLink);
-		chdir(dirPath.Path());
+				entry.Remove();
+				ret = dir->CreateSymLink(destination->Path(), fLink.String(),
+					&symlink);
+				break;
+			}
 
-		if (ret == B_BAD_VALUE || oldLink != fLink.String()) {
-			// The old symlink is different (or not a symlink) - ask the user
-			int32 selection = ItemExists(destination.Leaf());
-			switch (selection) {
-				case 0:
-				{
-					symlink.Unset();
-					BEntry entry;
-					ret = entry.SetTo(destination.Path());
-					if (ret != B_OK)
-						return ret;
+			case P_EXISTS_NONE:
+			case P_EXISTS_ASK:
+				ret = B_FILE_EXISTS;
+				break;
 
-					entry.Remove();
-					ret = dir.CreateSymLink(destination.Path(), fLink.String(),
-						&symlink);
-					break;
-				}
-				case 1:
-					parser_debug("Skipping already existent SymLink\n");
-					return B_OK;
-				default:
-					ret = B_FILE_EXISTS;
-			}
-		} else {
-			ret = B_OK;
+			case P_EXISTS_SKIP:
+				return B_OK;
 		}
 	}
+
 	if (ret != B_OK) {
 		parser_debug("CreateSymLink failed\n");
 		return ret;
@@ -756,13 +762,9 @@
 
 	if (fOffset) {
 		// Symlinks also seem to have attributes - so parse them
-		ret = HandleAttributes(&destination, &symlink, "LnDa");
+		ret = HandleAttributes(destination, &symlink, "LnDa");
 	}
 
-	if (final) {
-		*final = destination;
-	}
-
 	return ret;
 }
 
Index: src/apps/packageinstaller/PackageItem.h
===================================================================
--- src/apps/packageinstaller/PackageItem.h	(wersja 31688)
+++ src/apps/packageinstaller/PackageItem.h	(kopia robocza)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, Haiku, Inc.
+ * Copyright (c) 2007-2009, Haiku, Inc.
  * Distributed under the terms of the MIT license.
  *
  * Author:
@@ -15,6 +15,7 @@
 #include <File.h>
 #include <Path.h>
 #include <String.h>
+#include <Directory.h>
 
 
 // Local macro for the parser debug output
@@ -32,10 +33,38 @@
 	P_USER_PATH
 };
 
+// Existant item overwriting policy of a single file
+enum {
+	P_EXISTS_ASK = 0,
+	P_EXISTS_OVERWRITE,
+	P_EXISTS_SKIP,
+	P_EXISTS_ABORT,
+	P_EXISTS_NONE
+};
+
 extern status_t inflate_data(uint8* in, uint32 inSize, uint8* out,
 	uint32 outSize);
 
 
+struct ItemState {
+	ItemState() : policy(P_EXISTS_NONE), status(B_NO_INIT) {}
+	~ItemState() {}
+
+	inline void Reset(int32 currentPolicy) 
+	{ 
+		destination.Unset();
+		parent.Unset();
+		status = B_NO_INIT;
+		policy = currentPolicy;
+	}
+
+	BPath destination;
+	BDirectory parent;
+	uint8 policy;
+	status_t status;
+};
+
+
 class PackageItem {
 public:
 							PackageItem(BFile* parent, const BString& path,
@@ -44,14 +73,13 @@
 	virtual					~PackageItem();
 
 	virtual	status_t		WriteToPath(const char* path = NULL,
-								BPath* final = NULL) = 0;
+								ItemState *state = NULL) = 0;
 	virtual	void			SetTo(BFile* parent, const BString& path,
 								uint8 type, uint32 ctime, uint32 mtime,
 								uint64 offset = 0, uint64 size = 0);
+	virtual	const char*		ItemKind() = 0;
 
 protected:
-	virtual	const char*		ItemKind() = 0;
-			int32			ItemExists(const char* name);
 			status_t		InitPath(const char* path, BPath* destination);
 			status_t		HandleAttributes(BPath* destination, BNode* node, 
 								const char* header);
@@ -84,9 +112,7 @@
 								uint64 offset = 0, uint64 size = 0);
 
 	virtual	status_t		WriteToPath(const char* path = NULL,
-								BPath* final = NULL);
-
-protected:
+								ItemState *state = NULL);
 	virtual	const char*		ItemKind();
 };
 
@@ -100,9 +126,7 @@
 								const BString& signature, uint32 mode);
 
 	virtual	status_t		WriteToPath(const char* path = NULL,
-								BPath* final = NULL);
-
-protected:
+								ItemState *state = NULL);
 	virtual	const char*		ItemKind();
 
 private:
@@ -123,9 +147,7 @@
 								uint64 size = 0);
 
 	virtual	status_t		WriteToPath(const char* path = NULL,
-								BPath* final = NULL);
-
-protected:
+								ItemState *state = NULL);
 	virtual	const char*		ItemKind();
 
 private:
Index: src/apps/packageinstaller/PackageView.cpp
===================================================================
--- src/apps/packageinstaller/PackageView.cpp	(wersja 31688)
+++ src/apps/packageinstaller/PackageView.cpp	(kopia robocza)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, Haiku, Inc.
+ * Copyright (c) 2007-2009, Haiku, Inc.
  * Distributed under the terms of the MIT license.
  *
  * Author:
@@ -305,8 +305,9 @@
 
 	// Install files and directories
 	PackageItem *iter;
-	BPath installedTo;
+	ItemState state;
 	uint32 i;
+	int32 choice;
 	BString label;
 	
 	packageInfo.SetName(fInfo.GetName());
@@ -322,20 +323,35 @@
 	packageInfo.SetDescription(description.String());
 	packageInfo.SetSpaceNeeded(type->space_needed);
 
+	fCurrentPolicy = P_EXISTS_NONE;
+
 	for (i = 0; i < n; i++) {
+		state.Reset(fCurrentPolicy); // Reset the current item state
 		iter = static_cast<PackageItem *>(type->items.ItemAt(i));
-		err = iter->WriteToPath(fCurrentPath.Path(), &installedTo);
+
+		err = iter->WriteToPath(fCurrentPath.Path(), &state);
+		if (err == B_FILE_EXISTS) {
+			// Writing to path failed because path already exists - ask the user
+			// what to do and retry the writing process
+			choice = _ItemExists(*iter, state.destination);
+			if (choice != P_EXISTS_ABORT) {
+				state.policy = choice;
+				err = iter->WriteToPath(fCurrentPath.Path(), &state);
+			}
+		}
+
 		if (err != B_OK) {
 			fprintf(stderr, "Error while writing path %s\n", fCurrentPath.Path());
 			return err;
 		}
+
 		if (fStatusWindow->Stopped())
 			return B_FILE_EXISTS;
 		label = "";
 		label << (uint32)(i + 1) << " of " << (uint32)n;
 		fStatusWindow->StageStep(1, NULL, label.String());
 
-		packageInfo.AddItem(installedTo.Path());
+		packageInfo.AddItem(state.destination.Path());
 	}
 
 	fStatusWindow->StageStep(1, "Finishing installation", "");
@@ -534,6 +550,68 @@
 }
 
 
+int32
+PackageView::_ItemExists(PackageItem &item, BPath &path)
+{
+	int32 choice = P_EXISTS_NONE;
+
+	switch (fCurrentPolicy) {
+		case P_EXISTS_OVERWRITE:
+			choice = P_EXISTS_OVERWRITE;
+			break;
+
+		case P_EXISTS_SKIP:
+			choice = P_EXISTS_SKIP;
+			break;
+
+		case P_EXISTS_ASK:
+		case P_EXISTS_NONE:
+		{
+			BString alertString = T("The ");
+
+			alertString << item.ItemKind() << T(" named \'") << path.Leaf() << "\' ";
+			alertString << T("already exists in the given path. Should I "
+				"replace the existing file with the one from this package?");
+
+			BAlert *alert = new BAlert(T("file_exists"), alertString.String(),
+				T("Yes"), T("No"), T("Abort"));
+
+			choice = alert->Go();
+			switch (choice) {
+				case 0:
+					choice = P_EXISTS_OVERWRITE;
+					break;
+				case 1:
+					choice = P_EXISTS_SKIP;
+					break;
+				default:
+					return P_EXISTS_ABORT;
+			}
+
+			if (fCurrentPolicy == P_EXISTS_NONE) {
+				// XXX: Maybe add 'No, but ask again' type of choice as well?
+				alertString = T("Should I remember this decision and ");
+				alertString << ((choice == P_EXISTS_OVERWRITE) ? T("replace") : T("leave"));
+				alertString << T(" all existing files encountered in the "
+					"future?");
+
+				alert = new BAlert(T("policy_decision"), alertString.String(),
+					T("Yes"), T("No"));
+
+				int32 decision = alert->Go();
+				if (decision == 0)
+					fCurrentPolicy = choice;
+				else
+					fCurrentPolicy = P_EXISTS_ASK;
+			}
+			break;
+		}
+	}
+
+	return choice;
+}
+
+
 status_t
 PackageView::_GroupChanged(int32 index)
 {
Index: src/apps/packageinstaller/PackageView.h
===================================================================
--- src/apps/packageinstaller/PackageView.h	(wersja 31688)
+++ src/apps/packageinstaller/PackageView.h	(kopia robocza)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, Haiku, Inc.
+ * Copyright (c) 2007-2009, Haiku, Inc.
  * Distributed under the terms of the MIT license.
  *
  * Author:
@@ -42,6 +42,7 @@
 	private:
 		void _InitView();
 		void _InitProfiles();
+		int32 _ItemExists(PackageItem &item, BPath &path);
 
 		status_t _GroupChanged(int32 index);
 
@@ -54,6 +55,7 @@
 		BFilePanel *fOpenPanel;
 		BPath fCurrentPath;
 		uint32 fCurrentType;
+		int32 fCurrentPolicy;
 
 		PackageInfo fInfo;
 		PackageStatus *fStatusWindow;
