first commit

This commit is contained in:
ngn
2024-07-16 20:37:46 +03:00
commit aeb375e8d3
21 changed files with 2117 additions and 0 deletions

91
src/args.c Normal file
View File

@@ -0,0 +1,91 @@
#include <libmp/all.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include "args.h"
#include "util.h"
void args_add(args_t *args, char *name){
if(NULL == args->list)
args->list = malloc(sizeof(arg_t)*(args->count+1));
else
args->list = realloc(args->list, sizeof(arg_t)*(args->count+1));
args->list[args->count].name = name;
args->list[args->count++].value = NULL;
}
void args_add_value(args_t *args, char *value){
if(NULL != args->list && args->list[args->count-1].value == NULL){
args->list[args->count-1].value = value;
return;
}
if(NULL == args->list)
args->list = malloc(sizeof(arg_t)*(args->count+1));
else
args->list = realloc(args->list, sizeof(arg_t)*(args->count+1));
args->list[args->count].name = NULL;
args->list[args->count++].value = value;
}
args_t *args_parse(int argc, char *argv[]){
args_t *args = malloc(sizeof(args_t));
bzero(args, sizeof(args_t));
if(argc <= 1)
return args;
for(int i = 1; i < argc; i++){
if(startswith(argv[i], "--"))
args_add(args, argv[i]+2);
else
args_add_value(args, argv[i]);
}
return args;
}
void args_free(args_t *args){
free(args->list);
free(args);
}
char *args_get_string(args_t *args, char *name){
for(int i = 0; i < args->count; i++){
if(NULL == args->list[i].name)
continue;
if(eq(args->list[i].name, name))
return args->list[i].value;
}
return NULL;
}
bool args_get_bool(args_t *args, char *name){
for(int i = 0; i < args->count; i++){
if(NULL == args->list[i].name)
continue;
if(eq(args->list[i].name, name))
return true;
}
return false;
}
int args_get_int(args_t *args, char *name){
for(int i = 0; i < args->count; i++){
if(NULL == args->list[i].name)
continue;
if(eq(args->list[i].name, name))
return atoi(args->list[i].value);
}
return -1;
}

18
src/args.h Normal file
View File

@@ -0,0 +1,18 @@
#pragma once
#include <stddef.h>
typedef struct arg {
char *name;
char *value;
} arg_t;
typedef struct args {
arg_t *list;
size_t count;
} args_t;
args_t *args_parse(int argc, char *argv[]);
char *args_get_string(args_t *args, char *name);
bool args_get_bool(args_t *args, char *name);
int args_get_int(args_t *args, char *name);
void args_free(args_t *args);

20
src/cmd.h Normal file
View File

@@ -0,0 +1,20 @@
#pragma once
#include <libmp/all.h>
#include <stdbool.h>
#include "config.h"
#include "args.h"
typedef bool (*cmd_func_t)(lm_ctx_t *ctx, config_t *config, args_t *args);
typedef struct cmd {
char *name;
char *desc;
cmd_func_t func;
} cmd_t;
bool cmd_sync(lm_ctx_t *ctx, config_t *config, args_t *args);
bool cmd_install(lm_ctx_t *ctx, config_t *config, args_t *args);
bool cmd_remove(lm_ctx_t *ctx, config_t *config, args_t *args);
bool cmd_update(lm_ctx_t *ctx, config_t *config, args_t *args);
bool cmd_list(lm_ctx_t *ctx, config_t *config, args_t *args);

116
src/cmd/install.c Normal file
View File

@@ -0,0 +1,116 @@
#include <libmp/all.h>
#include <libmp/ctx.h>
#include <libmp/error.h>
#include <stdio.h>
#include "../util.h"
#include "../cmd.h"
#include "../log.h"
bool cmd_install_callback(lm_ctx_t *ctx, lm_pkg_t *pkg, char *file, size_t current, size_t total, void *data){
bar(current, total);
return true;
}
bool cmd_download_callback(lm_ctx_t *ctx, lm_pkg_t *pkg, bool is_archive, size_t current, size_t total, void *data){
if(!is_archive)
bar(current, total);
return true;
}
bool cmd_install(lm_ctx_t *ctx, config_t *config, args_t *args){
lm_ctx_resolve_list_t *list = NULL;
ssize_t current = 1, size = 0;
char ssize[LONGSTR_MAX+3];
lm_pkg_t *pkg = NULL;
char *name = NULL;
bool ret = false;
if(lm_ctx_sync(ctx, false, NULL, NULL) <= 0){
error(_("There are no avaliable pools"));
goto end;
}
for(int i = 0; i < args->count; i++){
if(NULL != args->list[i].name)
continue;
if(eq((name = args->list[i].value), "install"))
continue;
// find the package
if((pkg = lm_ctx_pool_find(ctx, name, NULL)) == NULL){
error(_("Failed to find "FG_BOLD"%s"FG_RESET": %s"), name, lm_strerror());
goto end;
}
// WEIRD CRASH HERE
if(lm_ctx_database_is_installed(ctx, pkg, true)){
error(_("Package "FG_BOLD"%s"FG_RESET" ("FG_BLUE"%s"FG_RESET") is already installed"));
goto end;
}
// resolve the package and depends
if((list = lm_ctx_resolve(ctx, pkg, list)) == NULL){
error(_("Failed to resolve "FG_BOLD"%s"FG_RESET": %s"), name, lm_strerror());
goto end;
}
}
if(NULL == list){
error(_("Please specify at least one package to install"));
goto end;
}
while((pkg = lm_ctx_resolve_next(list)) != NULL)
size += pkg->size;
size_to_human(ssize, size);
info(_("Following packages will be "FG_BOLD"INSTALLED:"FG_RESET));
// list all the packages
info(_("Total of "FG_BOLD"%s"FG_RESET" disk space will be used"), ssize);
if(!yesno("Continue?")){
error("Operation cancelled");
goto end;
}
// download resolved packages
while((pkg = lm_ctx_resolve_next(list)) != NULL){
info(_("(%d/%d) Downloading "FG_BOLD"%s"FG_RESET" ("FG_BOLD FG_BLUE"%s"FG_RESET")"), current, list->count, pkg->name, pkg->version);
if(!lm_ctx_download(ctx, pkg, cmd_download_callback, NULL)){
error(_("Failed to download "FG_BOLD"%s"FG_RESET": %s"), pkg->name, lm_strerror());
goto end;
}
current++;
bar_free();
}
current = 1;
// install resolved packages
while((pkg = lm_ctx_resolve_next(list)) != NULL){
info(_("(%d/%d) Installing "FG_BOLD"%s"FG_RESET" ("FG_BOLD FG_BLUE"%s"FG_RESET")"), current, list->count, pkg->name, pkg->version);
if(!lm_ctx_install(ctx, pkg, cmd_install_callback, NULL)){
error(_("Failed to install "FG_BOLD"%s"FG_RESET": %s"), pkg->name, lm_strerror());
goto end;
}
current++;
bar_free();
}
if(current == list->count){
success("Installed all of the %d packages", list->count);
ret = true;
}
end:
bar_free();
lm_ctx_resolve_free(list);
return ret;
}

46
src/cmd/list.c Normal file
View File

@@ -0,0 +1,46 @@
#include <libmp/all.h>
#include <stdio.h>
#include "../cmd.h"
#include "../log.h"
bool cmd_list(lm_ctx_t *ctx, config_t *config, args_t *args){
ssize_t count = 0;
lm_pkg_t pkg;
if(args_get_bool(args, "help")){
info(_("Listing options for the install command:"));
printf(_(" "FG_BOLD"--grep"FG_RESET":\tmakes the output \"grepable\"\n"));
printf(_(" "FG_BOLD"--desc"FG_RESET":\tshow package descriptions\n"));
return true;
}
while(lm_ctx_database_next(ctx, &pkg))
count++;
lm_ctx_database_next_free(ctx, &pkg);
if(count <= 0 && !args_get_bool(args, "grep")){
info(_("There no installed packages"));
return true;
}
if(!args_get_bool(args, "grep"))
info(count > 1 ? _("Listing %d packages") : _("Listing %d package"));
while(lm_ctx_database_next(ctx, &pkg)){
if(args_get_bool(args, "grep")){
if(args_get_bool(args, "desc"))
printf("%s:%s:%s\n", pkg.name, pkg.version, pkg.desc);
else
printf("%s:%s\n", pkg.name, pkg.version);
}
if(args_get_bool(args, "desc"))
printf(FG_BOLD"%s ("FG_BLUE"%s"FG_RESET"): %s\n", pkg.name, pkg.version, pkg.desc);
else
printf(FG_BOLD"%s ("FG_BLUE"%s"FG_RESET")\n", pkg.name, pkg.version);
}
lm_ctx_database_next_free(ctx, &pkg);
return true;
}

62
src/cmd/sync.c Normal file
View File

@@ -0,0 +1,62 @@
#include <libmp/all.h>
#include <libmp/error.h>
#include <stdio.h>
#include "../cmd.h"
#include "../log.h"
bool cmd_sync_callback(lm_ctx_t *ctx, lm_pool_t *pool, lm_ctx_sync_state_t state, size_t current, size_t total, void *data){
switch (state) {
case SYNC_DOWNLOADING_INFO:
bar(current, total);
break;
case SYNC_INFO_SUCCESS:
bar_free();
break;
case SYNC_INFO_FAIL:
bar_free();
error(_("Failed to sync "FG_BOLD"%s"FG_RESET": %s"), pool->name, lm_strerror());
break;
case SYNC_DOWNLOADING_LIST:
bar(current, total);
break;
case SYNC_LIST_SUCCESS:
bar_free();
success(_("Synced "FG_BOLD"%s [%s]"FG_RESET), pool->name, pool->info.pubkey);
break;
case SYNC_LIST_FAIL:
bar_free();
if(LM_ERR_InfoNotLoaded != lm_error())
error(_("Failed to sync %s: %s"), pool->name, lm_strerror());
break;
}
return true;
}
bool cmd_sync(lm_ctx_t *ctx, config_t *config, args_t *args){
size_t sycned = 0;
bool ret = false;
if(0 == config->pool_count){
error(_("There are no pools specified in the configuration"));
goto end;
}
if((sycned = lm_ctx_sync(ctx, true, cmd_sync_callback, NULL) < 0)){
error(_("Failed to sync pools: %s"), lm_strerror());
goto end;
}
info(_("Synced "FG_BOLD"%d/%d"FG_RESET" pools"), sycned, config->pool_count);
ret = true;
end:
bar_free();
return ret;
}

122
src/config.c Normal file
View File

@@ -0,0 +1,122 @@
#include <errno.h>
#include <ini.h>
#include <libmp/all.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "config.h"
#include "intl.h"
#include "log.h"
bool config_set(config_t *config, char *key, char *value){
if(eq(key, "tmpdir"))
config->tmpdir = strdup(value);
else if(eq(key, "datadir"))
config->datadir = strdup(value);
else
return false;
return true;
}
pool_config_t *config_pool_add(config_t *config, char *name) {
pool_config_t *pool = malloc(sizeof(pool_config_t));
bzero(pool, sizeof(pool_config_t));
pool->name = strdup(name);
pool->next = config->pools;
config->pools = pool;
config->pool_count++;
return pool;
}
bool config_pool_contains(config_t *config, char *name) {
pool_config_t *cur = config->pools;
while (NULL != cur) {
if (eq(cur->name, name))
return true;
cur = cur->next;
}
return false;
}
int config_load_handler(void *data, const char *_section, const char *_key, const char *_value) {
char *section = (char *)_section, *key = (char *)_key, *value = (char *)_value;
config_t *config = data;
if(eq(section, "")){
if(config_set(config, key, value))
return 1;
goto unknown;
}
if (!config_pool_contains(config, section))
config_pool_add(config, section);
if (eq(key, "url")) {
free(config->pools->url);
config->pools->url = strdup(value);
}
else
goto unknown;
return 1;
unknown:
error(_("Unknown configuration option: %s/%s"), section, key);
return 0;
}
bool config_load(lm_ctx_t *ctx, config_t *config, char *file) {
bzero(config, sizeof(config_t));
if (!exists(file) || !can_read(file)) {
error(_("Failed to access the configuration file: %s"), file);
return false;
}
if (ini_parse(file, config_load_handler, config) < 0) {
error(_("Failed to parse the configuration"));
return false;
}
if(NULL == config->tmpdir){
error(_("Please specify \"tmpdir\" in the configuration"));
return false;
}
if(NULL == config->datadir){
error(_("Please specify \"datadir\" in the configuration"));
return false;
}
pool_config_t *pool = config->pools;
while (NULL != pool) {
if (NULL == pool->url) {
error(_("URL not specified for the pool: %s"), pool->name);
return false;
}
pool = pool->next;
}
return true;
}
void config_free(config_t *config) {
free(config->datadir);
free(config->tmpdir);
pool_config_t *cur = config->pools, *old = NULL;
while (NULL != cur) {
old = cur;
cur = cur->next;
free(old->name);
free(old->url);
free(old);
}
}

19
src/config.h Normal file
View File

@@ -0,0 +1,19 @@
#pragma once
#include <libmp/ctx.h>
#include <stdbool.h>
#include <stdio.h>
typedef struct pool_config {
struct pool_config *next;
char *name;
char *url;
} pool_config_t;
typedef struct config {
pool_config_t *pools;
char *datadir, *tmpdir;
ssize_t pool_count;
} config_t;
bool config_load(lm_ctx_t *ctx, config_t *config, char *file);
void config_free(config_t *config);

2
src/intl.h Normal file
View File

@@ -0,0 +1,2 @@
#include <libintl.h>
#define _(x) gettext(x)

138
src/log.c Normal file
View File

@@ -0,0 +1,138 @@
#include <sys/ioctl.h>
#include <stdbool.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include "intl.h"
#include "log.h"
void info(const char *msg, ...) {
va_list args;
va_start(args, msg);
printf(FG_BOLD FG_BLUE ">>> " FG_RESET);
vprintf(msg, args);
printf(FG_RESET "\n");
va_end(args);
}
void error(const char *msg, ...) {
va_list args;
va_start(args, msg);
printf(FG_BOLD FG_RED ">>> " FG_RESET);
vprintf(msg, args);
printf(FG_RESET "\n");
va_end(args);
}
void success(const char *msg, ...) {
va_list args;
va_start(args, msg);
printf(FG_BOLD FG_GREEN ">>> " FG_RESET);
vprintf(msg, args);
printf(FG_RESET "\n");
va_end(args);
}
void input(const char *msg, ...) {
va_list args;
va_start(args, msg);
printf(FG_BOLD FG_CYAN ">>> " FG_RESET);
vprintf(msg, args);
printf(FG_RESET);
va_end(args);
}
bool yesno(const char *msg) {
char *yes[] = {_("y"), _("Y")};
char *no[] = {_("n"), _("N")};
char question[strlen(msg) + 12], c;
sprintf(question, _("%s [y/N] "), msg);
while (true) {
input(question);
int c = getchar();
if (c == '\n')
return false;
getchar();
for (int i = 0; i < sizeof(yes) / sizeof(char *); i++) {
if (yes[i][0] == c)
return true;
}
for (int i = 0; i < sizeof(no) / sizeof(char *); i++) {
if (no[i][0] == c)
return false;
}
error(_("Please answer with y/n"));
}
}
bool bar(float cur, float max) {
if (cur <= 0 || max <= 0 || cur > max)
return false;
float per = 100 * (cur / max);
if (per > 100) {
return false;
}
struct winsize barwin = {0};
char perc[20];
ioctl(STDOUT_FILENO, TIOCGWINSZ, &barwin);
sprintf(perc, " %%%.f", per);
int size = barwin.ws_col - (3 + strlen(perc));
if (size <= 0) {
return false;
}
int prog = size * (cur / max);
if (prog > size)
return true;
printf("\e[?25l");
printf("\r" FG_BOLD "[");
for (int i = 0; i < prog; i++) {
printf("#");
}
printf(FG_BLUE "#" FG_RESET FG_BOLD);
for (int i = prog; i < size; i++) {
printf(" ");
}
printf("]" FG_RESET);
printf(FG_BOLD FG_BLUE "%s" FG_RESET "\r", perc);
return true;
}
void bar_free() {
struct winsize barwin = {0};
ioctl(STDOUT_FILENO, TIOCGWINSZ, &barwin);
printf("\r");
for (int i = 0; i < barwin.ws_col; i++)
printf(" ");
printf("\r");
printf("\e[?25h");
}

18
src/log.h Normal file
View File

@@ -0,0 +1,18 @@
#pragma once
#include <stdbool.h>
#define FG_RED "\x1b[31m"
#define FG_BOLD "\x1b[1m"
#define FG_BLUE "\x1b[34m"
#define FG_CYAN "\x1b[36m"
#define FG_GREEN "\x1b[32m"
#define FG_GRAY "\x1b[37m"
#define FG_MAGENTA "\x1b[35m"
#define FG_RESET "\x1b[0m"
void info(const char *msg, ...);
void error(const char *msg, ...);
void success(const char *msg, ...);
bool yesno(const char *msg);
bool bar(float cur, float max);
void bar_free();

137
src/main.c Normal file
View File

@@ -0,0 +1,137 @@
// clang-format off
/*
* matt | MatterLinux package manager
* MatterLinux 2023-2024 (https://matterlinux.xyz)
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
// clang-format on
#include <libmp/all.h>
#include <libmp/ctx.h>
#include <libmp/error.h>
#include <libmp/util.h>
#include <stdlib.h>
#include <stdio.h>
#include "config.h"
#include "args.h"
#include "intl.h"
#include "cmd.h"
#include "log.h"
int main(int argc, char *argv[]) {
cmd_t commands[] = {
{.name="list", .desc="list all the installed packages", .func=cmd_list},
{.name="sync", .desc="update the pool info and package lists", .func=cmd_sync},
{.name="install", .desc="install package(s) from remote pools", .func=cmd_install},
{.name="remove", .desc="remove installed package(s)", .func=NULL},
{.name="update", .desc="update installed package(s)", .func=NULL},
};
char *config_file = NULL, *root_dir = NULL;
char *full_datadir = NULL, *full_tmpdir = NULL;
args_t *args = NULL;
bool ret = false;
config_t config;
lm_ctx_t ctx;
lm_ctx_init(&ctx);
args = args_parse(argc, argv);
if((config_file = args_get_string(args, "config")) == NULL)
config_file = "/etc/matt/config.ini";
if(!config_load(&ctx, &config, config_file))
goto end;
if(args->count <= 0 || args->list[0].name != NULL)
goto help;
if((root_dir = args_get_string(args, "root")) == NULL)
root_dir = "/";
if(!lm_ctx_set_root(&ctx, root_dir)){
if(LM_ERR_CtxRootNoWrite == lm_error())
error(_("Failed to access the root directory, are you running as root?"));
else
error(_("Bad root directory (%s): %s"), root_dir, lm_strerror());
goto end;
}
full_datadir = join_alloc(root_dir, config.datadir);
full_tmpdir = join_alloc(root_dir, config.tmpdir);
if(!lm_ctx_set_data(&ctx, full_datadir)){
error(_("Bad datadir (%s): %s"), full_datadir, lm_strerror());
goto end;
}
if(!lm_ctx_set_temp(&ctx, full_tmpdir)){
error(_("Bad tmpdir (%s): %s"), full_tmpdir, lm_strerror());
goto end;
}
pool_config_t *pool = config.pools;
while (NULL != pool) {
if(!lm_ctx_pool_add(&ctx, pool->name, pool->url)){
error(_("Failed to add pool "FG_BOLD"%s"FG_RESET" to the list: %s"), pool->name, lm_strerror());
goto end;
}
pool = pool->next;
}
for(int i = 0; i < sizeof(commands)/sizeof(cmd_t); i++){
if(eq(commands[i].name, args->list[0].value)){
ret = commands[i].func(&ctx, &config, args);
goto end;
}
}
error(_("Command not found: "FG_BOLD"%s"), args->list[0].value);
help:
info(_("MatterLinux package manager (version %s)"), VERSION);
info(_("Usage: "FG_BOLD"%s [command] <options> <arguments>"), argv[0]);
printf("\n");
info(_("Here is a list of available commands:"));
for(int i = 0; i < sizeof(commands)/sizeof(cmd_t); i++)
printf(" "FG_BOLD"%s"FG_RESET":\t %s\n", commands[i].name, commands[i].desc);
printf("\n");
info(_("To list different options, use commands with "FG_BOLD"--help"FG_RESET" option"));
info(_("Here is a list of available global options:"));
printf(_(" "FG_BOLD"--config"FG_RESET":\t specify the configuration file (default is /etc/matt/config.ini)\n"));
printf(_(" "FG_BOLD"--root"FG_RESET":\t specify a custom root directory (default is /)\n\n"));
info(_("Licensed under GPLv3, see https://www.gnu.org/licenses/ for more information"));
ret = true;
goto end;
end:
free(full_datadir);
free(full_tmpdir);
args_free(args);
lm_ctx_free(&ctx);
config_free(&config);
return ret ? EXIT_SUCCESS : EXIT_FAILURE;
}

30
src/util.c Normal file
View File

@@ -0,0 +1,30 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "util.h"
bool startswith(char *str, char *sub){
size_t strl = strlen(str);
size_t subl = strlen(sub);
if(subl > strl)
return false;
return strncmp(sub, str, subl) == 0;
}
void size_to_human(char *buf, long size) {
char *suffix[] = {"B", "KB", "MB", "GB", "TB"};
char length = sizeof(suffix) / sizeof(suffix[0]);
int i = 0;
double cursize = size;
if (size > 1024) {
for (i = 0; (size / 1024) > 0 && i < length - 1; i++, size /= 1024)
cursize = size / 1024.0;
}
sprintf(buf, "%.02lf%s", cursize, suffix[i]);
}

7
src/util.h Normal file
View File

@@ -0,0 +1,7 @@
#pragma once
#include <stdbool.h>
#define LONGSTR_MAX 30
bool startswith(char *str, char *sub);
void size_to_human(char *buf, long size);