mirror of
https://github.com/skeeto/w64devkit.git
synced 2026-10-23 06:08:56 +03:00
New command: recycle
Send files and folders to the recycle bin without confirmation.
This commit is contained in:
@@ -807,6 +807,10 @@ RUN printf "id ICON \"$PREFIX/src/w64devkit.ico\"" >w64devkit.rc \
|
||||
-O2 -fno-asynchronous-unwind-tables -fno-builtin -Wl,--gc-sections \
|
||||
-s -nostdlib -o $PREFIX/bin/peports.exe $PREFIX/src/peports.c \
|
||||
-lkernel32 -lshell32 -lmemory \
|
||||
&& $ARCH-gcc \
|
||||
-Oz -fno-asynchronous-unwind-tables -fno-builtin -Wl,--gc-sections \
|
||||
-s -nostdlib -o $PREFIX/bin/recycle.exe $PREFIX/src/recycle.c \
|
||||
-lkernel32 -lshell32 -lmemory \
|
||||
&& $ARCH-gcc -DEXE=pkg-config.exe -DCMD=pkg-config \
|
||||
-Oz -fno-asynchronous-unwind-tables -Wl,--gc-sections -s -nostdlib \
|
||||
-o $PREFIX/bin/$ARCH-pkg-config.exe $PREFIX/src/alias.c -lkernel32 \
|
||||
|
||||
144
src/recycle.c
Normal file
144
src/recycle.c
Normal file
@@ -0,0 +1,144 @@
|
||||
// recycle: send files and folders to the recycle bin without confirmation
|
||||
// $ cc -nostartfiles -o recycle.exe recycle.c
|
||||
// $ recycle [PATHS...]
|
||||
// Tip: Enable storage sense to automatically clear old recycle bin items
|
||||
// This is free and unencumbered software released into the public domain.
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <uchar.h>
|
||||
|
||||
#define lenof(a) (ptrdiff_t)(sizeof(a) / sizeof(*(a)))
|
||||
#define affirm(c) while (!(c)) __builtin_trap()
|
||||
#define new(a, n, t) (t *)alloc(a, n, sizeof(t), _Alignof(t))
|
||||
|
||||
typedef struct {
|
||||
uintptr_t wnd;
|
||||
int32_t func;
|
||||
char16_t *from;
|
||||
char16_t *to;
|
||||
int16_t flags;
|
||||
int32_t abort_flags;
|
||||
uintptr_t mappings;
|
||||
uintptr_t title;
|
||||
} FileOp;
|
||||
|
||||
enum : int32_t {
|
||||
FO_DELETE = 0x0003,
|
||||
};
|
||||
|
||||
enum : int16_t {
|
||||
FOF_ALLOWUNDO = 0x0040,
|
||||
FOF_NOCONFIRMATION = 0x0010,
|
||||
FOF_NOERRORUI = 0x0400,
|
||||
FOF_SILENT = 0x0004,
|
||||
};
|
||||
|
||||
enum : int32_t {
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100,
|
||||
FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000,
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200,
|
||||
};
|
||||
|
||||
#define W32 [[gnu::dllimport, gnu::stdcall]]
|
||||
W32 char16_t **CommandLineToArgvW(char16_t *, int32_t *);
|
||||
W32 void ExitProcess(int32_t);
|
||||
W32 int32_t FormatMessageW(int32_t,uintptr_t,int32_t,int32_t,char16_t**,int32_t,uintptr_t);
|
||||
W32 char16_t *GetCommandLineW();
|
||||
W32 uintptr_t GetStdHandle(int32_t);
|
||||
W32 int32_t SHFileOperationW(FileOp *);
|
||||
W32 int32_t WriteConsoleW(uintptr_t,char16_t*,int32_t,int32_t *,uintptr_t);
|
||||
|
||||
typedef struct {
|
||||
char *beg;
|
||||
char *end;
|
||||
} Arena;
|
||||
|
||||
static size_t tousize(ptrdiff_t x)
|
||||
{
|
||||
affirm(x >= 0);
|
||||
return (size_t)x;
|
||||
}
|
||||
|
||||
static char *alloc(Arena *a, ptrdiff_t count, ptrdiff_t size, ptrdiff_t align)
|
||||
{
|
||||
ptrdiff_t pad = (ptrdiff_t)-(uintptr_t)a->beg & (align - 1);
|
||||
affirm(count < (a->end - a->beg - pad)/size);
|
||||
char *r = a->beg + pad;
|
||||
a->beg += pad;
|
||||
return __builtin_memset(r, 0, tousize(count*size));
|
||||
}
|
||||
|
||||
static int32_t len16(char16_t *s)
|
||||
{
|
||||
int32_t len = 0;
|
||||
for (; s[len]; len++) {}
|
||||
return len;
|
||||
}
|
||||
|
||||
static char16_t *termzz(Arena *a, char16_t *s)
|
||||
{
|
||||
ptrdiff_t len = len16(s);
|
||||
char16_t *r = new(a, len+2, char16_t);
|
||||
__builtin_memcpy(r, s, tousize(len)*2);
|
||||
return r;
|
||||
}
|
||||
|
||||
static char16_t *geterror(int32_t err)
|
||||
{
|
||||
int32_t flags =
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS;
|
||||
char16_t *r = {};
|
||||
FormatMessageW(flags, 0, err, 0, &r, 0, 0);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void report(char16_t *path, int32_t code)
|
||||
{
|
||||
uintptr_t h = GetStdHandle(-12);
|
||||
static char16_t pre[] = u"recycle: ";
|
||||
WriteConsoleW(h, pre, lenof(pre)-1, &(int32_t){}, 0);
|
||||
WriteConsoleW(h, path, len16(path), &(int32_t){}, 0);
|
||||
static char16_t mid[] = u": ";
|
||||
WriteConsoleW(h, mid, lenof(mid)-1, &(int32_t){}, 0);
|
||||
char16_t *err = geterror(code);
|
||||
WriteConsoleW(h, err, len16(err), &(int32_t){}, 0);
|
||||
WriteConsoleW(h, u"\n", 1, &(int32_t){}, 0);
|
||||
}
|
||||
|
||||
static int32_t recycle(int32_t argc, char16_t **argv, Arena scratch)
|
||||
{
|
||||
int32_t errors = 0;
|
||||
for (int32_t i = 1; i < argc; i++) {
|
||||
Arena a = scratch;
|
||||
|
||||
FileOp op = {};
|
||||
op.func = FO_DELETE;
|
||||
op.from = termzz(&a, argv[i]);
|
||||
op.flags =
|
||||
FOF_ALLOWUNDO |
|
||||
FOF_NOCONFIRMATION |
|
||||
FOF_NOERRORUI |
|
||||
FOF_SILENT;
|
||||
|
||||
int32_t r = SHFileOperationW(&op);
|
||||
if (r) {
|
||||
report(argv[i], r);
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
return errors > 0;
|
||||
}
|
||||
|
||||
[[gnu::stdcall]]
|
||||
void mainCRTStartup()
|
||||
{
|
||||
static char mem[1<<18];
|
||||
char16_t *cmd = GetCommandLineW();
|
||||
int32_t argc = {};
|
||||
char16_t **argv = CommandLineToArgvW(cmd, &argc);
|
||||
int32_t stat = recycle(argc, argv, (Arena){mem, mem+lenof(mem)});
|
||||
ExitProcess(stat);
|
||||
__builtin_unreachable();
|
||||
}
|
||||
Reference in New Issue
Block a user