This commit is contained in:
anon
2025-01-23 09:41:36 +01:00
commit b3437f8f62
14 changed files with 1107 additions and 0 deletions

222
source/dictate.c Normal file
View File

@ -0,0 +1,222 @@
#include "dictate.h"
/* ## Dictate Imeplementation
*/
// Do not ever set by hand, might be subject to change
static int color_enabled_global__ = 1;
static int pedantic_flushing__ = 1;
void dictate_pedantic_flush(int b) { pedantic_flushing__ = b; }
void dictate_color_enabled(int b) { color_enabled_global__ = b; }
// Every other function is ultimetly a wrapper around this one
static
void vararg_file_margin_dictate_conditional_format(
FILE * f,
char margin,
int do_process_format,
const char * fmt,
va_list args
) {
inline
void print_margin(char margin) {
const int margin_width = 3;
if (margin) {
for (int i = 0; i < margin_width; i++) {
fputc(margin, f);
}
fputc(' ', f);
}
}
print_margin(margin);
for (const char * s = fmt; *s != '\0'; s++) {
switch (*s) {
case '$': { // Color handling
if (color_enabled_global__) {
switch (*(++s)) {
case 'r': fprintf(f, "\033[31m"); break;
case 'g': fprintf(f, "\033[32m"); break;
case 'b': fprintf(f, "\033[34m"); break;
case 'y': fprintf(f, "\033[33m"); break;
case 'm': fprintf(f, "\033[35m"); break;
case 'c': fprintf(f, "\033[36m"); break;
case 'B': fprintf(f, "\033[1m"); break;
case 'I': fprintf(f, "\033[3m"); break;
case '0': fprintf(f, "\033[0m"); break;
default: --s; break; // Invalid color code, backtrack
}
} else {
switch (*(++s)) {
// Don't echo valid color sequences
case 'r': ;
case 'g': ;
case 'b': ;
case 'y': ;
case 'm': ;
case 'c': ;
case 'B': ;
case 'I': ;
case '0': break;
default: --s; break;
}
}
} break;
case '%': { // fmt specifiers
if (!do_process_format) {
fputc('%', f);
break;
}
switch (*(++s)) {
case 'd': { // Decimal
long long val = va_arg(args, long long);
fprintf(f, "%lld", val);
} break;
case 'x': { // Hex
unsigned int val = va_arg(args, unsigned int);
fprintf(f, "%x", val);
} break;
case 's': { // String
const char * str = va_arg(args, const char *);
fprintf(f, "%s", str);
} break;
case 'c': { // Char
char ch = (char)va_arg(args, int);
fprintf(f, "%c", ch);
} break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
{ // Static width
int width;
for (width = 0; *s >= '0' && *s <= '9'; s++) {
width = width * 10 + (*s - '0');
}
--s;
goto p;
case '*': // Dynamic width
width = va_arg(args, long long);
p:
switch (*(++s)) {
case 'd': {
long long x = va_arg(args, long long);
fprintf(f, "%*lld", width, x);
} break;
case 'x': {
unsigned int x = va_arg(args, unsigned int);
fprintf(f, "%*x", width, x);
} break;
case 's': {
const char * x = va_arg(args, const char *);
fprintf(f, "%*s", width, x);
} break;
case 'c': {
char x = (char)va_arg(args, int);
fprintf(f, "%*c", width, x);
} break;
default: --s; break;
}
} break;
default: --s;
} break;
} break;
case '\n': { // Margin handling
fputc('\n', f);
if (*(s+1) != '\0') {
print_margin(margin);
}
} break;
default: { // Regular characters
fputc(*s, f);
} break;
}
}
if (pedantic_flushing__) {
fflush(f);
}
}
static
void file_margin_dictate_conditional_format(FILE *f, char margin, const char * str, ...) {
va_list args;
va_start(args, str);
vararg_file_margin_dictate_conditional_format(f, margin, 0, str, args);
va_end(args);
}
void vafmdictatef(FILE * f, char margin, const char * fmt, va_list args) {
vararg_file_margin_dictate_conditional_format(f, margin, 1, fmt, args);
}
void fmdictate(FILE *f, char margin, const char * str) {
file_margin_dictate_conditional_format(f, margin, str);
fputs("\n", f);
}
// Wrapping fmdictate
void dictate(const char * str) {
fmdictate(stdout, '\00', str);
}
void fdictate(FILE * f, const char * str) {
fmdictate(f, '\00', str);
}
void mdictate(char margin, const char * str) {
fmdictate(stdout, margin, str);
}
// Wrapping vafmdictatef
void fmdictatef(FILE * f, char margin, const char * fmt, ...) {
va_list args;
va_start(args, fmt);
vafmdictatef(f, margin, fmt, args);
va_end(args);
}
void dictatef(const char * fmt, ...) {
va_list args;
va_start(args, fmt);
vafmdictatef(stdout, '\00', fmt, args);
va_end(args);
}
void vadictatef(const char * fmt, va_list args) {
vafmdictatef(stdout, '\00', fmt, args);
}
void fdictatef(FILE * f, const char * fmt, ...) {
va_list args;
va_start(args, fmt);
vafmdictatef(f, '\00', fmt, args);
va_end(args);
}
void vafdictatef(FILE * f, const char * fmt, va_list args) {
vafmdictatef(f, '\00', fmt, args);
}
void mdictatef(char margin, const char * fmt, ...) {
va_list args;
va_start(args, fmt);
vafmdictatef(stdout, margin, fmt, args);
va_end(args);
}
void vamdictatef(char margin, const char * fmt, va_list args) {
vafmdictatef(stdout, margin, fmt, args);
}
// Dictate is in the Public Domain, and if say this is not a legal notice, I will sue you.

74
source/dictate.h Normal file
View File

@ -0,0 +1,74 @@
#ifndef DICTATE_H
#define DICTATE_H
/* # Dictate
* Dictate is a family of output functions with the designed to be comfortable, not robust.
* It's intended use-case is within the terminal.
*/
#include <stdarg.h>
#include <stdio.h>
/* ## State
* Dictate has global state for convinience.
*/
/* Flush after every print.
* Useful for debugging when the process could terminate unexpectedly.
*/
void dictate_pedantic_flush(int b);
/* Enable or disable the processing of color sequences (see below).
* When colors are disabled, color sequences are not printed at all.
*/
void dictate_color_enabled(int b);
/* ## Grammar
* Dictate functions follow a similar paradigm as <stdio.h>.
*
* [a] [va] [TARGET] [m] dictate [f]
*
* a -> allocate (must be used along side 'str')
* va -> take a va_list (instead of varargs)
* m -> margin; specifies a prefix inserted before each line
* f -> printf style formatted output; if ommited, a new line will be appended
*
* TARGET:
* NOTE -> stdout
* f -> FILE *
*/
void dictate(const char * str);
void fdictate(FILE * f, const char * str);
void mdictate(char margin, const char * str);
void fmdictate(FILE * f, char margin, const char * str);
void dictatef(const char * fmt, ...);
void vadictatef(const char * fmt, va_list args);
void fdictatef(FILE * f, const char * fmt, ...);
void vafdictatef(FILE * f, const char * fmt, va_list args);
void mdictatef(char margin, const char * fmt, ...);
void vamdictatef(char margin, const char * fmt, va_list args);
void fmdictatef(FILE *f, char margin, const char * fmt, ...);
void vafmdictatef(FILE * f, char margin, const char * fmt, va_list args); // NOTE: core function
/* # Format
* Dictate supports the most common subset of printf formats.
* - Width specification (hard coded number and *).
* - Placeholders:
* %d -> Decimal signed long long
* %x -> Hexadecimal number
* %s -> C string
* %c -> Single character
* - Colors (ineffective if color is disabled):
* $r -> Red
* $g -> Green
* $b -> Blue
* $y -> Yellow
* $m -> Magenta
* $c -> Cyan
* $B -> Bold
* $I -> Italic
* $0 -> Reset
*/
// Dictate is in the Public Domain, and if say this is not a legal notice, I will sue you.
#endif

221
source/directive.c Normal file
View File

@ -0,0 +1,221 @@
#include "directive.h"
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <pwd.h>
#include <grp.h>
#include "kvec.h"
#include "global.h"
#include "error.h"
#include "file_utils.h"
typedef struct {
char * name;
struct stat st;
} entry_t;
static
int entry_cmp(const void * a, const void * b) { // For qsort()
const entry_t * const A = a;
const entry_t * const B = b;
return strcmp(A->name, B->name);
}
static kvec_t(entry_t) entries;
static kvec_t(const char*) directory_queue;
static
int add_directory(const char * const folder) {
DIR * dir = opendir(folder);
CHECK_OPEN(dir, folder, return 1);
char full_path[1024];
struct stat file_stat;
struct dirent * mydirent;
entry_t entry;
while ((mydirent = readdir(dir)) != NULL) {
if (strcmp(mydirent->d_name, ".") == 0
|| strcmp(mydirent->d_name, "..") == 0) {
continue;
}
sprintf(full_path, "%s/%s",
folder,
mydirent->d_name
);
int e = stat(full_path, &file_stat);
if (e == -1) {
errorn(E_FILE_ACCESS, full_path);
return 1;
}
entry = (entry_t) {
.name = strdup(full_path),
.st = file_stat,
};
kv_push(entry_t, entries, entry);
if (is_recursive
&& (kv_A(entries, entries.n-1).st.st_mode & S_IFDIR)) {
kv_push(const char*, directory_queue, kv_A(entries, entries.n-1).name);
}
}
closedir(dir);
}
int init_directive_c(const char * folder) {
init_file_utils(is_dry_run);
kv_init(entries);
kv_init(directory_queue);
kv_push(const char*, directory_queue, folder);
while (directory_queue.n) {
add_directory(kv_pop(directory_queue));
}
qsort(
entries.a,
entries.n,
sizeof(entry_t),
entry_cmp
);
return 0;
}
int deinit_directive_c(void) {
for (int i = 0; i < entries.n; i++) {
free(kv_A(entries, i).name);
}
kv_destroy(directory_queue);
kv_destroy(entries);
deinit_file_utis();
}
int make_directive_file(FILE * f) {
for (int i = 0; i < entries.n; i++) {
entry_t * entry = &kv_A(entries, i);
// ID
fprintf(f, "%03d",
i
);
// Permissions
if (do_permissions) {
char permissions[11];
fprintf(f, "\t%s",
mode_to_str(entry->st.st_mode, permissions)
);
}
// Owner
if (do_owner) {
struct passwd * pw = getpwuid(entry->st.st_uid);
struct group * gr = getgrgid(entry->st.st_gid);
fprintf(f, "\t%s:%s",
pw->pw_name,
gr->gr_name
);
}
// Name
fprintf(f, "\t%s",
entry->name
);
// if Directory
if (entry->st.st_mode & S_IFDIR) {
putc('/', f);
}
putc('\n', f);
}
return 0;
}
static inline
char * next_field(const char * s) {
while (*s != '\t'
&& *s != '\n'
&& *s != '\0') {
++s;
}
return (char*)(++s);
}
int execute_directive_file(FILE * f) {
#define NEXT_FIELD do { \
if (*(sp = next_field(sp)) == '\0') { \
errorn(E_FORMAT); \
return 1; \
} \
} while (0)
const int LINE_SIZE = 1024;
char line[LINE_SIZE];
char buffer[1024];
char * sp;
int expected_id = 0;
int id;
while (fgets(line, LINE_SIZE, f) != NULL) {
sp = line;
// ID
int e = sscanf(line, "%d\t", &id);
// creation
if (e != 1) {
sscanf(sp, "%s\n", buffer);
mytouch(buffer);
continue;
}
// deletion
while (expected_id != id) {
const char * last_filename = kv_A(entries, expected_id).name;
mydelete(last_filename);
++expected_id;
}
++expected_id;
NEXT_FIELD;
entry_t * entry = &kv_A(entries, id);
// Permission
if (do_permissions) {
mode_t mode;
sscanf(sp, "%s\t", buffer);
mode = str_to_mode(buffer);
if (entry->st.st_mode != mode) {
mychmod(entry->name, mode);
}
NEXT_FIELD;
}
// Owner
if (do_owner) {
char buffer2[113];
sscanf(sp, "%s:%s\t", buffer, buffer2);
//mychown(filename, buffer, buffer2);
NEXT_FIELD;
}
// Name
sscanf(sp, "%s\n", buffer);
size_t len = strlen(buffer);
if (buffer[len-1] == '/') {
buffer[len-1] = '\0';
}
if (strcmp(entry->name, buffer)) {
mymove(entry->name, buffer);
}
}
}

11
source/directive.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef COMMAND_FILE_H
#define COMMAND_FILE_H
#include <stdio.h>
extern int init_directive_c(const char * folder_);
extern int deinit_directive_c(void);
extern int make_directive_file(FILE * f);
extern int execute_directive_file(FILE * f);
#endif

38
source/error.c Normal file
View File

@ -0,0 +1,38 @@
#include "error.h"
#include <stdarg.h>
#include <stdio.h>
#include "dictate.h"
static
void verror(const char * fmt, va_list argv) {
fdictatef(stderr, "vimdir: error: ");
vafdictatef(stderr, fmt, argv);
fdictate(stderr, "");
}
void warning(const char * fmt, ...) {
va_list argv;
va_start(argv, fmt);
fdictatef(stderr, "vimdir: warning: ");
vafdictatef(stderr, fmt, argv);
fdictate(stderr, "");
va_end(argv);
}
void errorn(int i, ...) {
va_list argv;
va_start(argv, i);
switch (i) {
case E_OPEN_EDITOR: verror("failed to open editor '%s'", argv); break;
case E_FILE_ACCESS: verror("failed to interact with file '%s'", argv); break;
case E_FILE_DELETE: verror("failed to delete file '%s'", argv); break;
case E_FILE_MOVE: verror("failed to move '%s' to '%s'", argv); break;
case E_FORMAT: verror("directive-file format violation", argv); break;
}
va_end(argv);
}

21
source/error.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef ERROR_H
#define ERROR_H
enum {
E_OPEN_EDITOR,
E_FILE_ACCESS,
E_FILE_DELETE,
E_FILE_MOVE,
E_FORMAT,
};
extern void errorn(int i, ...);
extern void warning(const char * fmt, ...);
#define CHECK_OPEN(f, n, E) \
if (!f) { \
errorn(E_FILE_ACCESS, f); \
E; \
}
#endif

210
source/file_utils.c Normal file
View File

@ -0,0 +1,210 @@
#include "file_utils.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include "error.h"
int (*mytouch)(const char *filename) = NULL;
int (*mydelete)(const char *filename) = NULL;
int (*mychmod)(const char *filename, mode_t mode) = NULL;
int (*mychown)(const char *filename, const char *owner, const char *group) = NULL;
int (*mymove)(const char *filename, const char *newname) = NULL;
static int dry_touch(const char * filename);
static int dry_delete(const char * filename);
static int dry_chmod(const char * filename, mode_t mode);
static int dry_chown(const char * filename, const char * owner, const char * group);
static int dry_move(const char * filename, const char * newname);
static int moist_touch(const char * filename);
static int moist_delete(const char * filename);
static int moist_chmod(const char * filename, mode_t mode);
static int moist_chown(const char * filename, const char * owner, const char * group);
static int moist_move(const char * filename, const char * newname);
int init_file_utils(bool is_dry_run) {
if (is_dry_run) {
mytouch = dry_touch;
mydelete = dry_delete;
mychmod = dry_chmod;
mychown = dry_chown;
mymove = dry_move;
} else {
//mytouch = moist_touch;
//mydelete = moist_delete;
//mychmod = moist_chmod;
//mychown = moist_chown;
//mymove = moist_move;
}
}
int deinit_file_utis() {
mytouch = NULL;
mydelete = NULL;
mychmod = NULL;
mychown = NULL;
mymove = NULL;
}
char mode_type_to_char(mode_t m) {
switch (m & S_IFMT) {
case S_IFREG: return '-'; // regular file
case S_IFDIR: return 'd'; // directory
case S_IFCHR: return 'c'; // character device
case S_IFBLK: return 'b'; // block device
case S_IFIFO: return 'p'; // fifo (pipe)
case S_IFLNK: return 'l'; // symbolic link
case S_IFSOCK: return 's'; // socket
default: return '?'; // unknown
}
}
mode_t char_to_mode_type(const char c) {
switch (c) {
case '-': return S_IFREG; // regular file
case 'd': return S_IFDIR; // directory
case 'c': return S_IFCHR; // character device
case 'b': return S_IFBLK; // block device
case 'p': return S_IFIFO; // fifo (pipe)
case 'l': return S_IFLNK; // symbolic link
case 's': return S_IFSOCK; // socket
default: return 0; // unknown
}
}
char * mode_to_str(mode_t mode, char * buffer) {
buffer[0] = mode_type_to_char(mode);
buffer[1] = (mode & S_IRUSR) ? 'r' : '-';
buffer[2] = (mode & S_IWUSR) ? 'w' : '-';
buffer[3] = (mode & S_IXUSR) ? 'x' : '-';
buffer[4] = (mode & S_IRGRP) ? 'r' : '-';
buffer[5] = (mode & S_IWGRP) ? 'w' : '-';
buffer[6] = (mode & S_IXGRP) ? 'x' : '-';
buffer[7] = (mode & S_IROTH) ? 'r' : '-';
buffer[8] = (mode & S_IWOTH) ? 'w' : '-';
buffer[9] = (mode & S_IXOTH) ? 'x' : '-';
buffer[10] = '\0';
return buffer;
}
mode_t str_to_mode(const char *permissions) {
mode_t mode = 0;
mode |= char_to_mode_type(permissions[0]);
mode |= (permissions[1] == 'r') ? S_IRUSR : 0;
mode |= (permissions[2] == 'w') ? S_IWUSR : 0;
mode |= (permissions[3] == 'x') ? S_IXUSR : 0;
mode |= (permissions[4] == 'r') ? S_IRGRP : 0;
mode |= (permissions[5] == 'w') ? S_IWGRP : 0;
mode |= (permissions[6] == 'x') ? S_IXGRP : 0;
mode |= (permissions[7] == 'r') ? S_IROTH : 0;
mode |= (permissions[8] == 'w') ? S_IWOTH : 0;
mode |= (permissions[9] == 'x') ? S_IXOTH : 0;
return mode;
}
// --- Dry implementations
static
int dry_touch(const char * filename) {
warning("touch '%s'", filename);
return 0;
}
static
int dry_delete(const char * filename) {
warning("delete '%s'", filename);
return 0;
}
static
int dry_chmod(const char * filename, mode_t mode) {
char buf[11];
warning("chmod '%s' (%s)", filename, mode_to_str(mode, buf));
return 0;
}
static
int dry_chown(const char * filename, const char * owner, const char * group) {
warning("chown '%s' (%s:%s)", filename, owner, group);
return 0;
}
static
int dry_move(const char * filename, const char * newname) {
warning("rename '%s' (-> %s)", filename, newname);
return 0;
}
// --- Moist implementations
static
int moist_touch(const char * filename) {
FILE * f = fopen(filename, "w");
CHECK_OPEN(f, filename, return 1);
fclose(f);
return 0;
}
static
int moist_delete(const char * filename) {
if (unlink(filename) != 0) {
errorn(E_FILE_DELETE, filename);
return 1;
}
return 0;
}
static
int moist_chmod(const char * filename, mode_t mode) {
if (chmod(filename, mode) != 0) {
errorn(E_FILE_ACCESS, filename);
return 1;
}
return 0;
}
static
int moist_chown(const char * filename, const char * owner, const char * group) {
uid_t uid = -1;
gid_t gid = -1;
struct passwd * pwd = getpwnam(owner);
if (!pwd) {
fprintf(stderr, "Error: User '%s' not found\n", owner);
return 1;
}
uid = pwd->pw_uid;
struct group * grp = getgrnam(group);
if (!grp) {
fprintf(stderr, "Error: Group '%s' not found\n", group);
return 1;
}
gid = grp->gr_gid;
if (chown(filename, uid, gid) != 0) {
perror("Error changing file ownership");
return 1;
}
return 0;
}
static
int moist_move(const char * filename, const char * newname) {
if (rename(filename, newname) != 0) {
errorn(E_FILE_MOVE, filename, newname);
return 1;
}
return 0;
}

21
source/file_utils.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef FILE_UTILS_H
#define FILE_UTILS_H
#include <stdbool.h>
#include <sys/stat.h>
extern int init_file_utils(bool is_dry_run);
extern int deinit_file_utis();
extern char mode_type_to_char(mode_t m);
extern mode_t char_to_mode_type(const char c);
extern char * mode_to_str(mode_t mode, char * buffer);
extern mode_t str_to_mode(const char *permissions);
extern int (*mytouch)(const char *filename);
extern int (*mydelete)(const char *filename);
extern int (*mychmod)(const char *filename, mode_t mode);
extern int (*mychown)(const char *filename, const char *owner, const char *group);
extern int (*mymove)(const char *filename, const char *newname);
#endif

12
source/global.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef GLOBAL_H
#define GLOBAL_H
extern char * editor;
extern char * folder;
extern char * custom_rm;
extern bool is_dry_run;
extern bool is_recursive;
extern bool do_permissions;
extern bool do_owner;
#endif

90
source/kvec.h Normal file
View File

@ -0,0 +1,90 @@
/* The MIT License
Copyright (c) 2008, by Attractive Chaos <attractor@live.co.uk>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/*
An example:
#include "kvec.h"
int main() {
kvec_t(int) array;
kv_init(array);
kv_push(int, array, 10); // append
kv_a(int, array, 20) = 5; // dynamic
kv_A(array, 20) = 4; // static
kv_destroy(array);
return 0;
}
*/
/*
2008-09-22 (0.1.0):
* The initial version.
*/
#ifndef AC_KVEC_H
#define AC_KVEC_H
#include <stdlib.h>
#define kv_roundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
#define kvec_t(type) struct { size_t n, m; type *a; }
#define kv_init(v) ((v).n = (v).m = 0, (v).a = 0)
#define kv_destroy(v) free((v).a)
#define kv_A(v, i) ((v).a[(i)])
#define kv_pop(v) ((v).a[--(v).n])
#define kv_size(v) ((v).n)
#define kv_max(v) ((v).m)
#define kv_resize(type, v, s) ((v).m = (s), (v).a = (type*)realloc((v).a, sizeof(type) * (v).m))
#define kv_copy(type, v1, v0) do { \
if ((v1).m < (v0).n) kv_resize(type, v1, (v0).n); \
(v1).n = (v0).n; \
memcpy((v1).a, (v0).a, sizeof(type) * (v0).n); \
} while (0) \
#define kv_push(type, v, x) do { \
if ((v).n == (v).m) { \
(v).m = (v).m? (v).m<<1 : 2; \
(v).a = (type*)realloc((v).a, sizeof(type) * (v).m); \
} \
(v).a[(v).n++] = (x); \
} while (0)
#define kv_pushp(type, v) (((v).n == (v).m)? \
((v).m = ((v).m? (v).m<<1 : 2), \
(v).a = (type*)realloc((v).a, sizeof(type) * (v).m), 0) \
: 0), ((v).a + ((v).n++))
#define kv_a(type, v, i) (((v).m <= (size_t)(i)? \
((v).m = (v).n = (i) + 1, kv_roundup32((v).m), \
(v).a = (type*)realloc((v).a, sizeof(type) * (v).m), 0) \
: (v).n <= (size_t)(i)? (v).n = (i) + 1 \
: 0), (v).a[(i)])
#endif

92
source/main.c Normal file
View File

@ -0,0 +1,92 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "error.h"
#include "opts.h"
#include "directive.h"
char * editor = NULL;
char * folder = NULL;
char * custom_rm = NULL;
bool is_dry_run = false;
bool is_recursive = false;
bool do_permissions = false;
bool do_owner = false;
#define DEBUG
#define CHECK(x) do { \
if (x) { \
r = x; \
goto end; \
} \
} while (0)
int edit(const char * filename) {
size_t cmd_len = strlen(editor) + sizeof(' ') + strlen(filename) + 1;
char cmd[cmd_len];
snprintf(cmd, cmd_len, "%s %s", editor, filename);
// XXX
int result = system(cmd);
if (result == -1) {
errorn(E_OPEN_EDITOR, editor);
} else
if (WIFEXITED(result)
&& WEXITSTATUS(result) != 0) {
return 1;
}
return 0;
}
void clean_up(void) {
free(folder);
deinit_directive_c();
}
signed main(int argc, char * * argv) {
int r = 0;
get_env();
parse_args(argc, argv);
// XXX: what if the user passed '/'?
size_t len = strlen(folder);
if (folder[len-1] == '/') {
folder[len-1] = '\0';
}
char * tmpfile_name;
FILE * tmpfile;
#ifdef DEBUG
tmpfile_name = "vimdir_test_file.vimdir";
#else
tmpfile_name = mktemp("/tmp/vidirXXXXXX.vimdir");
#endif
tmpfile = fopen(tmpfile_name, "w+");
CHECK_OPEN(tmpfile, tmpfile_name, goto end);
CHECK(init_directive_c(folder));
CHECK(make_directive_file(tmpfile));
fflush(tmpfile);
CHECK(edit(tmpfile_name));
fclose(tmpfile);
tmpfile = fopen(tmpfile_name, "r");
CHECK_OPEN(tmpfile, tmpfile_name, goto end);
CHECK(execute_directive_file(tmpfile));
end:
if (tmpfile) {
fclose(tmpfile);
}
clean_up();
return r;
}

68
source/opts.c Normal file
View File

@ -0,0 +1,68 @@
#include "opts.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include "global.h"
void usage() {
puts("vimdir <path>");
puts(" -h : print help");
puts(" -n : dry run; do not modify files, just print operations");
puts(" -p : allow for editing permissions");
puts(" -o : allow for editing owner/group");
puts(" -r : recursive");
}
void get_env(void) {
set_editor:
editor = getenv("VIMDIREDITOR");
if (editor) { goto set_custom_rm; }
editor = getenv("EDITOR");
if (editor) { goto set_custom_rm; }
editor = "vi";
set_custom_rm:
custom_rm = get_env("VIMDIRRM");
end:
return;
}
void parse_args(int argc, char * * argv) {
int opt;
while ((opt = getopt(argc, argv, "hnpor")) != -1) {
switch (opt) {
case 'h': {
usage();
exit(0);
} break;
case 'n': {
is_dry_run = true;
} break;
case 'p': {
do_permissions = true;
} break;
case 'o': {
do_owner = true;
} break;
case 'r': {
is_recursive = true;
} break;
default: {
fprintf(stderr, "Unknown option: -%c\n", optopt);
usage();
exit(1);
} break;
}
}
if (optind < argc) {
folder = strdup(argv[optind]);
} else {
folder = strdup(".");
}
}

10
source/opts.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef OPTS_H
#define OPTS_H
#include <stddef.h>
#include <stdbool.h>
extern void get_env(void);
extern void parse_args(int argc, char * * argv);
#endif