gout

A static git page generator
git clone https://git.bracken.jp/gout.git
Log | Files | Refs | README | LICENSE

fs_inmemory.c (5208B)


      1 #include "fs_inmemory.h"
      2 
      3 #include <stdio.h>
      4 #include <stdlib.h>
      5 #include <string.h>
      6 #include <sys/stat.h>
      7 #include <unistd.h>
      8 
      9 #include "utils.h"
     10 
     11 typedef struct {
     12   char* path;
     13   char* buf;
     14   size_t size;
     15 } InMemoryFile;
     16 
     17 #define MAX_MOCK_FILES 1024
     18 static InMemoryFile g_files[MAX_MOCK_FILES];
     19 static size_t g_files_count = 0;
     20 
     21 static FILE* g_active_streams[MAX_MOCK_FILES];
     22 static size_t g_active_streams_count = 0;
     23 
     24 static void register_active_stream(FILE* f) {
     25   if (g_active_streams_count < MAX_MOCK_FILES) {
     26     g_active_streams[g_active_streams_count++] = f;
     27   }
     28 }
     29 
     30 static int inmemory_mkdir(const char* path, mode_t mode) {
     31   (void)path;
     32   (void)mode;
     33   return 0;
     34 }
     35 
     36 static int inmemory_mkdirp(const char* path) {
     37   (void)path;
     38   return 0;
     39 }
     40 
     41 static FILE* inmemory_fopen(const char* path, const char* mode) {
     42   if (strcmp(mode, "w") == 0) {
     43     if (g_files_count >= MAX_MOCK_FILES) {
     44       return NULL;
     45     }
     46     // Register a new file.
     47     InMemoryFile* file = &g_files[g_files_count++];
     48     file->path = estrdup(path);
     49     file->buf = NULL;
     50     file->size = 0;
     51     FILE* f = open_memstream(&file->buf, &file->size);
     52     register_active_stream(f);
     53     return f;
     54   }
     55   if (strcmp(mode, "r") == 0) {
     56     for (size_t i = 0; i < g_files_count; i++) {
     57       if (strcmp(g_files[i].path, path) == 0) {
     58         if (g_files[i].buf) {
     59           FILE* f = fmemopen(g_files[i].buf, strlen(g_files[i].buf), "r");
     60           if (!f) {
     61             return NULL;
     62           }
     63           register_active_stream(f);
     64           return f;
     65         }
     66       }
     67     }
     68   }
     69   return NULL;
     70 }
     71 
     72 static int inmemory_fclose(FILE* stream) {
     73   for (size_t i = 0; i < g_active_streams_count; i++) {
     74     if (g_active_streams[i] == stream) {
     75       // Remove from active streams by shifting the rest down.
     76       if (i < g_active_streams_count - 1) {
     77         memmove(&g_active_streams[i], &g_active_streams[i + 1],
     78                 (g_active_streams_count - i - 1) * sizeof(FILE*));
     79       }
     80       g_active_streams_count--;
     81       return fclose(stream);
     82     }
     83   }
     84   return fclose(stream);  // Fallback for untracked streams.
     85 }
     86 
     87 const char* inmemory_fs_get_buffer(const char* path) {
     88   // We MUST flush all active streams to ensure buffers are up to date.
     89   for (size_t i = 0; i < g_active_streams_count; i++) {
     90     fflush(g_active_streams[i]);
     91   }
     92   for (size_t i = 0; i < g_files_count; i++) {
     93     if (strcmp(g_files[i].path, path) == 0) {
     94       return g_files[i].buf;
     95     }
     96   }
     97   return NULL;
     98 }
     99 
    100 void inmemory_fs_clear(void) {
    101   for (size_t i = 0; i < g_active_streams_count; i++) {
    102     fclose(g_active_streams[i]);
    103   }
    104   g_active_streams_count = 0;
    105 
    106   for (size_t i = 0; i < g_files_count; i++) {
    107     free(g_files[i].path);
    108     free(g_files[i].buf);
    109     g_files[i].path = NULL;
    110     g_files[i].buf = NULL;
    111     g_files[i].size = 0;
    112   }
    113   g_files_count = 0;
    114 }
    115 
    116 static int inmemory_mkstemp(char* template) {
    117   // We MUST return a real file descriptor because the code calls fdopen().
    118   return mkstemp(template);
    119 }
    120 
    121 static FILE* inmemory_fdopen(int fd, const char* mode) {
    122   return fdopen(fd, mode);
    123 }
    124 
    125 static int inmemory_close(int fd) {
    126   return close(fd);
    127 }
    128 
    129 static int inmemory_rename(const char* oldpath, const char* newpath) {
    130   // If oldpath is a real file on disk (from mkstemp), we read it into our
    131   // in-memory registry and then delete the disk file.
    132   if (access(oldpath, F_OK) == 0) {
    133     FILE* f = fopen(oldpath, "r");
    134     if (f) {
    135       fseek(f, 0, SEEK_END);
    136       long fsize = ftell(f);
    137       fseek(f, 0, SEEK_SET);
    138 
    139       char* content = malloc(fsize + 1);
    140       fread(content, 1, fsize, f);
    141       content[fsize] = '\0';
    142       fclose(f);
    143 
    144       // Register/Update the path in our in-memory map.
    145       bool found = false;
    146       for (size_t i = 0; i < g_files_count; i++) {
    147         if (strcmp(g_files[i].path, newpath) == 0) {
    148           free(g_files[i].buf);
    149           g_files[i].buf = content;
    150           g_files[i].size = fsize;
    151           found = true;
    152           break;
    153         }
    154       }
    155       if (!found) {
    156         if (g_files_count < MAX_MOCK_FILES) {
    157           InMemoryFile* file = &g_files[g_files_count++];
    158           file->path = estrdup(newpath);
    159           file->buf = content;
    160           file->size = fsize;
    161         }
    162       }
    163       unlink(oldpath);
    164       return 0;
    165     }
    166   }
    167   return 0;
    168 }
    169 
    170 static int inmemory_chmod(const char* path, mode_t mode) {
    171   (void)path;
    172   (void)mode;
    173   return 0;
    174 }
    175 
    176 static int inmemory_access(const char* path, int amode) {
    177   (void)amode;
    178   for (size_t i = 0; i < g_files_count; i++) {
    179     if (strcmp(g_files[i].path, path) == 0) {
    180       return 0;
    181     }
    182   }
    183   return -1;
    184 }
    185 
    186 static char* inmemory_realpath(const char* path, char* resolved_path) {
    187   if (resolved_path) {
    188     strcpy(resolved_path, path);
    189     return resolved_path;
    190   }
    191   return strdup(path);
    192 }
    193 
    194 static const FileSystem kStdInmemoryFs = {
    195 
    196     .mkdir = inmemory_mkdir,
    197     .mkdirp = inmemory_mkdirp,
    198     .fopen = inmemory_fopen,
    199     .fdopen = inmemory_fdopen,
    200     .fclose = inmemory_fclose,
    201     .close = inmemory_close,
    202     .mkstemp = inmemory_mkstemp,
    203     .rename = inmemory_rename,
    204     .chmod = inmemory_chmod,
    205     .access = inmemory_access,
    206     .realpath = inmemory_realpath,
    207 };
    208 
    209 const FileSystem* g_fs_inmemory = &kStdInmemoryFs;