/* 
 * This software is provided with no warranty  whatsoever. You may use, modify
 * and redistribute it as long as you give a credit to the original author and
 * provide a link to the original source.
 *
 * $Id: util.c,v 1.3 2025/07/16 21:12:49 alx Exp $
 */

#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <math.h>
#include <errno.h>
#include "util.h"

char* get_size_string(unsigned long size, char buffer[SIZE_CS_MAX])
{
	char CS_BYTES[] = "B";
	char CS_KILO[] = "K";
	char CS_MEGA[] = "M";
	char CS_GIGA[] = "G";

	const double kilo = 1024;
	double fsize = size;
	char *sz_units = CS_BYTES;
	double dp;
	char *fmt;

	
	if(size >= pow(kilo, 3)) {
		fsize /=  pow(kilo, 3);
		sz_units = CS_GIGA;
	}else if(size >=  pow(kilo, 2)) {
		fsize /= pow(kilo, 2);
		sz_units = CS_MEGA;
	} else if(size >= kilo) {
		fsize /= kilo;
		sz_units = CS_KILO;
	}

	/* don't show decimal part if it's near .0 */
	dp = fsize - trunc(fsize);
	if(dp > 0.1 && dp < 0.9)
		fmt = "%.1f%s";
	else
		fmt = "%.0f%s";

	snprintf(buffer, SIZE_CS_MAX, fmt, fsize, sz_units);
	return buffer;
}

/*
 * Returns ls style mode string for the mode specified
 */
char* get_mode_string(mode_t mode, char buf[MODE_CS_MAX])
{
	switch((mode & S_IFMT)) {
		case S_IFSOCK: buf[0] = 's'; break;
		case S_IFLNK: buf[0] = 'l'; break;
		case S_IFDIR: buf[0] = 'd'; break;
		case S_IFBLK: buf[0] = 'b'; break;
		case S_IFCHR: buf[0] = 'c'; break;
		case S_IFIFO: buf[0] = 'p'; break;
		default: buf[0] = '-'; break;
	};
	
	buf[1] = (mode & S_IRUSR) ? 'r' : '-';
	buf[2] = (mode & S_IWUSR) ? 'w' : '-';
	buf[3] = (mode & (S_ISUID|S_ISGID)) ?
		((mode & S_IXUSR) ? 'S' : 's') : ((mode & S_IXUSR) ? 'x' : '-');
	
	buf[4] = (mode & S_IRGRP) ? 'r' : '-';
	buf[5] = (mode & S_IWGRP) ? 'w' : '-';
	buf[6] = (mode & S_IXGRP) ? 'x' : '-';
	
	buf[7] = (mode & S_IROTH) ? 'r' : '-';
	buf[8] = (mode & S_IWOTH) ? 'w' : '-';
	buf[9] = (mode & S_ISVTX) ?
		((mode & S_IXOTH) ? 'T' : 't') : ((mode & S_IXOTH) ? 'x' : '-');

	buf[10] = '\0';
	
	return buf;
}


/* Same as getcwd but allocates a buffer */
char* get_working_dir(void)
{
	char *cwd = NULL;
	char *buffer = NULL;
	size_t size = 0;	
	
	do {
		char *p;
		size += 256;
		p = realloc(buffer, size + 1);
		if(!p) {
			if(buffer) free(buffer);
			return NULL;
		}
		buffer = p;
		
		cwd = getcwd(buffer, size);
	}while(!cwd && (errno == ERANGE));
	
	if(!cwd && buffer) free(buffer);
	
	return cwd;
}

/*
 * Runs an executable file in a separate process with arguments specified.
 * Returns zero on success, errno otherwise.
 */
int spawn_command(const char *cmd, char * const *args, size_t nargs)
{
	pid_t pid;
	volatile int errval = 0;
	char **argv;
	size_t i = 0;
	
	argv = malloc( sizeof(char*) * (nargs + 2) );
	if(!argv) return errno;
	
	for(i = 0; i < nargs; i++) {
		argv[i + 1] = args[i];
	}	
	argv[0] = (char*)cmd;
	argv[nargs + 1] = NULL;
	
	pid = vfork();
	if(pid == 0) {
		setsid();
		
		if(execvp(cmd, argv) == (-1))
			errval = errno;
		_exit(0);
	} else if(pid == -1) {
		errval = errno;
	}

	free(argv);
	return errval;
}
