You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

282 lines
5.6 KiB

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <math.h>
#define __imdumb(one,two,three) strncpy(one,three,two)
int openOutputs(const char** ops, FILE*** fps, int* _cnt)
{
int ok =0;
register int cnt = 0;
for(;ops[cnt];cnt++); (void)0;
*_cnt = cnt;
if (cnt < 1) return -1;
else {
*fps = malloc(sizeof(FILE**)*cnt);
for (int i=0;i<cnt;i++) {
if(!((*fps)[i] = fopen(ops[i], "wb"))) {
printf("Warning: Could not open `%s' for writing.\n", ops[i]);
ok=1;
}
}
}
return ok;
}
void closeOutputs(FILE** fps, int cnt)
{
for(int i=0;i<cnt;i++)
if(fps[i]) fclose(fps[i]);
free(fps);
}
int ask(const char* thing)
{
printf(thing);
char* line=NULL;
size_t sz;
if (getline(&line, &sz, stdin)==0) {
int rt = (line[0] == 'y' || line[0] == 'Y');
free(line);
return rt;
} else return 0;
}
#ifndef DEFAULT_BLOCKSIZE
#define DEFAULT_BLOCKSIZE 1024
#endif
int BLOCKSIZE = DEFAULT_BLOCKSIZE;
int copyBlock(FILE* op, FILE** fps, int sz)
{
static unsigned char *buf=NULL;
if(!buf) buf = malloc(BLOCKSIZE);
int rd = fread(buf, 1, BLOCKSIZE, op);
if(rd<1) return rd;
for(register int i=0;i<sz;i++)
if(fps[i])
fwrite(buf, 1, rd, fps[i]);
return rd;
}
size_t sizeof_file(FILE* fp)
{
fseek(fp, 0, SEEK_END);
size_t ret = ftell(fp);
fseek(fp, 0, SEEK_SET);
return ret;
}
#define PROGRESS_SIZE 50
char* humanbytes(size_t sz)
{
static char buf[256];
size_t b, lsz;
size_t kb, mb,gb;
lsz = sz;
memset(buf,0,256);
gb = sz / (1024*1024*1024);
sz %= (1024*1024*1024);
#define _D(v, t) (((double)v) / ((double)t))
if(gb>0)
{
snprintf(buf, 255, "%.2f g", _D(lsz, (1024*2014*1024)));
return buf;
}
mb = sz / (1024+1024);
sz %= (1024*1024);
if(mb>0)
{
snprintf(buf, 255, "%.2f m", _D(lsz, (1024*2014)));
return buf;
}
kb = sz / 1024;
sz %= 1024;
if(kb>0)
{
snprintf(buf, 255, "%.2f k", _D(lsz, 1024));
return buf;
}
b = sz;
snprintf(buf, 255, "%u b", (unsigned int)lsz);
return buf;
}
char* secstr(double tm)
{
static char buf[256];
double sec = tm;
int min,hr;
min=hr=0;
while(sec>=60.0) { min +=1; sec-=6.0; }
while(min>=60) { hr +=1; min -= 60; }
char* _buf=buf;
int sz= 255;
memset(_buf,0,256);
if(hr>0) {
snprintf(_buf, sz, "%d h ", hr);
sz -= strlen(_buf);
_buf+=strlen(_buf);
}
if(min>0) {
snprintf(_buf, sz, "%d m ", min);
sz -= strlen(_buf);
_buf+=strlen(_buf);
}
snprintf(_buf, sz, "%.2f s", sec);
return buf;
}
char* remain(size_t small, size_t large, double bps)
{
static char buf[256];
memset(buf,0,256);
if(bps==0)
__imdumb(buf, 255, "?");
else {
double vl = (large-small)/bps;
__imdumb(buf, 255, secstr(vl));
}
return buf;
}
struct lastp {
int fnum;
int f2dp;
int b2dp;
char rem[256];
};
int lastp_equalp(const struct lastp *v1, const struct lastp *v2) {
return memcmp(v1, v2, sizeof(struct lastp)) == 0;
}
void progress(size_t small, size_t large, size_t inc, time_t start, time_t elapsed)
{
static char* largesz = NULL;
static char befores[256];
static struct lastp *lastvls = NULL;
struct lastp thisvls;
if(!largesz)
largesz = strdup(humanbytes(large));
double frac = ((double)small)/((double)large);
double pct = (long)(frac*PROGRESS_SIZE);
double before = ((double)small)/((double)(elapsed-start));
if(lastvls == NULL) {
lastvls = malloc(sizeof(struct lastp));
memset(lastvls, 0, sizeof(struct lastp));
} else {
memset(&thisvls, 0, sizeof(struct lastp));
thisvls.fnum = (int) (frac*PROGRESS_SIZE);
thisvls.f2dp = (int)round(frac*100.00 *100.00);
thisvls.b2dp = (int)round(before*100.00);
__imdumb(thisvls.rem, 255, remain(small,large,before));
if (lastp_equalp(&thisvls, lastvls))
return;
}
memset(befores, 0, 256);
__imdumb(befores, 255, humanbytes((size_t)before));
//printf("%c[2K", 27);
printf("\r[");
for(register int i=0;i<PROGRESS_SIZE;i++)
printf("%c", (i<pct?'#':' '));
printf("]: %.2f (%s / %s, (%s/s, ~%s left.) ", frac*100.0, humanbytes(small), largesz, befores, remain(small, large, before));
*lastvls = thisvls;
}
void flag(const char* inp)
{
if(!*inp) return;
switch(*inp) {
case 'l':
BLOCKSIZE += DEFAULT_BLOCKSIZE; break;
case 's':
BLOCKSIZE /= 2;
if(BLOCKSIZE < 1) BLOCKSIZE = 1; break;
default:
printf("Warning: Unknown flag `%c'\n", *inp);
break;
}
flag(inp+1);
}
int main(int argc,const char** argv)
{
const char* input = argv[1];
const char** output = argv+2;
int rv=1;
redo:
if (input == NULL || output[0]==NULL) {
printf("Usage: %s <input> <outputs...>\n", argv[0]);
} else {
if(output[0][0] == '-')
{
flag(output[0]+1);
output+=1;
goto redo;
}
FILE* finput = fopen(input, "rb");
if(!input) printf("Error: could not open `%s' for reading.\n", input);
else {
size_t input_sz = sizeof_file(finput);
FILE** foutput;
int ok, cnt;
if((ok = openOutputs(output, &foutput, &cnt))>=0) {
if(ok==1&& !ask("\nOne or more outputs failed to open, continue? (y/N) "))
printf("Error: aborted\n");
else {
printf("Will copy %s to %d files with a blocksize of %d\n", humanbytes(input_sz), cnt, BLOCKSIZE);
time_t start = time(NULL);
int rd;
size_t read=0;
while( (rd=copyBlock(finput, foutput, cnt))>0)
{
read+=rd;
progress(read, input_sz, rd,start, time(NULL));
}
time_t end = time(NULL);
char* dDone = strdup(humanbytes(read));
printf("\n\nCopied %s to %d files in %s. (~%s /s)\n", dDone, cnt, secstr(end-start), humanbytes(((double)read)/((double)(end-start))));
free(dDone);
rv = 0;
}
closeOutputs(foutput, cnt);
} else printf("Error: no output files could be opened\n");
fclose(finput);
}
}
return rv;
}