This commit is contained in:
kento2 2026-09-09 18:41:42 +02:00
commit aae369b9c8
9 changed files with 334 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build/

32
extension.c Normal file
View file

@ -0,0 +1,32 @@
#include <magic.h>
#include <libowfat/errmsg.h>
#include <libowfat/byte.h>
#include <libowfat/str.h>
#include "pipeshare.h"
#define MAX(a,b) (a>b?a:b)
#define failmagic(...) do { \
carp(__VA_ARGS__,": ",magic_error(magic)); \
return 0; \
} while(0)
int extension(int64 fd,char result[16])
{
magic_t magic=magic_open(MAGIC_EXTENSION);
if(!magic) failmagic("initialize libmagic");
if(magic_load(magic,NULL)<0) failmagic("load magic database");
const char *extensions=magic_descriptor(magic,fd);
if(!extensions) failmagic("get magic data");
size_t l=MAX(str_chr(extensions,'/'),16);
byte_copy(result,l,extensions);
result[l]=0;
magic_close(magic);
if(str_equal(result,"???"))
str_copy(result,"txt");
return 1;
}
#undef failmagic

29
meson.build Normal file
View file

@ -0,0 +1,29 @@
project(
'pipeshare', 'c',
default_options: [
'c_std=c99',
'warning_level=2',
],
)
cc = meson.get_compiler('c')
deps = [
cc.find_library('libowfat'),
dependency('libmagic'),
]
executable('pipeshare', files(
'./pipeshare.c',
'./send_stdin.c',
'./extension.c',
), dependencies:deps, install:true)
executable('pipeshare-clean', files(
'./pipeshare-clean.c',
), dependencies:deps, install:true)
install_man([
'./pipeshare.1',
'./pipeshare-clean.1',
])

23
pipeshare-clean.1 Normal file
View file

@ -0,0 +1,23 @@
.TH pipeshare 1
.SH NAME
pipeshare-clean \- remove old files
.SH SYNOPSIS
pipeshare-clean [-C dir]
.SH OPTIONS
\-C dir
chdir into dir at startup
.SH DESCRIPTION
if \-C is given, pipeshare-clean cd's into the specified
directory.
It then walks through all files that do not have a file
extension(i.e. do not contain a dot in their name) and for
each whose last chagne is older than 14 days, prompts the
user whether to delete it or not. If it is marked for
deletion, the file along with any other file starting with
the same prefix(id) are deleted.
.PP
The confirmation prompt is used to avoid accidental deletion
of files when pipeshare-clean is used from the wrong working
directory.
.SH "SEE ALSO"
pipeshare(1)

89
pipeshare-clean.c Normal file
View file

@ -0,0 +1,89 @@
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <libowfat/errmsg.h>
#include <libowfat/array.h>
#include <limits.h>
#include <libowfat/str.h>
#include <libowfat/buffer.h>
#include <time.h>
#define dieusage() die(100,argv0," [-C dir]")
#define dienoarg(OPT) \
do { \
carp("missing argument for -"OPT); \
dieusage(); \
} while(0)
static time_t now,expiration;
static int prompt_confirm(char *file)
{
char c;
buffer_puts(buffer_1,"remove ");
buffer_puts(buffer_1,file);
buffer_putsflush(buffer_1,"? [y/N]");
if (read(0,&c,1)<=0) return 0;
return c=='y' || c=='Y';
}
static void delete_shared(char *id)
{
DIR *dir;
struct dirent *ent;
dir=opendir(".");
if (!dir) diesys(111,"opendir");
while ((ent=readdir(dir)))
if (str_start(ent->d_name,id))
unlink(ent->d_name);
closedir(dir);
}
static void run(void)
{
DIR *dir;
struct dirent *ent;
struct stat st;
dir=opendir(".");
if (!dir) diesys(111,"opendir");
while ((ent=readdir(dir))) {
if (str_chr(ent->d_name,'.')!=str_len(ent->d_name))
continue;
if (stat(ent->d_name,&st)<0)
diesys(111,"stat ",ent->d_name);
/* ignore alias */
if (now-st.st_mtime > expiration) {
if (!prompt_confirm(ent->d_name))
continue;
buffer_putsflush(buffer_1,"\n");
delete_shared(ent->d_name);
}
}
}
int main(int argc, char **argv)
{
int i;
char *dir=".";
errmsg_iam(*argv);
for (i=1;i<argc;i++) {
if (argv[i][0]!='-') break;
if (argv[i][1]=='-' && !argv[i][2]) break;
switch (argv[i][1]) {
case 'C':
if(!argv[++i]) dienoarg("C");
dir=argv[i];
break;
default: dieusage();
}
}
argc-=i; argv+=i;
if (chdir(dir)<0)
diesys(100,"chdir ",dir);
time(&now);
/* 14 days */
expiration = 14*60*60*24;
run();
}

24
pipeshare.1 Normal file
View file

@ -0,0 +1,24 @@
.TH pipeshare 1
.SH NAME
pipeshare \- share files
.SH SYNOPSIS
pipeshare [-C dir] < FILE
.SH OPTIONS
\-C dir
chdir into dir at startup
.SH DESCRIPTION
if \-C is given, pipeshare cd's into the specified
directory.
It chooses a unique filename in the directory(called the id)
and copys stdin into that files. Then it creates an
"alias"-file that reuses the same filename but with an added
extension. The extension is determined based on the MIME
type of the file.
.PP
To delete old files, pipeshare-clean(1) may be used.
.SH ENVIRONMENT
WWWBASE
base url to print. e.g. https://example.org will make
pipeshare print https://example.org/r4nd0m.txt
.SH "SEE ALSO"
pipeshare-clean(1)

95
pipeshare.c Normal file
View file

@ -0,0 +1,95 @@
#define _XOPEN_SOURCE 500
#include <libowfat/errmsg.h>
#include <libowfat/str.h>
#include <libowfat/buffer.h>
#include <unistd.h>
#include <alloca.h>
#include <libgen.h>
#include "pipeshare.h"
#define dieusage() die(100,argv0," [-C dir]")
#define dienoarg(OPT) \
do { \
carp("missing argument for -"OPT); \
dieusage(); \
} while(0)
static char *wwwbase=NULL;
static int mkaliasfile(char *id,char ext[16])
{
char *alias;
size_t l;
alias=alloca(str_len(id)+17);
l=str_copy(alias,id);
alias[l++]='.';
l+=str_copy(alias+l,ext);
alias[l]=0;
if(symlink(id,alias)<0) {
carpsys("symlink ",id," -> ",alias);
return 0;
}
return 1;
}
int main(int argc, char **argv)
{
int i;
char *dir=".";
char tmp[]="./XXXXXX";
int tmpfd;
char ext[16];
errmsg_iam(*argv);
for (i=1;i<argc;i++) {
if (argv[i][0]!='-') break;
if (argv[i][1]=='-' && !argv[i][2]) break;
switch (argv[i][1]) {
case 'C':
if (!argv[++i]) dienoarg("C");
dir=argv[i];
break;
default: dieusage();
}
}
argc-=i; argv+=i;
if (argc!=0) dieusage();
wwwbase=getenv("WWWBASE");
if (!wwwbase) die(100,"WWWBASE not set");
if (chdir(dir)<0)
diesys(111,"chdir ",dir);
tmpfd=mkstemp(tmp);
if(tmpfd<0) diesys(111, "mkstemp in ", argv[0]);
if(!io_fd(tmpfd)
||!io_fd(0)
) {
unlink(tmp);
diesys(111,"io_fd");
}
if(!send_stdin(tmpfd)) {
unlink(tmp);
die(111,"copy stdin to ",tmp);
}
lseek(tmpfd,0,SEEK_SET);
if(!extension(tmpfd,ext)) {
carp("determine extension");
return 0;
}
if(!mkaliasfile(tmp,ext)) {
unlink(tmp);
die(111,"create alias with extension");
}
buffer_puts(buffer_1,wwwbase);
buffer_puts(buffer_1,"/");
buffer_puts(buffer_1,basename(tmp));
buffer_puts(buffer_1,".");
buffer_puts(buffer_1,ext);
buffer_putsflush(buffer_1,"\n");
}

10
pipeshare.h Normal file
View file

@ -0,0 +1,10 @@
#ifndef PIPESHARE_H
#define PIPESHARE_H
#include <libowfat/io.h>
int send_stdin(int64 fd);
int extension(int64 fd,char result[16]);
#endif

31
send_stdin.c Normal file
View file

@ -0,0 +1,31 @@
#include <libowfat/errmsg.h>
#include "pipeshare.h"
int send_stdin(int64 fd)
{
char buf[8192];
int r;
uint64 readpos=0,writepos=0;
int eof=0;
for(;;){
if(eof && writepos>=readpos)break;
if(!eof && readpos < sizeof buf) {
r=io_tryread(0,buf+readpos,(sizeof buf)-readpos);
if(r<=-2){carpsys("read stdin");return 0;}
if(r==0)eof=1;
if(r>0)readpos+=r;
}
if(writepos<readpos){
r=io_trywrite(fd,buf+writepos,readpos-writepos);
if(r<=-2){carpsys("write");return 0;}
if(r>0){
writepos+=r;
if(writepos==readpos)
readpos=writepos=0;
}
}
}
return 1;
}