AnotherOS

Documentation

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.