Adds package.c and package.h. Adds support for the `--pack` argument for packing a project directory into an exe or tar file.
208 lines
4.6 KiB
C
208 lines
4.6 KiB
C
/**
|
|
* Copyright (c) 2016 rxi
|
|
*
|
|
* This library is free software; you can redistribute it and/or modify it
|
|
* under the terms of the MIT license. See LICENSE for details.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <dirent.h>
|
|
#include <stdarg.h>
|
|
|
|
#include "lib/microtar/microtar.h"
|
|
|
|
#include "package.h"
|
|
|
|
|
|
static void error(const char *fmt, ...) {
|
|
va_list argp;
|
|
printf("Package error: ");
|
|
va_start(argp, fmt);
|
|
vprintf(fmt, argp);
|
|
va_end(argp);
|
|
printf("\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
|
|
static int tar_stream_write(mtar_t *tar, const void *data, unsigned size) {
|
|
unsigned res = fwrite(data, 1, size, tar->stream);
|
|
if (res != size) {
|
|
error("failed when writing to output file");
|
|
}
|
|
return MTAR_ESUCCESS;
|
|
}
|
|
|
|
|
|
static void concat(char *dst, int dstsz, ...) {
|
|
const char *s;
|
|
va_list argp;
|
|
int i = 0;
|
|
va_start(argp, dstsz);
|
|
while ( (s = va_arg(argp, const char*)) ) {
|
|
while (*s) {
|
|
dst[i++] = *s++;
|
|
if (i == dstsz) {
|
|
error("string length exceeds destination buffer");
|
|
}
|
|
}
|
|
}
|
|
dst[i] = '\0';
|
|
va_end(argp);
|
|
}
|
|
|
|
|
|
static
|
|
void concat_path(char *dst, int dstsz, const char *dir, const char *filename) {
|
|
int dirlen = strlen(dir);
|
|
if ( dir[dirlen - 1] == '/' || *dir == '\0' ) {
|
|
concat(dst, dstsz, dir, filename, NULL);
|
|
} else {
|
|
concat(dst, dstsz, dir, "/", filename, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
static void write_file(mtar_t *tar, const char *inname, const char *outname) {
|
|
FILE *fp = fopen(inname, "rb");
|
|
if (!fp) {
|
|
error("couldn't open input file '%s'", inname);
|
|
}
|
|
|
|
/* Get size */
|
|
fseek(fp, 0, SEEK_END);
|
|
int size = ftell(fp);
|
|
fseek(fp, 0, SEEK_SET);
|
|
|
|
/* Write header */
|
|
mtar_header_t h;
|
|
memset(&h, 0, sizeof(h));
|
|
concat(h.name, sizeof(h.name), outname, NULL);
|
|
h.size = size;
|
|
h.mode = 0664;
|
|
mtar_write_header(tar, &h);
|
|
|
|
/* Write file */
|
|
int chr;
|
|
while ( (chr = fgetc(fp)) != EOF ) {
|
|
unsigned char byte = chr;
|
|
mtar_write_data(tar, &byte, 1);
|
|
}
|
|
|
|
/* Close file and return ok */
|
|
fclose(fp);
|
|
}
|
|
|
|
|
|
static void write_dir(mtar_t *tar, const char *indir, const char *outdir) {
|
|
char inbuf[256];
|
|
char outbuf[256];
|
|
struct dirent *ep;
|
|
|
|
DIR *dir = opendir(indir);
|
|
if (!dir) {
|
|
error("couldn't open input dir '%s'", indir);
|
|
}
|
|
|
|
/* Write dir */
|
|
mtar_header_t h;
|
|
memset(&h, 0, sizeof(h));
|
|
concat(h.name, sizeof(h.name), outdir, NULL);
|
|
h.type = MTAR_TDIR;
|
|
h.mode = 0664;
|
|
mtar_write_header(tar, &h);
|
|
|
|
/* Write files */
|
|
while ( (ep = readdir(dir)) ) {
|
|
/* Skip `.` and `..` */
|
|
if (!strcmp(ep->d_name, ".") || !strcmp(ep->d_name, "..")) {
|
|
continue;
|
|
}
|
|
/* Get full input name and full output name */
|
|
concat_path(inbuf, sizeof(inbuf), indir, ep->d_name);
|
|
concat_path(outbuf, sizeof(outbuf), outdir, ep->d_name);
|
|
/* Write */
|
|
DIR *d = opendir(inbuf);
|
|
if (d) {
|
|
closedir(d);
|
|
write_dir(tar, inbuf, outbuf);
|
|
} else {
|
|
write_file(tar, inbuf, outbuf);
|
|
}
|
|
}
|
|
|
|
closedir(dir);
|
|
}
|
|
|
|
|
|
void package_make(const char *indir, const char *outfile, const char *exefile, int type) {
|
|
/* Open file */
|
|
FILE *fp = fopen(outfile, "wb");
|
|
if (!fp) {
|
|
error("couldn't open output file");
|
|
}
|
|
|
|
/* Copy .exe to file if exe type is set */
|
|
if (type == PACKAGE_TEXE) {
|
|
FILE *exefp = fopen(exefile, "rb");
|
|
if (!exefp) {
|
|
error("couldn't open .exe file");
|
|
}
|
|
int chr;
|
|
while ( (chr = fgetc(exefp)) != EOF ) {
|
|
fputc(chr, fp);
|
|
}
|
|
fclose(exefp);
|
|
}
|
|
|
|
/* Get start of file (used for offset) */
|
|
int start = ftell(fp);
|
|
|
|
/* Init tar writer */
|
|
mtar_t tar;
|
|
memset(&tar, 0, sizeof(tar));
|
|
tar.write = tar_stream_write;
|
|
tar.stream = fp;
|
|
|
|
/* Write package data to file and finalize tar */
|
|
write_dir(&tar, indir, "");
|
|
mtar_finalize(&tar);
|
|
|
|
/* Write "TAR\0" and offset int so we can find the .tar if it was appended
|
|
* onto the end of the .exe */
|
|
int offset = ftell(fp) - start + 8;
|
|
fwrite("TAR\0", 1, 4, fp);
|
|
fwrite(&offset, 1, 4, fp);
|
|
|
|
/* Done */
|
|
fclose(fp);
|
|
}
|
|
|
|
|
|
int package_run(int argc, char **argv) {
|
|
/* Check for `--pack` argument; return failure if it isn't present */
|
|
if (argc < 2) {
|
|
return PACKAGE_EFAILURE;
|
|
}
|
|
if ( strcmp(argv[1], "--pack") != 0 && strcmp(argv[1], "--PACK") != 0 ) {
|
|
return PACKAGE_EFAILURE;
|
|
}
|
|
|
|
/* Check arguments */
|
|
if (argc < 4) {
|
|
error("expected arguments: %s DIRNAME OUTFILE", argv[1]);
|
|
}
|
|
|
|
/* Set package type based on file extension */
|
|
int type = PACKAGE_TTAR;
|
|
if ( strstr(argv[3], ".exe") || strstr(argv[3], ".EXE") ) {
|
|
type = PACKAGE_TEXE;
|
|
}
|
|
|
|
/* Make package and return success*/
|
|
package_make(argv[2], argv[3], argv[0], type);
|
|
return PACKAGE_ESUCCESS;
|
|
}
|