dep

Package manager for embedded C libraries
git clone git://git.finwo.net/app/dep
Log | Files | Refs | README | LICENSE

main.c (9993B)


      1 #include <dirent.h>
      2 #include <limits.h>
      3 #include <stdio.h>
      4 #include <stdlib.h>
      5 #include <string.h>
      6 #include <sys/stat.h>
      7 #include <unistd.h>
      8 
      9 #include "../command.h"
     10 #include "cofyc/argparse.h"
     11 #include "common/fs-utils.h"
     12 
     13 static int cmd_repository_list(int argc, const char **argv);
     14 static int cmd_repository_add(int argc, const char **argv);
     15 static int cmd_repository_remove(int argc, const char **argv);
     16 static int cmd_repository_clean_cache(int argc, const char **argv);
     17 
     18 static const char *const usages[] = {
     19     "repository <subcommand> [options]",
     20     NULL,
     21 };
     22 
     23 static int cmd_repository(int argc, const char **argv) {
     24   struct argparse_option options[] = {
     25       OPT_HELP(),
     26       OPT_END(),
     27   };
     28   struct argparse argparse;
     29   argparse_init(&argparse, options, usages, 0);
     30   argc = argparse_parse(&argparse, argc, argv);
     31 
     32   // If no subcommand provided, show available subcommands
     33   if (argc < 1) {
     34     printf("Available subcommands:\n");
     35     printf("  list          List the names of the repositories\n");
     36     printf("  add           Add a repository: add <name> <url>\n");
     37     printf("  remove        Remove a repository: remove <name>\n");
     38     printf("  clean-cache   Remove all cached manifest files\n");
     39     return 0;
     40   }
     41 
     42   // Dispatch to the appropriate subcommand handler
     43   if (!strcmp(argv[0], "list")) {
     44     return cmd_repository_list(argc - 1, argv + 1);
     45   } else if (!strcmp(argv[0], "add")) {
     46     return cmd_repository_add(argc - 1, argv + 1);
     47   } else if (!strcmp(argv[0], "remove")) {
     48     return cmd_repository_remove(argc - 1, argv + 1);
     49   } else if (!strcmp(argv[0], "clean-cache")) {
     50     return cmd_repository_clean_cache(argc - 1, argv + 1);
     51   } else {
     52     fprintf(stderr, "Error: unknown subcommand '%s'\n", argv[0]);
     53     return 1;
     54   }
     55 }
     56 
     57 // List all repository names from files in the repository directory
     58 // Files are parsed line by line, with '#' starting comments
     59 static int cmd_repository_list(int argc, const char **argv) {
     60   (void)argc;
     61   (void)argv;
     62 
     63   char *repo_dir = get_repo_dir();
     64   if (!repo_dir) {
     65     return 1;
     66   }
     67   DIR *dir = opendir(repo_dir);
     68   free(repo_dir);
     69   if (!dir) {
     70     fprintf(stderr, "Error: could not open repository directory\n");
     71     return 1;
     72   }
     73 
     74   struct dirent *entry;
     75   // Iterate through all files in the repository directory
     76   while ((entry = readdir(dir)) != NULL) {
     77     // Skip . and .. entries
     78     if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) continue;
     79 
     80     struct stat st;
     81     char       *repo_dir2 = get_repo_dir();
     82     if (!repo_dir2) {
     83       closedir(dir);
     84       return 1;
     85     }
     86     char filepath[PATH_MAX];
     87     snprintf(filepath, sizeof(filepath), "%s%s", repo_dir2, entry->d_name);
     88     free(repo_dir2);
     89     // Skip non-regular files
     90     if (stat(filepath, &st) < 0 || !S_ISREG(st.st_mode)) continue;
     91 
     92     FILE *f = fopen(filepath, "r");
     93     if (!f) continue;
     94 
     95     // Parse each line in the file
     96     char line[LINE_MAX];
     97     while (fgets(line, sizeof(line), f)) {
     98       // Remove trailing newline
     99       line[strcspn(line, "\n")] = '\0';
    100       // Strip comments (everything after '#')
    101       char *comment = strchr(line, '#');
    102       if (comment) *comment = '\0';
    103       // Extract the first word as the repository name
    104       char *name = strtok(line, " \t");
    105       if (name && name[0] != '\0') {
    106         printf("%s\n", name);
    107       }
    108     }
    109     fclose(f);
    110   }
    111 
    112   closedir(dir);
    113   return 0;
    114 }
    115 
    116 // Add a repository entry to the 00-managed file
    117 // Format: <name> <url>
    118 static int cmd_repository_add(int argc, const char **argv) {
    119   if (argc < 2) {
    120     fprintf(stderr, "Error: add requires <name> and <url>\n");
    121     fprintf(stderr, "Usage: repository add <name> <url>\n");
    122     return 1;
    123   }
    124 
    125   const char *name = argv[0];
    126   const char *url  = argv[1];
    127 
    128   // Build the file path: ~/.config/finwo/dep/repositories.d/00-managed
    129   char *repo_dir = get_repo_dir();
    130   if (!repo_dir) {
    131     return 1;
    132   }
    133   char filepath[PATH_MAX];
    134   snprintf(filepath, sizeof(filepath), "%s00-managed", repo_dir);
    135   free(repo_dir);
    136 
    137   // Open the file in append mode
    138   FILE *f = fopen(filepath, "a");
    139   if (!f) {
    140     fprintf(stderr, "Error: could not open file '%s' for writing\n", filepath);
    141     return 1;
    142   }
    143 
    144   // Write the repository entry: name url
    145   fprintf(f, "%s %s\n", name, url);
    146   fclose(f);
    147 
    148   printf("Repository '%s' added.\n", name);
    149   return 0;
    150 }
    151 
    152 // Remove a repository entry by name from all files in the repository directory
    153 // If a file becomes empty after removal, it is deleted
    154 static int cmd_repository_remove(int argc, const char **argv) {
    155   if (argc < 1) {
    156     fprintf(stderr, "Error: remove requires <name>\n");
    157     fprintf(stderr, "Usage: repository remove <name>\n");
    158     return 1;
    159   }
    160 
    161   const char *name  = argv[0];
    162   int         found = 0;
    163 
    164   char *repo_dir = get_repo_dir();
    165   if (!repo_dir) {
    166     return 1;
    167   }
    168   DIR *dir = opendir(repo_dir);
    169   free(repo_dir);
    170   if (!dir) {
    171     fprintf(stderr, "Error: could not open repository directory\n");
    172     return 1;
    173   }
    174 
    175   struct dirent *entry;
    176   // Iterate through all files in the repository directory
    177   while ((entry = readdir(dir)) != NULL) {
    178     // Skip . and .. entries
    179     if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) continue;
    180 
    181     char *repo_dir2 = get_repo_dir();
    182     if (!repo_dir2) {
    183       closedir(dir);
    184       return 1;
    185     }
    186     char filepath[PATH_MAX];
    187     snprintf(filepath, sizeof(filepath), "%s%s", repo_dir2, entry->d_name);
    188     free(repo_dir2);
    189 
    190     struct stat st;
    191     // Skip non-regular files
    192     if (stat(filepath, &st) < 0 || !S_ISREG(st.st_mode)) continue;
    193 
    194     // Create a temporary file for writing the modified content
    195     char temp_path[PATH_MAX];
    196     snprintf(temp_path, sizeof(temp_path), "%s.tmp", filepath);
    197 
    198     FILE *in  = fopen(filepath, "r");
    199     FILE *out = fopen(temp_path, "w");
    200     if (!in || !out) {
    201       fprintf(stderr, "Error: could not open files for processing '%s'\n", filepath);
    202       if (in) fclose(in);
    203       if (out) fclose(out);
    204       closedir(dir);
    205       return 1;
    206     }
    207 
    208     // Process each line, removing lines that match the repository name
    209     char line[LINE_MAX];
    210     while (fgets(line, sizeof(line), in)) {
    211       // Make a copy for parsing (preserving original line for output)
    212       char line_copy[LINE_MAX];
    213       strncpy(line_copy, line, sizeof(line_copy) - 1);
    214       line_copy[sizeof(line_copy) - 1] = '\0';
    215 
    216       // Strip comments before matching
    217       line_copy[strcspn(line_copy, "\n")] = '\0';
    218       char *comment                       = strchr(line_copy, '#');
    219       if (comment) *comment = '\0';
    220       char *line_name = strtok(line_copy, " \t");
    221 
    222       // Skip lines that match the repository name
    223       if (line_name && !strcmp(line_name, name)) {
    224         found = 1;
    225         continue;
    226       }
    227       // Write the original line back to the temp file
    228       fputs(line, out);
    229     }
    230 
    231     fclose(in);
    232     fclose(out);
    233 
    234     // Replace original file with the temporary file
    235     if (rename(temp_path, filepath) < 0) {
    236       fprintf(stderr, "Error: could not replace file '%s'\n", filepath);
    237       closedir(dir);
    238       return 1;
    239     }
    240 
    241     // If the file is now empty, remove it
    242     if (stat(filepath, &st) == 0 && st.st_size == 0) {
    243       if (remove(filepath) < 0) {
    244         fprintf(stderr, "Error: could not remove empty file '%s'\n", filepath);
    245         closedir(dir);
    246         return 1;
    247       }
    248     }
    249   }
    250 
    251   closedir(dir);
    252 
    253   if (!found) {
    254     fprintf(stderr, "Warning: repository '%s' not found\n", name);
    255   } else {
    256     printf("Repository '%s' removed.\n", name);
    257   }
    258 
    259   return 0;
    260 }
    261 
    262 static int cmd_repository_clean_cache(int argc, const char **argv) {
    263   (void)argc;
    264   (void)argv;
    265 
    266   char *cache_dir = get_cache_dir();
    267   if (!cache_dir) {
    268     return 1;
    269   }
    270 
    271   DIR *dir = opendir(cache_dir);
    272   if (!dir) {
    273     free(cache_dir);
    274     return 0;
    275   }
    276 
    277   struct dirent *entry;
    278   int            removed = 0;
    279 
    280   while ((entry = readdir(dir)) != NULL) {
    281     if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) continue;
    282 
    283     char filepath[PATH_MAX];
    284     snprintf(filepath, sizeof(filepath), "%s%s", cache_dir, entry->d_name);
    285 
    286     struct stat st;
    287     if (stat(filepath, &st) < 0 || !S_ISREG(st.st_mode)) continue;
    288 
    289     if (remove(filepath) == 0) {
    290       removed++;
    291     }
    292   }
    293 
    294   closedir(dir);
    295   free(cache_dir);
    296 
    297   printf("Removed %d cached manifest file%s.\n", removed, removed == 1 ? "" : "s");
    298   return 0;
    299 }
    300 
    301 // Register the repository command with the command system
    302 void __attribute__((constructor)) cmd_repository_setup(void) {
    303   struct cmd_struct *cmd = calloc(1, sizeof(struct cmd_struct));
    304   if (!cmd) {
    305     fprintf(stderr, "Failed to allocate memory for repository command\n");
    306     return;
    307   }
    308   cmd->next                             = commands;
    309   cmd->fn                               = cmd_repository;
    310   static const char *repository_names[] = {"repository", "repo", "r", NULL};
    311   cmd->name                             = repository_names;
    312   cmd->display                          = "r(epo(sitory))";
    313   cmd->description                      = "Repository management";
    314   cmd->help_text =
    315       "dep repository - Repository management\n"
    316       "\n"
    317       "Usage:\n"
    318       "  dep repository list\n"
    319       "  dep repository add <name> <url>\n"
    320       "  dep repository remove <name>\n"
    321       "  dep repository clean-cache\n"
    322       "\n"
    323       "Description:\n"
    324       "  dep can use custom repositories to discover packages. Repositories are\n"
    325       "  configured in ~/.config/finwo/dep/repositories.d/.\n"
    326       "\n"
    327       "Subcommands:\n"
    328       "  list          List the names of the configured repositories\n"
    329       "  add           Add a repository: dep repository add <name> <url>\n"
    330       "  remove        Remove a repository: dep repository remove <name>\n"
    331       "  clean-cache   Remove all cached manifest files\n"
    332       "\n"
    333       "Examples:\n"
    334       "  dep repository add myorg https://example.com/path/to/manifest\n"
    335       "  dep repository list\n"
    336       "  dep repository remove myorg\n"
    337       "  dep repository clean-cache\n";
    338   commands = cmd;
    339 }