thalassa/cms/fileops.cpp
2026-03-19 06:23:52 +05:00

213 lines
4.8 KiB
C++

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <libgen.h>
#include <scriptpp/scrvar.hpp>
#include <scriptpp/scrvect.hpp>
#include <scriptpp/cmd.hpp>
#include "fileops.hpp"
static void normalize_path(ScriptVector &path)
{
bool absolute = (path[0] == "");
int i = 0;
while(i < path.Length()) {
if(path[i] == "" || path[i] == ".") { // things like "_//_", "_/./_"
path.Remove(i);
continue;
}
if(path[i] == ".." && i>0 && path[i-1]!=".." && path[i-1]!= "") {
path.Remove(i-1, 2);
i--;
continue;
}
i++;
}
if(absolute && path[0] != "")
path.Insert(0, "");
else
if(!absolute && path[0] == "")
path.Remove(0);
}
ScriptVariable short_link_path(ScriptVariable src, ScriptVariable dst)
{
if(src[0] == '/' && dst[0] != '/') {
ScriptVariable cwd;
scriptpp_getcwd(cwd);
dst = cwd + "/" + dst;
} else
if(src[0] != '/' && dst[0] == '/') {
ScriptVariable cwd;
scriptpp_getcwd(cwd);
src = cwd + "/" + src;
}
// now either both are absolute, or both are relative
ScriptVector srcv(src, "/", "");
ScriptVector dstv(dst, "/", "");
normalize_path(srcv);
normalize_path(dstv);
int minlen = srcv.Length();
int n = dstv.Length();
if(minlen > n)
minlen = n;
int i;
for(i = 0; i < minlen; i++)
if(srcv[i] != dstv[i])
break;
int steps_up = dstv.Length() - i - 1;
if(steps_up < 0) // this means trying to link into our own path
return ScriptVariableInv(); // like ln -s /a/b/c/d /a/b
if(i > 0)
srcv.Remove(0, i);
for(i = 0; i < steps_up; i++)
srcv.Insert(0, "..");
return srcv.Join("/");
}
// please note that make_directory_path is a plain C function,
// plz don't break this so that it can be reused in other projects
int make_directory_path(const char *path, int skip_the_last)
{
char *pc, *dr;
int r;
if(!skip_the_last) {
struct stat st;
r = stat(path, &st);
if(r != -1) {
if(S_ISDIR(st.st_mode)) {
return 0; // okay
} else {
errno = ENOTDIR;
return -1;
}
}
/* stat() failed... why? */
if(errno != ENOENT)
return -1;
}
/* okay, it really doesn't exist */
pc = strdup(path);
dr = strdup(dirname(pc));
/* the check for "." eliminates a stupid call to stat */
r = (strcmp(".", dr) == 0) ? 0 : make_directory_path(dr, 0);
free(dr);
free(pc);
if(r == -1)
return -1;
r = skip_the_last ? 0 : mkdir(path, 0777);
return r;
}
int file_copy(const char *src, const char *dst, int mode)
{
static char buf[4096];
int rc;
int fs, fd;
fs = open(src, O_RDONLY);
if(fs == -1)
return -1;
fd = open(dst, O_WRONLY|O_TRUNC|O_CREAT, mode ? mode : 0666);
if(fd == -1) {
int e = errno;
close(fs);
errno = e;
return -1;
}
if(mode)
fchmod(fd, mode);
while((rc = read(fs, buf, sizeof(buf))) > 0)
write(fd, buf, rc);
close(fd);
close(fs);
return 0;
}
int make_link(const char *src, const char *dst)
{
int res = link(src, dst);
if(res == -1 && errno == EEXIST) {
unlink(dst);
res = link(src, dst);
}
return res;
}
static int symlink_with_retry(const char *src, const char *dst)
{
//printf("SYMLINK: %s %s\n\n", src, dst);
int res = symlink(src, dst);
if(res == -1 && errno == EEXIST) {
res = unlink(dst);
if(res == -1)
return -1;
res = symlink(src, dst);
}
return res;
}
int make_symlink(const ScriptVariable &src, const ScriptVariable &dst)
{
//printf("SRC/DST: %s %s\n", src.c_str(), dst.c_str());
ScriptVariable shortpath = short_link_path(src, dst);
//printf("SHORTPATH: %s\n", shortpath.c_str());
int r = make_directory_path(dst.c_str(), 1);
if(r == -1)
return -1;
return symlink_with_retry(shortpath.c_str(), dst.c_str());
}
int preserve_symlink(const ScriptVariable &src, const ScriptVariable &dst)
{
ScriptVariable lnk;
scriptpp_readlink(src, lnk);
if(lnk.IsInvalid())
return -1;
return symlink_with_retry(lnk.c_str(), dst.c_str());
}
#ifdef LINKPATH_TEST
#include <stdio.h>
int main(int argc, char **argv)
{
if(argc < 3) {
fprintf(stderr, "specify source and destination path\n");
return 1;
}
ScriptVariable a(argv[1]), b(argv[2]);
ScriptVariable c = short_link_path(a, b);
if(c.IsValid())
printf("%s\n", c.c_str());
else
fprintf(stderr, "can not link at that position!\n");
return 0;
}
#endif