File operations
One copy, move, trash and undo engine, shared by the Finder and the desktop.
fz_fileops is the file-operation engine: the single implementation of copy,
move, rename, Duplicate, New Folder, Make Alias, Move to Trash, Put Back, Empty
Trash, Delete Immediately, Compress, Decompress and Undo that both
Rastinis and the desktop's icon menus call.
Two programs that each had their own "name copy" rule, or a Trash only one of them could read, would be a bug nobody could see from either side.
It is plain POSIX — no compositor, no toolkit — so anything can link it.
The rules
These are the Finder's, deliberately.
| Operation | Result |
|---|---|
| Duplicate | name copy.ext, name copy 2.ext, … (so x copy becomes x copy 2) |
| Paste into the item's own folder | A Duplicate, never a conflict with itself |
| Keep Both, on a conflict | name 2.ext, name 3.ext, … |
| New Folder | untitled folder, untitled folder 2, … |
| Make Alias | name alias — a symlink holding the absolute target path |
| Compress | One item becomes name.zip; several become Archive.zip |
| Decompress | One top-level item becomes that item; otherwise a folder named after the archive |
An extension is the last dot-suffix with no space in it and at most 15
characters. A leading dot is not one, so .profile duplicates to
.profile copy, and a bundle keeps its suffix: AEdit.aop becomes
AEdit copy.aop.
Two departures from the Finder, both toward keeping data
Replace never destroys. The item being replaced goes to the Trash, so undoing a replacing copy brings the original back.
The Trash remembers where things came from. Each trashed item has an origin
record in the freedesktop.org trashinfo format. Put Back reads it, recreates a
missing parent folder, and asks about a conflict if the original place is now
occupied. An item on a different device goes to that volume's own trash rather
than being copied across to the system disk, which is the macOS rule.
The API
#include "fz_fileops.h"
fo_ctx c;
fo_ctx_init(&c);
c.progress = my_progress; /* bytes and items done and total, current path */
c.conflict = my_conflict; /* FO_REPLACE / FO_KEEP_BOTH / FO_SKIP / FO_STOP */
c.cancel = &my_cancel_flag; /* raise from anywhere */
const char *items[] = { "/root/Documents/report.txt" };
int rc = fo_copy(&c, items, 1, "/root/Desktop");
if (rc != FO_OK) puts(fo_error(&c));
The return is FO_OK, FO_ERR, FO_CANCELLED or FO_STOPPED.
Entry points: fo_copy, fo_move, fo_duplicate, fo_new_folder,
fo_rename, fo_make_alias, fo_trash, fo_put_back, fo_empty_trash,
fo_delete, fo_compress, fo_decompress, fo_undo_perform, fo_measure.
Queries: fo_in_trash, fo_trash_origin, fo_trash_count, and
fo_archiver_available, which answers whether Compress may be enabled at all —
it uses whichever of the usual archivers the system has.
Undo
After an operation, the context says how to reverse it, and performing that reversal leaves its own undo record, which is what Redo performs.
fo_ctx u;
fo_ctx_init(&u);
fo_undo_perform(&u, &c.undo);
There are three step kinds: moved (undo moves it back), created (undo moves it to the Trash — it never deletes), and trashed (undo puts it back).
Running it without blocking a UI
#include "fz_fileops_job.h"
foj_spec s = { .op = FOJ_COPY, .paths = items, .n = 1, .dst = "/root/Desktop" };
foj *j = foj_start(&s, spawn_fn, NULL, err, sizeof err);
/* every run-loop turn: */
if (foj_pump(j, &callbacks, ui)) foj_free(j); /* never blocks */
/* in the conflict callback, or later when the sheet closes: */
foj_answer(j, FO_KEEP_BOTH, apply_all);
/* Cancel: */
foj_cancel(j);
The child is started by the caller's spawn function, so the fork happens in your spawn helper rather than inside the file manager.
Messages travel as escaped lines over one AF_UNIX socket, because a file name
may contain a tab or a newline. A conflict is asynchronous for the parent. A
child killed mid-copy yields a completion carrying an error, and a parent that
exits makes the child stop and remove its partial item.
More documentation
- Booting the live image
- Installing to disk
- Packages and the filesystem
- How it boots
- The syscall ABI
- Hardware support
- ACPI
- UEFI boot entries
- NetBooting a Mac
- The AO toolkit
- Grafika — the graphics API
- The display protocol
- Desktop effects
- The level bezel
- Notifications
- Rastinis — the file manager
- Audio — Garsas
- Hardware video decode
- Networking — Tinklas
- Network shares — SMB and NFS
- Remote control
- The Win32 subsystem
- DirectX and OpenGL for Win32 programs
- Ports and packaging