-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 8dee7e1
Showing
8 changed files
with
259 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
ColumnLimit: 0 | ||
PointerAlignment: Left |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
version: 2 | ||
updates: | ||
- package-ecosystem: github-actions | ||
directory: "/" | ||
schedule: | ||
interval: "weekly" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
name: ci | ||
|
||
on: [pull_request, push] | ||
|
||
jobs: | ||
check: | ||
runs-on: macos-15 | ||
steps: | ||
- uses: actions/checkout@v4 | ||
|
||
- name: build | ||
run: make CC=$(brew --prefix llvm@18)/bin/clang | ||
|
||
- name: run | ||
run: ./bin/can -V |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# General. | ||
.DS_Store | ||
bin/ | ||
|
||
# Prerequisites | ||
*.d | ||
|
||
# Object files | ||
*.o | ||
*.ko | ||
*.obj | ||
*.elf | ||
|
||
# Linker output | ||
*.ilk | ||
*.map | ||
*.exp | ||
|
||
# Precompiled Headers | ||
*.gch | ||
*.pch | ||
|
||
# Libraries | ||
*.lib | ||
*.a | ||
*.la | ||
*.lo | ||
|
||
# Shared objects (inc. Windows DLLs) | ||
*.dll | ||
*.so | ||
*.so.* | ||
*.dylib | ||
|
||
# Executables | ||
*.exe | ||
*.out | ||
*.app | ||
*.i*86 | ||
*.x86_64 | ||
*.hex | ||
|
||
# Debug files | ||
*.dSYM/ | ||
*.su | ||
*.idb | ||
*.pdb | ||
|
||
# Kernel Module Compile Results | ||
*.mod* | ||
*.cmd | ||
.tmp_versions/ | ||
modules.order | ||
Module.symvers | ||
Mkfile.old | ||
dkms.conf |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
ISC License | ||
|
||
Copyright (c) 2023 Tshaka Lekholoane | ||
|
||
Permission to use, copy, modify, and/or distribute this software for any | ||
purpose with or without fee is hereby granted, provided that the above | ||
copyright notice and this permission notice appear in all copies. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH | ||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY | ||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, | ||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM | ||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | ||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
PERFORMANCE OF THIS SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
COMMIT = $(shell git rev-parse --short HEAD) | ||
DATE = $(shell date -u +"%Y.%m.%d") | ||
BUILD = $(shell printf "%s (%s)" "$(DATE)" "$(COMMIT)" ) | ||
CC = cc | ||
CFLAGS = -DCAN_BUILD="\"$(BUILD)\"" -O3 -Wall -Wextra -Wno-c++98-compat \ | ||
-Wno-cast-function-type-strict -Wno-declaration-after-statement \ | ||
-Wno-format-nonliteral -Wno-incompatible-pointer-types-discards-qualifiers \ | ||
-Wno-poison-system-directories -Wno-vla -framework Foundation -march=native \ | ||
-pedantic -std=c23 | ||
|
||
.PHONY: all clean | ||
|
||
all: | ||
mkdir -p bin/ | ||
$(CC) $(CFLAGS) -o bin/can src/main.c | ||
|
||
clean: | ||
rm -rf bin/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# `can` | ||
|
||
![Continuous Integration](https://github.com/tshakalekholoane/can/actions/workflows/ci.yaml/badge.svg) | ||
|
||
`can` is a macOS command-line utility that provides an alternative to the `rm` command. Instead of permanently deleting files and directories, `can` moves them to the user's Trash, allowing for easy recovery if needed. | ||
|
||
## Usage | ||
|
||
``` | ||
usage: can [-h | -V] [--] file ... | ||
``` | ||
|
||
## Installation | ||
|
||
### Source | ||
|
||
The application can be built from source by cloning the repository and running the following commands which require working versions of [Make](https://www.gnu.org/software/make/) and a C compiler with C23 support. | ||
|
||
```shell | ||
git clone https://github.com/tshakalekholoane/can && cd can | ||
make | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
#ifndef __APPLE__ | ||
#error "This program is intended to only run on macOS." | ||
#endif | ||
|
||
#if __STDC_VERSION__ < 202311L | ||
#error "This code requires C23 or later." | ||
#endif | ||
|
||
#include <errno.h> | ||
#include <getopt.h> | ||
#include <objc/message.h> | ||
#include <pwd.h> | ||
#include <stdio.h> | ||
#include <string.h> | ||
#include <unistd.h> | ||
|
||
#ifndef CAN_BUILD | ||
#define CAN_BUILD "TIP" | ||
#endif | ||
|
||
static const char* usage = "usage: can [-h | -V] [--] file ..."; | ||
|
||
// Objective-C messaging primitives require that functions be cast to an | ||
// appropriate function pointer type before being called [1]. | ||
// | ||
// [1]: https://github.com/apple-oss-distributions/objc4/blob/89543e2c0f67d38ca5211cea33f42c51500287d5/runtime/message.h#L52-L53 | ||
|
||
static struct objc_object* file_manager_default_manager(void) { | ||
auto file_manager = objc_getClass("NSFileManager"); | ||
typedef struct objc_object* (*send_type)(struct objc_class*, struct objc_selector*); | ||
auto func = (send_type)objc_msgSend; | ||
return func(file_manager, sel_registerName("defaultManager")); | ||
} | ||
|
||
static struct objc_object* file_manager_string_with_file_system_representation(struct objc_object* self, const char string[static 1]) { | ||
typedef struct objc_object* (*send_type)(struct objc_object*, struct objc_selector*, const char*, unsigned long); | ||
auto func = (send_type)objc_msgSend; | ||
return func(self, sel_registerName("stringWithFileSystemRepresentation:length:"), string, strlen(string)); | ||
} | ||
|
||
static bool file_manager_trash_item_at_url(struct objc_object* self, struct objc_object* url, struct objc_object** error) { | ||
typedef bool (*send_type)(struct objc_object*, struct objc_selector*, struct objc_object*, struct objc_object*, struct objc_object**); | ||
auto func = (send_type)objc_msgSend; | ||
return func(self, sel_registerName("trashItemAtURL:resultingItemURL:error:"), url, nullptr, error); | ||
} | ||
|
||
static struct objc_object* error_localized_description(struct objc_object* self) { | ||
typedef struct objc_object* (*send_type)(struct objc_object*, struct objc_selector*); | ||
auto func = (send_type)objc_msgSend; | ||
return func(self, sel_registerName("localizedDescription")); | ||
} | ||
|
||
static const char* string_utf8_string(struct objc_object* self) { | ||
typedef const char* (*send_type)(struct objc_object*, struct objc_selector*); | ||
auto func = (send_type)objc_msgSend; | ||
return func(self, sel_registerName("UTF8String")); | ||
} | ||
|
||
static struct objc_object* url_file_url_with_path(struct objc_object* string) { | ||
auto url = objc_getClass("NSURL"); | ||
typedef struct objc_object* (*send_type)(struct objc_class*, struct objc_selector*, struct objc_object*); | ||
auto func = (send_type)objc_msgSend; | ||
return func(url, sel_registerName("fileURLWithPath:"), string); | ||
} | ||
|
||
int main(int argc, char* argv[argc + 1]) { | ||
int opt; | ||
while ((opt = getopt(argc, argv, "hV")) != -1) { | ||
switch (opt) { | ||
case 'h': | ||
printf("%s\n", usage); | ||
return EXIT_SUCCESS; | ||
case 'V': | ||
printf("can %s\n", CAN_BUILD); | ||
return EXIT_SUCCESS; | ||
default: | ||
fprintf(stderr, "%s\n", usage); | ||
return EXIT_FAILURE; | ||
} | ||
} | ||
if (__builtin_expect(argc == 1, false)) { | ||
fprintf(stderr, "%s\n", usage); | ||
return EXIT_FAILURE; | ||
} | ||
argc--; | ||
argv++; | ||
|
||
for (ssize_t i = 0; i < (ssize_t)argc; i++) { | ||
auto name = argv[i]; | ||
if (__builtin_expect(strcmp(name, ".") == 0 || strcmp(name, "..") == 0 || strcmp(name, "/") == 0, false)) { | ||
fprintf(stderr, "\"/\", \".\", and \"..\" may not be removed.\n"); | ||
return EXIT_FAILURE; | ||
} | ||
} | ||
|
||
// Avoid using the root user's trash when invoked with sudo. | ||
auto superuser = getenv("SUDO_USER"); | ||
if (__builtin_expect(superuser != nullptr, false)) { | ||
auto entry = getpwnam(superuser); | ||
if (__builtin_expect(!entry || seteuid(entry->pw_uid), false)) { | ||
fprintf(stderr, "%s\n", strerror(errno)); | ||
return EXIT_FAILURE; | ||
} | ||
} | ||
|
||
// Ignore flag separator. | ||
if (__builtin_expect(strcmp(argv[0], "--") == 0, false)) { | ||
argc--; | ||
argv++; | ||
} | ||
|
||
auto exit_code = EXIT_SUCCESS; | ||
auto file_manager = file_manager_default_manager(); | ||
for (ssize_t i = 0; i < (ssize_t)argc; i++) { | ||
auto path = file_manager_string_with_file_system_representation(file_manager, argv[i]); | ||
auto url = url_file_url_with_path(path); | ||
struct objc_object* err; | ||
if (__builtin_expect(!file_manager_trash_item_at_url(file_manager, url, &err), false)) { | ||
auto description = error_localized_description(err); | ||
fprintf(stderr, "%s\n", string_utf8_string(description)); | ||
exit_code = EXIT_FAILURE; | ||
} | ||
} | ||
return exit_code; | ||
} |