mirror of
https://github.com/NikitaIvanovV/ctpv.git
synced 2024-11-23 21:33:07 +01:00
Initial commit
This commit is contained in:
commit
16bdee675c
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
*.o
|
||||||
|
*.d
|
||||||
|
/gen/
|
||||||
|
/ctpv
|
||||||
|
/embed/embed
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 Nikita Ivanov
|
||||||
|
|
||||||
|
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.
|
53
Makefile
Normal file
53
Makefile
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
PREFIX := /usr/local
|
||||||
|
BINPREFIX := $(PREFIX)/bin
|
||||||
|
|
||||||
|
SRC := $(wildcard *.c)
|
||||||
|
OBJ := $(SRC:.c=.o)
|
||||||
|
DEP := $(OBJ:.o=.d)
|
||||||
|
PRE := $(wildcard prev/*.sh)
|
||||||
|
GEN := gen/prev/scripts.h gen/prev/helpers.h
|
||||||
|
|
||||||
|
CFLAGS += -Os -MD -Wall -Wextra -Wno-unused-parameter
|
||||||
|
LDFLAGS += -lmagic
|
||||||
|
|
||||||
|
all: ctpv
|
||||||
|
|
||||||
|
options:
|
||||||
|
@echo "CC = $(CC)"
|
||||||
|
@echo "CFLAGS = $(CFLAGS)"
|
||||||
|
@echo "LDFLAGS = $(LDFLAGS)"
|
||||||
|
|
||||||
|
install: ctpv
|
||||||
|
install -d $(BINPREFIX)
|
||||||
|
install $< $(BINPREFIX)
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
$(RM) $(BINPREFIX)/ctpv
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(RM) ctpv $(OBJ) $(DEP) $(GEN)
|
||||||
|
$(MAKE) -C embed clean
|
||||||
|
|
||||||
|
make_embed:
|
||||||
|
$(MAKE) -C embed
|
||||||
|
|
||||||
|
ctpv: $(OBJ)
|
||||||
|
|
||||||
|
ctpv.c: $(GEN)
|
||||||
|
|
||||||
|
gen/prev/scripts.h: embed/embed $(PRE)
|
||||||
|
@mkdir -p $(@D)
|
||||||
|
embed/embed -p prev_scr_ $(PRE) > $@
|
||||||
|
|
||||||
|
gen/prev/helpers.h: embed/embed helpers.sh
|
||||||
|
@mkdir -p $(@D)
|
||||||
|
embed/embed -p prev_ helpers.sh > $@
|
||||||
|
|
||||||
|
embed/embed: make_embed
|
||||||
|
@:
|
||||||
|
|
||||||
|
-include $(DEP)
|
||||||
|
|
||||||
|
.PHONY: all options install uninstall clean make_embed
|
||||||
|
|
||||||
|
.DELETE_ON_ERROR:
|
117
ctpv.c
Normal file
117
ctpv.c
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <magic.h>
|
||||||
|
|
||||||
|
#include "error.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "previews.h"
|
||||||
|
|
||||||
|
static magic_t magic;
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
int server;
|
||||||
|
} ctpv;
|
||||||
|
|
||||||
|
static void cleanup(void) {
|
||||||
|
cleanup_previews();
|
||||||
|
if (magic != NULL)
|
||||||
|
magic_close(magic);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int init_magic() {
|
||||||
|
magic = magic_open(MAGIC_MIME_TYPE);
|
||||||
|
ERRCHK_RET(!magic, "magic_open() failed");
|
||||||
|
|
||||||
|
ERRCHK_RET(magic_load(magic, NULL) != 0, "magic_load() failed: %s",
|
||||||
|
magic_error(magic));
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *get_mimetype(char const *path) {
|
||||||
|
const char *r = magic_file(magic, path);
|
||||||
|
if (!r) {
|
||||||
|
print_errorf("magic_file() failed: %s", magic_error(magic));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *get_ext(char const *path) {
|
||||||
|
const char *r = strrchr(path, '.');
|
||||||
|
if (!r)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return &r[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
static int server(void)
|
||||||
|
{
|
||||||
|
/* TODO */
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define GET_PARG(a, i) (a) = argc > (i) ? argv[i] : NULL
|
||||||
|
|
||||||
|
static int client(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
char *f, *w, *h, *x, *y;
|
||||||
|
GET_PARG(f, 0);
|
||||||
|
GET_PARG(w, 1);
|
||||||
|
GET_PARG(h, 2);
|
||||||
|
GET_PARG(x, 3);
|
||||||
|
GET_PARG(y, 4);
|
||||||
|
|
||||||
|
ERRCHK_RET(!f, "file not given");
|
||||||
|
ERRCHK_RET(access(f, R_OK) != 0, "failed to access '%s': %s", f,
|
||||||
|
strerror(errno));
|
||||||
|
|
||||||
|
ERRCHK_RET_OK(init_magic());
|
||||||
|
|
||||||
|
init_previews(previews, LEN(previews));
|
||||||
|
|
||||||
|
const char *mimetype = get_mimetype(f);
|
||||||
|
ERRCHK_RET(!mimetype);
|
||||||
|
|
||||||
|
Preview *p = find_preview(get_ext(f), mimetype);
|
||||||
|
if (!p) {
|
||||||
|
puts("no preview found");
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
PreviewArgs args = { .f = f, .w = w, .h = h, .x = x, .y = y };
|
||||||
|
|
||||||
|
ERRCHK_RET_OK(run_preview(p, &args));
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
program = argc > 0 ? argv[0] : "ctpv";
|
||||||
|
|
||||||
|
int c;
|
||||||
|
while ((c = getopt(argc, argv, "s")) != -1) {
|
||||||
|
switch (c) {
|
||||||
|
case 's':
|
||||||
|
ctpv.server = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret;
|
||||||
|
if (ctpv.server)
|
||||||
|
ret = server();
|
||||||
|
else
|
||||||
|
ret = client(argc, &argv[optind]);
|
||||||
|
|
||||||
|
cleanup();
|
||||||
|
|
||||||
|
return ret == OK ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||||
|
}
|
6
embed/Makefile
Normal file
6
embed/Makefile
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
embed: embed.c
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(RM) embed
|
||||||
|
|
||||||
|
.PHONY: clean
|
72
embed/embed.c
Normal file
72
embed/embed.c
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
void getvarname(char *res, char *prefix, char *filename)
|
||||||
|
{
|
||||||
|
char *s = strrchr(filename, '/');
|
||||||
|
if (s)
|
||||||
|
s++;
|
||||||
|
else
|
||||||
|
s = filename;
|
||||||
|
|
||||||
|
if (prefix) {
|
||||||
|
size_t prefix_len = strlen(prefix);
|
||||||
|
memcpy(res, prefix, prefix_len);
|
||||||
|
res += prefix_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int c, i = 0; s[i] != 0; i++) {
|
||||||
|
c = s[i];
|
||||||
|
if (!isalnum(c))
|
||||||
|
c = '_';
|
||||||
|
|
||||||
|
res[i] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void embed_file(char *prefix, char *filename)
|
||||||
|
{
|
||||||
|
FILE *f = fopen(filename, "r");
|
||||||
|
if (!f) {
|
||||||
|
fprintf(stderr, "failed to open %s: %s\n", filename, strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char varname[FILENAME_MAX];
|
||||||
|
getvarname(varname, prefix, filename);
|
||||||
|
|
||||||
|
printf("char %s[] = { ", varname);
|
||||||
|
|
||||||
|
int c;
|
||||||
|
while ((c = fgetc(f)) != EOF)
|
||||||
|
printf("0x%x, ", c);
|
||||||
|
|
||||||
|
puts("0 };");
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
char *prefix = NULL;
|
||||||
|
|
||||||
|
int c;
|
||||||
|
while ((c = getopt(argc, argv, "p:")) != -1) {
|
||||||
|
switch (c) {
|
||||||
|
case 'p':
|
||||||
|
prefix = optarg;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = optind; i < argc; i++)
|
||||||
|
embed_file(prefix, argv[i]);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
16
error.c
Normal file
16
error.c
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "error.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
void print_error(char const *error_msg)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: %s\n", program, error_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_errorf(char const *format, ...)
|
||||||
|
{
|
||||||
|
char s[1024];
|
||||||
|
FORMATTED_STRING(s, format);
|
||||||
|
print_error(s);
|
||||||
|
}
|
33
error.h
Normal file
33
error.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#ifndef ERROR_H
|
||||||
|
#define ERROR_H
|
||||||
|
|
||||||
|
#define _ERRCHK_RET_PR(format, ...) \
|
||||||
|
print_error##__VA_OPT__(f)(format __VA_OPT__(, ) __VA_ARGS__)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If cond is true, return ERR. Also call print_error or
|
||||||
|
* print_errorf if error message or format string is given.
|
||||||
|
*/
|
||||||
|
#define ERRCHK_RET(cond, ...) \
|
||||||
|
do { \
|
||||||
|
if (cond) { \
|
||||||
|
__VA_OPT__(_ERRCHK_RET_PR(__VA_ARGS__);) \
|
||||||
|
return ERR; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Shortcut for ERRCHK_RET(expr != OK)
|
||||||
|
*/
|
||||||
|
#define ERRCHK_RET_OK(expr, ...) \
|
||||||
|
ERRCHK_RET((expr) != OK __VA_OPT__(, ) __VA_ARGS__)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
OK,
|
||||||
|
ERR,
|
||||||
|
};
|
||||||
|
|
||||||
|
void print_error(char const *error_msg);
|
||||||
|
void print_errorf(char const *format, ...);
|
||||||
|
|
||||||
|
#endif
|
3
helpers.sh
Normal file
3
helpers.sh
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
exists() {
|
||||||
|
command -v "$1" > /dev/null
|
||||||
|
}
|
1
prev/file.sh
Normal file
1
prev/file.sh
Normal file
@ -0,0 +1 @@
|
|||||||
|
echo "$f"
|
176
preview.c
Normal file
176
preview.c
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
#include "error.h"
|
||||||
|
#include "preview.h"
|
||||||
|
|
||||||
|
#define PREVP_SIZE sizeof(Preview *)
|
||||||
|
|
||||||
|
static char shell[] = "sh";
|
||||||
|
|
||||||
|
static Preview **sorted_by_ext,
|
||||||
|
**sorted_by_mimetype;
|
||||||
|
static size_t prevs_length;
|
||||||
|
|
||||||
|
static int cmp_prev_ext(const void *p1, const void *p2)
|
||||||
|
{
|
||||||
|
Preview *pr1 = *(Preview **)p1;
|
||||||
|
Preview *pr2 = *(Preview **)p2;
|
||||||
|
|
||||||
|
if (!pr1->ext)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (!pr2->ext)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return strcmp(pr1->ext, pr2->ext);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmp_prev_mimetype(const void *p1, const void *p2)
|
||||||
|
{
|
||||||
|
Preview *pr1 = *(Preview **)p1;
|
||||||
|
Preview *pr2 = *(Preview **)p2;
|
||||||
|
|
||||||
|
if (pr1->type[0] == '\0')
|
||||||
|
return 1;
|
||||||
|
else if (pr2->type[0] == '\0')
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!pr1->subtype && pr2->subtype) {
|
||||||
|
return 1;
|
||||||
|
} else if (pr1->subtype && !pr2->subtype) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
int ret = strcmp(pr1->type, pr2->type);
|
||||||
|
if (ret == 0 && pr1->subtype && pr2->subtype)
|
||||||
|
return strcmp(pr1->subtype, pr2->subtype);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_previews(Preview *ps, size_t len)
|
||||||
|
{
|
||||||
|
prevs_length = len;
|
||||||
|
size_t size = len * PREVP_SIZE;
|
||||||
|
|
||||||
|
sorted_by_ext = malloc(size);
|
||||||
|
sorted_by_mimetype = malloc(size);
|
||||||
|
if (!sorted_by_ext || !sorted_by_mimetype) {
|
||||||
|
print_error("malloc() failed");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < len; i++)
|
||||||
|
sorted_by_ext[i] = sorted_by_mimetype[i] = &ps[i];
|
||||||
|
|
||||||
|
qsort(sorted_by_ext, len, PREVP_SIZE, cmp_prev_ext);
|
||||||
|
qsort(sorted_by_mimetype, len, PREVP_SIZE, cmp_prev_mimetype);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanup_previews(void)
|
||||||
|
{
|
||||||
|
if (sorted_by_ext) {
|
||||||
|
free(sorted_by_ext);
|
||||||
|
sorted_by_ext = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sorted_by_mimetype) {
|
||||||
|
free(sorted_by_mimetype);
|
||||||
|
sorted_by_mimetype = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
prevs_length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Preview *find_by_ext(char const *ext)
|
||||||
|
{
|
||||||
|
Preview *key = &(Preview){ .ext = (char *)ext };
|
||||||
|
|
||||||
|
void *p =
|
||||||
|
bsearch(&key, sorted_by_ext, prevs_length, PREVP_SIZE, cmp_prev_ext);
|
||||||
|
|
||||||
|
return p ? *(Preview **)p : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void break_mimetype(char *mimetype, char **type, char **subtype)
|
||||||
|
{
|
||||||
|
*type = mimetype[0] == '\0' ? NULL : mimetype;
|
||||||
|
*subtype = NULL;
|
||||||
|
|
||||||
|
char *s = strchr(mimetype, '/');
|
||||||
|
if (!s) {
|
||||||
|
print_errorf("invalid mimetype: '%s'", mimetype);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
*s = '\0';
|
||||||
|
*subtype = &s[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MIMETYPE_MAX 64
|
||||||
|
|
||||||
|
static Preview *find_by_mimetype(char const *mimetype)
|
||||||
|
{
|
||||||
|
Preview *p;
|
||||||
|
char mimetype_c[MIMETYPE_MAX], *t, *s;
|
||||||
|
|
||||||
|
strncpy(mimetype_c, mimetype, MIMETYPE_MAX - 1);
|
||||||
|
break_mimetype(mimetype_c, &t, &s);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < prevs_length; i++) {
|
||||||
|
p = sorted_by_mimetype[i];
|
||||||
|
|
||||||
|
if (strcmp(t, p->type) == 0) {
|
||||||
|
if (p->subtype) {
|
||||||
|
if (strcmp(s, p->subtype) == 0)
|
||||||
|
return p;
|
||||||
|
} else {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Preview *find_preview(char const *ext, char const *mimetype)
|
||||||
|
{
|
||||||
|
if (!sorted_by_ext || !sorted_by_mimetype) {
|
||||||
|
print_error("init_previews() not called");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
Preview *ret = NULL;
|
||||||
|
if (mimetype)
|
||||||
|
ret = find_by_mimetype(mimetype);
|
||||||
|
if (!ret)
|
||||||
|
ret = find_by_ext(ext);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SET_PENV(n, v) \
|
||||||
|
do { \
|
||||||
|
if (v) \
|
||||||
|
ERRCHK_RET(setenv((n), (v), 1) != 0); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
int run_preview(Preview *p, PreviewArgs *pa)
|
||||||
|
{
|
||||||
|
SET_PENV("f", pa->f);
|
||||||
|
SET_PENV("w", pa->w);
|
||||||
|
SET_PENV("h", pa->h);
|
||||||
|
SET_PENV("x", pa->x);
|
||||||
|
SET_PENV("y", pa->y);
|
||||||
|
|
||||||
|
char *args[] = { shell, "-c", p->script, shell, NULL };
|
||||||
|
int ret, exitcode;
|
||||||
|
|
||||||
|
ret = spawn(args, NULL, &exitcode);
|
||||||
|
|
||||||
|
if (exitcode > 0)
|
||||||
|
printf("error: preview exited with code: %d\n", exitcode);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
19
preview.h
Normal file
19
preview.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#ifndef PREVIEW_H
|
||||||
|
#define PREVIEW_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char *ext, *type, *subtype, *script;
|
||||||
|
} Preview;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char *f, *w, *h, *x, *y;
|
||||||
|
} PreviewArgs;
|
||||||
|
|
||||||
|
void init_previews(Preview *ps, size_t len);
|
||||||
|
void cleanup_previews(void);
|
||||||
|
Preview *find_preview(char const *ext, char const *mimetype);
|
||||||
|
int run_preview(Preview *p, PreviewArgs *pa);
|
||||||
|
|
||||||
|
#endif
|
16
previews.h
Normal file
16
previews.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "gen/prev/scripts.h"
|
||||||
|
#include "preview.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is supposed to be included in ctpv.c
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define P(e, t, s, f) { e, t, s, f }
|
||||||
|
|
||||||
|
Preview previews[] = {
|
||||||
|
P(NULL, "text", "plain", prev_scr_file_sh),
|
||||||
|
};
|
||||||
|
|
||||||
|
#undef P
|
90
utils.c
Normal file
90
utils.c
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "error.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
char *program;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Call command
|
||||||
|
*
|
||||||
|
* If cpid is NULL, wait for the command to finish executing;
|
||||||
|
* otherwise store pid in cpid
|
||||||
|
*/
|
||||||
|
int spawn(char *args[], pid_t *cpid, int *exitcode)
|
||||||
|
{
|
||||||
|
if (exitcode)
|
||||||
|
*exitcode = -1;
|
||||||
|
|
||||||
|
pid_t pid = fork();
|
||||||
|
ERRCHK_RET(pid == -1, "fork() failed");
|
||||||
|
|
||||||
|
/* Child process */
|
||||||
|
if (pid == 0) {
|
||||||
|
execvp(args[0], args);
|
||||||
|
print_errorf("exec() failed: %s", strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cpid) {
|
||||||
|
*cpid = pid;
|
||||||
|
} else {
|
||||||
|
int stat;
|
||||||
|
ERRCHK_RET(waitpid(pid, &stat, 0) == -1, "waitpid() failed: %s",
|
||||||
|
strerror(errno));
|
||||||
|
|
||||||
|
if (exitcode && WIFEXITED(stat))
|
||||||
|
*exitcode = WEXITSTATUS(stat);
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
CharVec char_v_new(size_t cap)
|
||||||
|
{
|
||||||
|
CharVec v;
|
||||||
|
v.buf = NULL;
|
||||||
|
v.len = 0;
|
||||||
|
v.cap = cap;
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
void char_v_free(CharVec *v)
|
||||||
|
{
|
||||||
|
if (v->buf) {
|
||||||
|
free(v->buf);
|
||||||
|
v->buf = NULL;
|
||||||
|
v->len = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void char_v_append(CharVec *v, char c)
|
||||||
|
{
|
||||||
|
if (!v->buf) {
|
||||||
|
v->buf = malloc(v->cap * sizeof(v->buf[0]));
|
||||||
|
if (!v->buf) {
|
||||||
|
print_error("calloc() failed");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
v->buf[0] = '\0';
|
||||||
|
v->len++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v->len + 1 >= v->cap) {
|
||||||
|
v->cap *= 2;
|
||||||
|
v->buf = realloc(v->buf, v->cap * sizeof(v->buf[0]));
|
||||||
|
if (!v->buf) {
|
||||||
|
print_error("realloc() failed");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
v->buf[v->len - 1] = c;
|
||||||
|
v->buf[v->len] = '\0';
|
||||||
|
v->len++;
|
||||||
|
}
|
32
utils.h
Normal file
32
utils.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#ifndef UTILS_H
|
||||||
|
#define UTILS_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#define LEN(a) (sizeof(a) / sizeof((a)[0]))
|
||||||
|
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||||
|
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||||
|
|
||||||
|
#define FORMATTED_STRING(arr, format) \
|
||||||
|
do { \
|
||||||
|
va_list args; \
|
||||||
|
va_start(args, (format)); \
|
||||||
|
vsnprintf((arr), LEN(arr) - 1, (format), args); \
|
||||||
|
va_end(args); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char *buf;
|
||||||
|
size_t len, cap;
|
||||||
|
} CharVec;
|
||||||
|
|
||||||
|
extern char *program;
|
||||||
|
|
||||||
|
int spawn(char *args[], pid_t *cpid, int *exitcode);
|
||||||
|
|
||||||
|
CharVec char_v_new(size_t cap);
|
||||||
|
void char_v_append(CharVec *v, char c);
|
||||||
|
void char_v_free(CharVec *v);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user