mirror of https://gitlab.com/nakst/essence
copying folders
This commit is contained in:
parent
5d9a6f72dc
commit
787e51b372
apps/file_manager
desktop
shared
util
|
@ -152,21 +152,81 @@ void CommandCopy(Instance *instance, EsElement *, EsCommand *) {
|
|||
}
|
||||
}
|
||||
|
||||
EsError CommandPasteFile(String source, String destinationBase, void **copyBuffer, String *_destination = nullptr) {
|
||||
if (PathHasPrefix(destinationBase, source)) {
|
||||
return ES_ERROR_TARGET_WITHIN_SOURCE;
|
||||
}
|
||||
|
||||
String name = PathGetName(source);
|
||||
String destination = StringAllocateAndFormat("%s%z%s", STRFMT(destinationBase), PathHasTrailingSlash(destinationBase) ? "" : "/", STRFMT(name));
|
||||
|
||||
if (StringEquals(PathGetParent(source), destinationBase)) {
|
||||
destination.allocated += 32;
|
||||
destination.text = (char *) EsHeapReallocate(destination.text, destination.allocated, false);
|
||||
size_t bytes = EsPathFindUniqueName(destination.text, destination.bytes, destination.allocated);
|
||||
|
||||
if (bytes) {
|
||||
destination.bytes = destination.allocated = bytes;
|
||||
} else {
|
||||
destination.allocated = destination.bytes;
|
||||
StringDestroy(&destination);
|
||||
return ES_ERROR_FILE_ALREADY_EXISTS;
|
||||
}
|
||||
}
|
||||
|
||||
EsPrint("Copying %s -> %s...\n", STRFMT(source), STRFMT(destination));
|
||||
EsError error = EsFileCopy(STRING(source), STRING(destination), copyBuffer);
|
||||
|
||||
if (error == ES_ERROR_INCORRECT_NODE_TYPE) {
|
||||
EsDirectoryChild *buffer;
|
||||
ptrdiff_t childCount = EsDirectoryEnumerateChildren(STRING(source), &buffer);
|
||||
|
||||
if (ES_CHECK_ERROR(childCount)) {
|
||||
error = (EsError) childCount;
|
||||
} else {
|
||||
error = EsPathCreate(STRING(destination), ES_NODE_DIRECTORY, false);
|
||||
|
||||
if (error == ES_SUCCESS) {
|
||||
for (intptr_t i = 0; i < childCount && error == ES_SUCCESS; i++) {
|
||||
String childSourcePath = StringAllocateAndFormat("%s%z%s", STRFMT(source),
|
||||
PathHasTrailingSlash(source) ? "" : "/", buffer[i].nameBytes, buffer[i].name);
|
||||
error = CommandPasteFile(childSourcePath, destination, copyBuffer);
|
||||
StringDestroy(&childSourcePath);
|
||||
}
|
||||
}
|
||||
|
||||
EsHeapFree(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
if (error == ES_SUCCESS) {
|
||||
FolderFileUpdatedAtPath(destination, nullptr);
|
||||
}
|
||||
|
||||
if (_destination && error == ES_SUCCESS) {
|
||||
*_destination = destination;
|
||||
} else {
|
||||
StringDestroy(&destination);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void CommandPaste(Instance *instance, EsElement *, EsCommand *) {
|
||||
if (EsClipboardHasFormat(ES_CLIPBOARD_PRIMARY, ES_CLIPBOARD_FORMAT_PATH_LIST)) {
|
||||
// TODO Background task.
|
||||
// TODO Renaming.
|
||||
// TODO Recursing into folders.
|
||||
// TODO Reporting errors properly.
|
||||
// TODO Reporting errors properly. Ask to retry or cancel.
|
||||
// TODO If the destination file already exists, ask to replace, skip, rename or cancel.
|
||||
// TODO Other namespace handlers.
|
||||
// TODO Selecting *all* pasted files.
|
||||
// TODO Update parent folders after copy complete.
|
||||
// TODO Undo.
|
||||
|
||||
void *copyBuffer = nullptr;
|
||||
|
||||
size_t bytes;
|
||||
char *pathList = EsClipboardReadText(ES_CLIPBOARD_PRIMARY, &bytes);
|
||||
|
||||
Array<String> itemsToSelect = {};
|
||||
|
||||
if (pathList) {
|
||||
const char *position = pathList;
|
||||
|
||||
|
@ -175,29 +235,18 @@ void CommandPaste(Instance *instance, EsElement *, EsCommand *) {
|
|||
if (!newline) break;
|
||||
|
||||
String source = StringFromLiteralWithSize(position, newline - position);
|
||||
String name = PathGetName(source);
|
||||
String destination = StringAllocateAndFormat("%s%s", STRFMT(instance->folder->path), STRFMT(name));
|
||||
EsError error = EsFileCopy(STRING(source), STRING(destination), ©Buffer);
|
||||
String destination;
|
||||
|
||||
if (error == ES_SUCCESS) {
|
||||
EsMutexAcquire(&instance->folder->modifyEntriesMutex);
|
||||
EsAssert(instance->folder->doneInitialEnumeration);
|
||||
EsDirectoryChild directoryChild;
|
||||
|
||||
if (EsPathQueryInformation(STRING(destination), &directoryChild)) {
|
||||
FolderAddEntryAndUpdateInstances(instance->folder, STRING(name), &directoryChild, instance);
|
||||
} else {
|
||||
// File must have been deleted by the time we got here!
|
||||
}
|
||||
|
||||
EsMutexRelease(&instance->folder->modifyEntriesMutex);
|
||||
} else {
|
||||
if (ES_SUCCESS != CommandPasteFile(source, instance->folder->path, ©Buffer, &destination)) {
|
||||
goto encounteredError;
|
||||
}
|
||||
|
||||
String destinationItem = StringDuplicate(PathGetName(destination));
|
||||
itemsToSelect.Add(destinationItem);
|
||||
StringDestroy(&destination);
|
||||
|
||||
position += source.bytes + 1;
|
||||
bytes -= source.bytes + 1;
|
||||
StringDestroy(&destination);
|
||||
}
|
||||
} else {
|
||||
encounteredError:;
|
||||
|
@ -205,8 +254,23 @@ void CommandPaste(Instance *instance, EsElement *, EsCommand *) {
|
|||
EsAnnouncementShow(instance->window, ES_FLAGS_DEFAULT, point.x, point.y, INTERFACE_STRING(CommonAnnouncementPasteErrorOther));
|
||||
}
|
||||
|
||||
size_t pathSectionCount = PathCountSections(instance->folder->path);
|
||||
|
||||
for (uintptr_t i = 0; i < pathSectionCount - 1; i++) {
|
||||
String parent = PathGetParent(instance->folder->path, i + 1);
|
||||
FolderFileUpdatedAtPath(parent, nullptr);
|
||||
}
|
||||
|
||||
EsListViewSelectNone(instance->list);
|
||||
|
||||
for (uintptr_t i = 0; i < itemsToSelect.Length(); i++) {
|
||||
InstanceSelectByName(instance, itemsToSelect[i], true, i == itemsToSelect.Length() - 1);
|
||||
StringDestroy(&itemsToSelect[i]);
|
||||
}
|
||||
|
||||
EsHeapFree(pathList);
|
||||
EsHeapFree(copyBuffer);
|
||||
itemsToSelect.Free();
|
||||
} else {
|
||||
// TODO Paste the data into a new file.
|
||||
}
|
||||
|
|
|
@ -426,6 +426,29 @@ void FolderAddEntryAndUpdateInstances(Folder *folder, const char *name, size_t n
|
|||
}
|
||||
}
|
||||
|
||||
void FolderFileUpdatedAtPath(String path, Instance *instance) {
|
||||
path = PathRemoveTrailingSlash(path);
|
||||
String file = PathGetName(path);
|
||||
String folder = PathGetParent(path);
|
||||
EsDirectoryChild information = {};
|
||||
|
||||
if (EsPathQueryInformation(STRING(path), &information)) {
|
||||
for (uintptr_t i = 0; i < loadedFolders.Length(); i++) {
|
||||
if (loadedFolders[i]->itemHandler->type != NAMESPACE_HANDLER_FILE_SYSTEM) continue;
|
||||
if (EsStringCompareRaw(STRING(loadedFolders[i]->path), STRING(folder))) continue;
|
||||
|
||||
EsMutexAcquire(&loadedFolders[i]->modifyEntriesMutex);
|
||||
|
||||
if (loadedFolders[i]->doneInitialEnumeration) {
|
||||
FolderAddEntryAndUpdateInstances(loadedFolders[i], file.text, file.bytes, &information, instance);
|
||||
}
|
||||
|
||||
EsMutexRelease(&loadedFolders[i]->modifyEntriesMutex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint64_t FolderRemoveEntryAndUpdateInstances(Folder *folder, const char *name, size_t nameBytes) {
|
||||
FolderEntry *entry = (FolderEntry *) HashTableGetLong(&folder->entries, name, nameBytes);
|
||||
uint64_t id = 0;
|
||||
|
|
|
@ -230,6 +230,7 @@ void InstanceFolderPathChanged(Instance *instance, bool fromLoadFolder);
|
|||
void InstanceAddContents(struct Instance *instance, HashTable *newEntries);
|
||||
void InstanceAddSingle(struct Instance *instance, ListEntry newEntry);
|
||||
void InstanceRemoveContents(struct Instance *instance);
|
||||
void InstanceSelectByName(Instance *instance, String name, bool addToExistingSelection, bool focusItem);
|
||||
ListEntry InstanceRemoveSingle(Instance *instance, FolderEntry *folderEntry);
|
||||
ListEntry *InstanceGetSelectedListEntry(Instance *instance);
|
||||
void ListItemCreated(EsElement *element, uintptr_t index, bool fromFolderRename);
|
||||
|
@ -549,25 +550,7 @@ void _start() {
|
|||
size_t pathSectionCount = PathCountSections(fullPath);
|
||||
|
||||
for (uintptr_t i = 0; i < pathSectionCount; i++) {
|
||||
String path = PathGetParent(fullPath, i + 1);
|
||||
String file = PathGetSection(fullPath, i + 1);
|
||||
String folder = PathGetParent(path);
|
||||
EsDirectoryChild information = {};
|
||||
|
||||
if (EsPathQueryInformation(STRING(path), &information)) {
|
||||
for (uintptr_t i = 0; i < loadedFolders.Length(); i++) {
|
||||
if (loadedFolders[i]->itemHandler->type != NAMESPACE_HANDLER_FILE_SYSTEM) continue;
|
||||
if (EsStringCompareRaw(STRING(loadedFolders[i]->path), STRING(folder))) continue;
|
||||
|
||||
EsMutexAcquire(&loadedFolders[i]->modifyEntriesMutex);
|
||||
|
||||
if (loadedFolders[i]->doneInitialEnumeration) {
|
||||
FolderAddEntryAndUpdateInstances(loadedFolders[i], file.text, file.bytes, &information, nullptr);
|
||||
}
|
||||
|
||||
EsMutexRelease(&loadedFolders[i]->modifyEntriesMutex);
|
||||
}
|
||||
}
|
||||
FolderFileUpdatedAtPath(PathGetParent(fullPath, i + 1), nullptr);
|
||||
}
|
||||
|
||||
EsHandleClose(message->user.context1.u);
|
||||
|
|
|
@ -176,7 +176,24 @@ String PathGetParent(String string) {
|
|||
return result;
|
||||
}
|
||||
|
||||
bool PathHasTrailingSlash(String path) {
|
||||
return path.bytes && path.text[path.bytes - 1] == '/';
|
||||
}
|
||||
|
||||
String PathRemoveTrailingSlash(String path) {
|
||||
if (PathHasTrailingSlash(path)) path.bytes--;
|
||||
return path;
|
||||
}
|
||||
|
||||
String PathGetName(String path) {
|
||||
intptr_t i = path.bytes - 2;
|
||||
while (i >= 0 && path.text[i] != '/') i--;
|
||||
path.text += i + 1, path.bytes -= i + 1;
|
||||
return path;
|
||||
}
|
||||
|
||||
bool PathHasPrefix(String path, String prefix) {
|
||||
prefix = PathRemoveTrailingSlash(prefix);
|
||||
return StringStartsWith(path, prefix) && path.bytes > prefix.bytes && path.text[prefix.bytes] == '/';
|
||||
}
|
||||
|
||||
|
@ -191,18 +208,3 @@ bool PathReplacePrefix(String *knownPath, String oldPath, String newPath) {
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
String PathRemoveTrailingSlash(String path) {
|
||||
if (path.bytes && path.text[path.bytes - 1] == '/') {
|
||||
path.bytes--;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
String PathGetName(String path) {
|
||||
intptr_t i = path.bytes - 2;
|
||||
while (i >= 0 && path.text[i] != '/') i--;
|
||||
path.text += i + 1, path.bytes -= i + 1;
|
||||
return path;
|
||||
}
|
||||
|
|
|
@ -324,6 +324,16 @@ ES_MACRO_SORT(InstanceSortListContents, ListEntry, {
|
|||
result = InstanceCompareFolderEntries(_left->entry, _right->entry, context);
|
||||
}, uint16_t);
|
||||
|
||||
void InstanceSelectByName(Instance *instance, String name, bool addToExistingSelection, bool focusItem) {
|
||||
for (uintptr_t i = 0; i < instance->listContents.Length(); i++) {
|
||||
if (0 == EsStringCompareRaw(STRING(instance->listContents[i].entry->GetName()), STRING(name))) {
|
||||
EsListViewSelect(instance->list, 0, i, addToExistingSelection);
|
||||
if (focusItem) EsListViewFocusItem(instance->list, 0, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceAddContents(Instance *instance, HashTable *newEntries) {
|
||||
// Call with the message mutex acquired.
|
||||
|
||||
|
@ -352,14 +362,7 @@ void InstanceAddContents(Instance *instance, HashTable *newEntries) {
|
|||
EsListViewInsert(instance->list, 0, 0, instance->listContents.Length());
|
||||
|
||||
if (instance->delayedFocusItem.bytes) {
|
||||
for (uintptr_t i = 0; i < instance->listContents.Length(); i++) {
|
||||
if (0 == EsStringCompareRaw(STRING(instance->listContents[i].entry->GetName()), STRING(instance->delayedFocusItem))) {
|
||||
EsListViewSelect(instance->list, 0, i);
|
||||
EsListViewFocusItem(instance->list, 0, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
InstanceSelectByName(instance, instance->delayedFocusItem, false, true);
|
||||
StringDestroy(&instance->delayedFocusItem);
|
||||
}
|
||||
}
|
||||
|
@ -696,7 +699,7 @@ int ListItemMessage(EsElement *element, EsMessage *message) {
|
|||
int ListCallback(EsElement *element, EsMessage *message) {
|
||||
Instance *instance = element->instance;
|
||||
|
||||
if (message->type == ES_MSG_FOCUSED_START) {
|
||||
if (message->type == ES_MSG_FOCUSED_START || message->type == ES_MSG_PRIMARY_CLIPBOARD_UPDATED) {
|
||||
InstanceUpdateItemSelectionCountCommands(instance);
|
||||
return 0;
|
||||
} else if (message->type == ES_MSG_FOCUSED_END) {
|
||||
|
|
|
@ -2900,8 +2900,8 @@ void ScrollPane::Refresh() {
|
|||
|
||||
EsRectangle bounds = parent->GetBounds();
|
||||
|
||||
if (bar[0]) ScrollbarSetMeasurements(bar[0], bounds.r - fixedViewport[0], contentWidth);
|
||||
if (bar[1]) ScrollbarSetMeasurements(bar[1], bounds.b - fixedViewport[1], contentHeight);
|
||||
if (bar[0]) ScrollbarSetMeasurements(bar[0], bounds.r - fixedViewport[0], contentWidth - fixedViewport[0]);
|
||||
if (bar[1]) ScrollbarSetMeasurements(bar[1], bounds.b - fixedViewport[1], contentHeight - fixedViewport[1]);
|
||||
|
||||
SetPosition(0, position[0], true);
|
||||
SetPosition(1, position[1], true);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// TODO Consistent int64_t/intptr_t.
|
||||
// TODO Drag and drop.
|
||||
// TODO GetFirstIndex/GetLastIndex assume that every group is non-empty.
|
||||
// TODO Sticking to top/bottom scroll when inserting/removing space.
|
||||
|
||||
struct ListViewItemElement : EsElement {
|
||||
uintptr_t index; // Index into the visible items array.
|
||||
|
@ -482,6 +483,22 @@ struct EsListView : EsElement {
|
|||
}
|
||||
|
||||
void Populate() {
|
||||
EsPrint("--- Before Populate() ---\n");
|
||||
EsPrint("Scroll: %i\n", (int) (scroll.position[1] - currentStyle->insets.t));
|
||||
|
||||
for (uintptr_t i = 0; i < visibleItems.Length(); i++) {
|
||||
EsMessage m = { ES_MSG_LIST_VIEW_GET_CONTENT };
|
||||
uint8_t _buffer[512];
|
||||
EsBuffer buffer = { .out = _buffer, .bytes = sizeof(_buffer) };
|
||||
m.getContent.buffer = &buffer;
|
||||
m.getContent.index = visibleItems[i].index;
|
||||
m.getContent.group = visibleItems[i].group;
|
||||
EsMessageSend(this, &m);
|
||||
EsPrint("%d: %d '%s' at %i\n", i, visibleItems[i].index, buffer.position, _buffer, visibleItems[i].element->offsetY - GetListBounds().t);
|
||||
}
|
||||
|
||||
EsPrint("------\n");
|
||||
|
||||
// TODO Keep one item before and after the viewport, so tab traversal on custom elements works.
|
||||
// TODO Always keep an item if it has FOCUS_WITHIN.
|
||||
// - But maybe we shouldn't allow focusable elements in a list view.
|
||||
|
@ -533,7 +550,7 @@ struct EsListView : EsElement {
|
|||
: visibleItem->element->offsetY - contentBounds.t;
|
||||
|
||||
if (position < expectedPosition - 1 || position > expectedPosition + 1) {
|
||||
EsPrint("Item in unexpected position: expected %d, got %d; index %d, scroll %d.\n",
|
||||
EsPrint("Item in unexpected position: got %d, should have been %d; index %d, scroll %d.\n",
|
||||
expectedPosition, position, visibleItem->index, scroll);
|
||||
EsAssert(false);
|
||||
}
|
||||
|
@ -726,33 +743,19 @@ struct EsListView : EsElement {
|
|||
return;
|
||||
}
|
||||
|
||||
int64_t currentScroll = (flags & ES_LIST_VIEW_HORIZONTAL) ? scroll.position[0] : scroll.position[1];
|
||||
int64_t scrollLimit = (flags & ES_LIST_VIEW_HORIZONTAL) ? scroll.limit[0] : scroll.limit[1];
|
||||
|
||||
totalSize += space;
|
||||
|
||||
if (((beforeItem == 0 && currentScroll) || currentScroll == scrollLimit) && firstLayout && space > 0 && scrollLimit) {
|
||||
scroll.Refresh();
|
||||
for (uintptr_t i = beforeItem; i < visibleItems.Length(); i++) {
|
||||
ListViewItem *item = &visibleItems[i];
|
||||
|
||||
if (flags & ES_LIST_VIEW_HORIZONTAL) {
|
||||
scroll.SetX(scroll.position[0] + space, false);
|
||||
item->element->offsetX += space;
|
||||
} else {
|
||||
scroll.SetY(scroll.position[1] + space, false);
|
||||
item->element->offsetY += space;
|
||||
}
|
||||
} else {
|
||||
for (uintptr_t i = beforeItem; i < visibleItems.Length(); i++) {
|
||||
ListViewItem *item = &visibleItems[i];
|
||||
|
||||
if (flags & ES_LIST_VIEW_HORIZONTAL) {
|
||||
item->element->offsetX += space;
|
||||
} else {
|
||||
item->element->offsetY += space;
|
||||
}
|
||||
}
|
||||
|
||||
scroll.Refresh();
|
||||
}
|
||||
|
||||
scroll.Refresh();
|
||||
EsElementUpdateContentSize(this);
|
||||
}
|
||||
|
||||
|
@ -1575,7 +1578,7 @@ struct EsListView : EsElement {
|
|||
|
||||
if (message->type == ES_MSG_GET_WIDTH || message->type == ES_MSG_GET_HEIGHT) {
|
||||
if (flags & ES_LIST_VIEW_HORIZONTAL) {
|
||||
message->measure.width = totalSize + currentStyle->insets.l + currentStyle->insets.r;
|
||||
message->measure.width = totalSize + currentStyle->insets.l + currentStyle->insets.r;
|
||||
} else {
|
||||
message->measure.height = totalSize + currentStyle->insets.t + currentStyle->insets.b;
|
||||
|
||||
|
@ -2305,10 +2308,20 @@ bool EsListViewGetFocusedItem(EsListView *view, EsListViewIndex *group, EsListVi
|
|||
return view->hasFocusedItem;
|
||||
}
|
||||
|
||||
void EsListViewSelect(EsListView *view, EsListViewIndex group, EsListViewIndex index) {
|
||||
void EsListViewSelectNone(EsListView *view) {
|
||||
EsMessageMutexCheck();
|
||||
view->Select(-1, 0, false, false, false);
|
||||
}
|
||||
|
||||
void EsListViewSelect(EsListView *view, EsListViewIndex group, EsListViewIndex index, bool addToExistingSelection) {
|
||||
EsMessageMutexCheck();
|
||||
|
||||
view->Select(group, index, false, false, false);
|
||||
if (addToExistingSelection) {
|
||||
view->SetSelected(group, index, group, index, true, false);
|
||||
view->UpdateVisibleItemsSelectionState();
|
||||
} else {
|
||||
view->Select(group, index, false, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
void EsListViewSetEmptyMessage(EsListView *view, const char *message, ptrdiff_t messageBytes) {
|
||||
|
|
|
@ -2422,7 +2422,8 @@ function void EsListViewEnumerateVisibleItems(EsListView *view, EsListViewEnumer
|
|||
function void EsListViewSetColumns(EsListView *view, EsListViewColumn *columns, size_t columnCount);
|
||||
function void EsListViewSetEmptyMessage(EsListView *view, STRING message = BLANK_STRING);
|
||||
function void EsListViewSetMaximumItemsPerBand(EsListView *view, int maximumItemsPerBand);
|
||||
function void EsListViewSelect(EsListView *view, EsListViewIndex group, EsListViewIndex index);
|
||||
function void EsListViewSelectNone(EsListView *view);
|
||||
function void EsListViewSelect(EsListView *view, EsListViewIndex group, EsListViewIndex index, bool addToExistingSelection = false);
|
||||
function void EsListViewFocusItem(EsListView *view, EsListViewIndex group, EsListViewIndex index);
|
||||
function bool EsListViewGetFocusedItem(EsListView *view, EsListViewIndex *group, EsListViewIndex *index); // Returns false if not item was focused.
|
||||
function void EsListViewInvalidateContent(EsListView *view, EsListViewIndex group, EsListViewIndex index);
|
||||
|
|
|
@ -235,25 +235,32 @@ EsError EsFileCopy(const char *source, ptrdiff_t sourceBytes, const char *destin
|
|||
EsError error = ES_SUCCESS;
|
||||
|
||||
EsFileInformation sourceFile = EsFileOpen(source, sourceBytes, ES_FILE_READ | ES_NODE_FILE | ES_NODE_FAIL_IF_NOT_FOUND);
|
||||
EsFileInformation destinationFile = EsFileOpen(destination, destinationBytes, ES_FILE_WRITE_EXCLUSIVE | ES_NODE_FILE | ES_NODE_FAIL_IF_FOUND);
|
||||
|
||||
if (sourceFile.error == ES_SUCCESS && destinationFile.error == ES_SUCCESS) {
|
||||
error = EsFileResize(destinationFile.handle, sourceFile.size);
|
||||
if (sourceFile.error == ES_SUCCESS) {
|
||||
EsFileInformation destinationFile = EsFileOpen(destination, destinationBytes, ES_FILE_WRITE_EXCLUSIVE | ES_NODE_FILE | ES_NODE_FAIL_IF_FOUND);
|
||||
|
||||
if (error == ES_SUCCESS) {
|
||||
for (uintptr_t i = 0; i < sourceFile.size; i += copyBufferBytes) {
|
||||
size_t bytesRead = EsFileReadSync(sourceFile.handle, i, copyBufferBytes, copyBuffer);
|
||||
if (ES_CHECK_ERROR(bytesRead)) { error = bytesRead; break; }
|
||||
size_t bytesWritten = EsFileWriteSync(destinationFile.handle, i, bytesRead, copyBuffer);
|
||||
if (ES_CHECK_ERROR(bytesWritten)) { error = bytesWritten; break; }
|
||||
if (destinationFile.error == ES_SUCCESS) {
|
||||
error = EsFileResize(destinationFile.handle, sourceFile.size);
|
||||
|
||||
if (error == ES_SUCCESS) {
|
||||
for (uintptr_t i = 0; i < sourceFile.size; i += copyBufferBytes) {
|
||||
size_t bytesRead = EsFileReadSync(sourceFile.handle, i, copyBufferBytes, copyBuffer);
|
||||
if (ES_CHECK_ERROR(bytesRead)) { error = bytesRead; break; }
|
||||
size_t bytesWritten = EsFileWriteSync(destinationFile.handle, i, bytesRead, copyBuffer);
|
||||
if (ES_CHECK_ERROR(bytesWritten)) { error = bytesWritten; break; }
|
||||
}
|
||||
}
|
||||
|
||||
EsHandleClose(destinationFile.handle);
|
||||
} else {
|
||||
error = destinationFile.error;
|
||||
}
|
||||
|
||||
EsHandleClose(sourceFile.handle);
|
||||
} else {
|
||||
error = sourceFile.error == ES_SUCCESS ? destinationFile.error : sourceFile.error;
|
||||
error = sourceFile.error;
|
||||
}
|
||||
|
||||
if (sourceFile.error == ES_SUCCESS) EsHandleClose(sourceFile.handle);
|
||||
if (destinationFile.error == ES_SUCCESS) EsHandleClose(destinationFile.handle);
|
||||
if (!_copyBuffer) EsHeapFree(copyBuffer);
|
||||
return error;
|
||||
}
|
||||
|
|
|
@ -1606,13 +1606,17 @@ void EnterDebugger() {
|
|||
|
||||
#ifndef KERNEL
|
||||
size_t EsPathFindUniqueName(char *buffer, size_t originalBytes, size_t bufferBytes) {
|
||||
// TODO Check that this runs in a reasonable amount of time when all files are already present.
|
||||
if (originalBytes && buffer[originalBytes - 1] == '/') {
|
||||
originalBytes--;
|
||||
}
|
||||
|
||||
size_t extensionPoint = originalBytes;
|
||||
|
||||
for (uintptr_t i = 0; i < originalBytes; i++) {
|
||||
if (buffer[i] == '.') {
|
||||
extensionPoint = i;
|
||||
} else if (buffer[i] == '/') {
|
||||
extensionPoint = originalBytes;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1625,6 +1629,8 @@ size_t EsPathFindUniqueName(char *buffer, size_t originalBytes, size_t bufferByt
|
|||
|
||||
uintptr_t attempt = 2;
|
||||
|
||||
// TODO Check that this runs in a reasonable amount of time when all files are already present.
|
||||
|
||||
while (attempt < 1000) {
|
||||
size_t length = EsStringFormat(buffer2, bufferBytes, "%s %d%s", extensionPoint, buffer,
|
||||
attempt, originalBytes - extensionPoint, buffer + extensionPoint);
|
||||
|
|
|
@ -430,3 +430,4 @@ EsListViewFixedItemGetSelected=428
|
|||
EsClipboardHasFormat=429
|
||||
EsClipboardHasData=430
|
||||
EsFileCopy=431
|
||||
EsListViewSelectNone=432
|
||||
|
|
Loading…
Reference in New Issue