init
This commit is contained in:
222
source/dictate.c
Normal file
222
source/dictate.c
Normal 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
74
source/dictate.h
Normal 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
221
source/directive.c
Normal 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
11
source/directive.h
Normal 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
38
source/error.c
Normal 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
21
source/error.h
Normal 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
210
source/file_utils.c
Normal 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
21
source/file_utils.h
Normal 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
12
source/global.h
Normal 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
90
source/kvec.h
Normal 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
92
source/main.c
Normal 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
68
source/opts.c
Normal 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
10
source/opts.h
Normal 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
|
Reference in New Issue
Block a user