2025-06-03 09:11:48 +00:00
// Simple package manager.
// Only needs stdint.h, stdlib.h, stdio.h, string.h & mkdir()
// May be extended with optional extras (checking timestamps etc.) later.
// This is NEW CODE written by Zak Yani Star Fenton, public domain (UNLICENSE license)
# ifndef PKG_MKDIR_SIMPLE
# define PKG_MKDIR_LINUX
# endif
2025-06-08 18:09:03 +10:00
# ifndef PKG_CHMOD_SIMPLE
# define PKG_CHMOD_LINUX
# endif
2025-06-03 09:11:48 +00:00
// For reliable integer sizing
# include <stdint.h>
// For malloc, free
# include <stdlib.h>
// For FILE, fopen, fread, fwrite, fclose, printf, fprintf, stdout, stderr
# include <stdio.h>
// For strcmp, strdup
# include <string.h>
// For mkdir
# ifdef PKG_MKDIR_LINUX
# include <sys/stat.h>
# else
# ifdef PKG_MKDIR_SIMPLE
int mkdir ( const char * dirname ) ;
# else
# error Either (-D) PKG_MKDIR_LINUX or PKG_MKDIR_SIMPLE must be defined
# endif
# endif
2025-06-08 18:09:03 +10:00
// For chmod
# ifdef PKG_CHMOD_LINUX
# include <sys/stat.h>
# else
# ifdef PKG_CHMOD_NONE
# else
# error Either (-D) PKG_CHMOD_LINUX or PKG_CHMOD_SIMPLE must be defined
# endif
# endif
2025-06-03 09:11:48 +00:00
// The compressed buffer needs to be a bit over twice size the regular
// buffer to account for worst case scenario.
# define PKG_BUFFERSIZE 4096
# define PKG_COMPRBUFSIZE 8224
# define PKG_JOBTYPE_BUILD 1
# define PKG_JOBTYPE_INSTALL 2
# define PKG_JOBTYPE_REMOVE 3
# define PKG_JOBTYPE_SUM 4
# define PKG_JOBTYPE_COMPRESS 5
# define PKG_JOBTYPE_DECOMPRESS 6
2025-06-08 18:09:03 +10:00
# define PKG_FLAGS_TYPEMASK 0xF00
2025-06-03 09:11:48 +00:00
# define PKG_FLAGS_DIRECTORY 0x100
# define PKG_FLAGS_EXECUTABLE 0x200
# define PKG_FLAGS_PROVIDES 0x300
# define PKG_FLAGS_REQUIRES 0x400
# define PKG_FLAGS_SUGGESTS 0x500
# define PKG_FLAGS_CONFLICTS 0x600
typedef struct pkg_job pkg_job_t ;
typedef struct pkg_project pkg_project_t ;
typedef struct pkg_sum pkg_sum_t ;
typedef struct pkg_archive pkg_archive_t ;
typedef struct pkg_version pkg_version_t ;
typedef struct pkg_versionmeta pkg_versionmeta_t ;
typedef long long pkg_size_t ;
typedef unsigned long long pkg_usize_t ;
struct pkg_job {
char * * names ;
char * target ;
char * installroot ;
char * dbroot ;
int usedb ;
int nnames ;
int jobtype ;
int verbose ;
} ;
struct pkg_project {
char * projdir ;
char * projfile ;
} ;
struct pkg_sum {
char * name ;
pkg_sum_t * next ;
pkg_usize_t offset ;
pkg_usize_t cmprsize ; // Compressed/in-package size
pkg_usize_t extrsize ; // Extracted/real-file size
pkg_usize_t checksum64 ;
unsigned int flags ; // This field is set to zero normally
unsigned int checksum32 ;
unsigned int nameoffset ;
unsigned int namelength ;
unsigned int namechecksum ;
} ;
struct pkg_archive {
int version ;
int nheaders ;
pkg_sum_t * headerlist ;
} ;
struct pkg_versionmeta {
const char * value ;
pkg_versionmeta_t * next ;
} ;
struct pkg_version {
int major ;
int minor ;
int patch ;
pkg_versionmeta_t * prerelease ;
pkg_versionmeta_t * meta ;
} ;
// Buffer used for most reads/writes, could be dynamically allocated later.
char pkg_linebuffer [ PKG_BUFFERSIZE ] ;
char pkg_comprbuffer [ PKG_COMPRBUFSIZE ] ;
int pkg_mkdir ( const char * path ) {
# ifdef PKG_MKDIR_LINUX
2025-06-08 18:09:03 +10:00
return mkdir ( path , 0755 ) ; // NOTE: Was previously using 0775, figured more secure by default is better?
2025-06-03 09:11:48 +00:00
# endif
# ifdef PKG_MKDIR_SIMPLE
return mkdir ( path ) ;
# endif
}
int pkg_remove ( const char * path , int isdir ) {
return unlink ( path ) ;
}
2025-06-08 18:09:03 +10:00
int pkg_chmod_default ( const char * path ) {
# ifdef PKG_CHMOD_LINUX
return chmod ( path , 0644 ) ;
# else
return 0 ;
# endif
}
int pkg_chmod_executable ( const char * path ) {
# ifdef PKG_CHMOD_LINUX
return chmod ( path , 0755 ) ;
# else
fprintf ( stderr , " NOTE: Ignoring executable bit for '%s' \n " , path ) ;
return 0 ;
# endif
}
2025-06-03 09:11:48 +00:00
int pkg_versionmeta_alldigits ( pkg_versionmeta_t * m ) {
const char * s = m - > value ;
while ( * s ) {
if ( * s < ' 0 ' | | * s > ' 9 ' ) {
return 0 ;
}
s + + ;
}
return 1 ;
}
int pkg_versionmeta_compare ( pkg_versionmeta_t * left , pkg_versionmeta_t * right ) {
int ldigits = pkg_versionmeta_alldigits ( left ) ;
int rdigits = pkg_versionmeta_alldigits ( right ) ;
if ( ldigits & & ! rdigits ) {
return - 1 ;
} else if ( rdigits & & ! ldigits ) {
return 1 ;
} else if ( ! rdigits & & ! ldigits ) {
return strcmp ( left - > value , right - > value ) ;
} else {
const char * l = left - > value ;
const char * r = right - > value ;
while ( * l = = ' 0 ' ) {
l + + ;
}
while ( * r = = ' 0 ' ) {
r + + ;
}
size_t llen = strlen ( l ) ;
size_t rlen = strlen ( r ) ;
if ( llen < rlen ) {
return - 1 ;
} else if ( llen > rlen ) {
return 1 ;
} else {
return strcmp ( l , r ) ; // ASCII comparison will do for numbers too if same number of digits
}
}
}
// Compares versions, returns 0 if equal, -1 if left < right or 1 if
// left > right. Treats NULLs as lower-than any real version.
int pkg_version_compare ( pkg_version_t * left , pkg_version_t * right ) {
// A NULL is just treated as less-than any real version.
if ( left = = NULL & & right = = NULL ) {
return 0 ;
} else if ( left = = NULL ) {
return - 1 ;
} else if ( right = = NULL ) {
return 1 ;
}
if ( left - > major < right - > major ) {
return - 1 ;
} else if ( left - > major > right - > major ) {
return 1 ;
}
if ( left - > minor < right - > minor ) {
return - 1 ;
} else if ( left - > minor > right - > minor ) {
return 1 ;
}
if ( left - > patch < right - > patch ) {
return - 1 ;
} else if ( left - > patch > right - > patch ) {
return 1 ;
}
if ( left - > prerelease ! = NULL & & right - > prerelease = = NULL ) {
return - 1 ;
} else if ( left - > prerelease = = NULL & & right - > prerelease ! = NULL ) {
return 1 ;
} else if ( left - > prerelease = = NULL & & right - > prerelease = = NULL ) {
return 0 ;
}
pkg_versionmeta_t * lpre = left - > prerelease ;
pkg_versionmeta_t * rpre = right - > prerelease ;
while ( lpre ! = NULL & & rpre ! = NULL ) {
lpre = lpre - > next ;
rpre = rpre - > next ;
}
if ( rpre ! = NULL ) {
return - 1 ;
} else if ( lpre ! = NULL ) {
return 1 ;
} else {
return 0 ;
}
}
int pkg_version_parseint ( int * resultvar , const char * string ) {
int i = 0 ;
int result = 0 ;
while ( string [ i ] > = ' 0 ' & & string [ i ] < = ' 9 ' ) {
result * = 10 ;
result + = ( string [ i ] - ' 0 ' ) ;
i + + ;
}
* resultvar = result ;
return i ;
}
int pkg_version_isalnum ( char foo ) {
if ( foo > = ' 0 ' & & foo < = ' 9 ' ) {
return 1 ;
} else if ( foo > = ' a ' & & foo < = ' z ' ) {
return 1 ;
} else if ( foo > = ' A ' & & foo < = ' Z ' ) {
return 1 ;
} else if ( foo = = ' - ' ) {
// Hyphens are treated as alnum in version meta
return 1 ;
} else {
return 0 ;
}
}
int pkg_versionmeta_parse1 ( pkg_versionmeta_t * * resultvar , const char * string ) {
int i = 0 ;
while ( pkg_version_isalnum ( string [ i ] ) ) {
i + + ;
}
if ( i > 0 ) {
pkg_versionmeta_t * newmeta = malloc ( sizeof ( pkg_versionmeta_t ) ) ;
if ( newmeta = = NULL ) {
return - 1 ;
}
newmeta - > value = strndup ( string , i ) ;
newmeta - > next = NULL ;
* resultvar = newmeta ;
}
return i ;
}
int pkg_versionmeta_parse ( pkg_versionmeta_t * * resultvar , const char * string ) {
int i = 0 ;
pkg_versionmeta_t * firstmeta = NULL ;
pkg_versionmeta_t * oldmeta = NULL ;
do {
pkg_versionmeta_t * newmeta ;
int tmp = pkg_versionmeta_parse1 ( & newmeta , string + i ) ;
if ( tmp < 1 ) {
return - 1 ;
}
i + = tmp ;
if ( oldmeta = = NULL ) {
firstmeta = newmeta ;
} else {
oldmeta - > next = newmeta ;
}
oldmeta = newmeta ;
} while ( string [ i + + ] = = ' . ' ) ;
* resultvar = firstmeta ;
return i ;
}
// Parses a version string, returns a positive integer indicating the
// number of chars parsed on success and <1 if no version string was
// detected.
int pkg_version_parse ( pkg_version_t * version , const char * string ) {
int i = 0 ;
while ( string [ i ] = = ' ' ) {
i + + ;
}
int tmp = pkg_version_parseint ( & version - > major , string + i ) ;
if ( tmp < 1 ) {
return - 1 ;
}
i + = tmp ;
if ( string [ i ] ! = ' . ' ) {
return - 1 ;
}
i + + ;
tmp = pkg_version_parseint ( & version - > minor , string + i ) ;
if ( tmp < 1 ) {
return - 1 ;
}
i + = tmp ;
if ( string [ i ] ! = ' . ' ) {
return - 1 ;
}
i + + ;
tmp = pkg_version_parseint ( & version - > patch , string ) ;
if ( tmp < 1 ) {
return - 1 ;
}
i + = tmp ;
if ( string [ i ] = = ' - ' ) {
i + + ;
tmp = pkg_versionmeta_parse ( & version - > prerelease , string + i ) ;
if ( tmp < 1 ) {
return - 1 ;
}
i + = tmp ;
} else {
version - > prerelease = NULL ;
}
if ( string [ i ] = = ' + ' ) {
i + + ;
tmp = pkg_versionmeta_parse ( & version - > meta , string + i ) ;
if ( tmp < 1 ) {
return - 1 ;
}
i + = tmp ;
} else {
version - > meta = NULL ;
}
return i ;
}
size_t pkg_firstlongestmatch ( unsigned char * data , size_t dlength , unsigned char * key , size_t klength , size_t * indexvar ) {
if ( klength < 1 ) {
* indexvar = - 1 ;
return 0 ;
}
size_t mlength = 0 ;
size_t i = 0 ;
while ( i < dlength ) {
size_t j = 0 ;
while ( j < klength & & i + j < dlength & & data [ i + j ] = = key [ j ] ) {
j + + ;
}
if ( j > mlength ) {
mlength = j ;
* indexvar = i ;
if ( mlength = = klength ) {
return mlength ;
}
}
i + = j ? j : 1 ;
}
return mlength ;
}
# define PKG_COMPRESS_OUTBYTE(b) if (outi < olength) {output[outi] = (unsigned char) (b);} outi++
# define PKG_COMPRESS_FLUSHUNCOMPRESSED() if (unflushed) { \
PKG_COMPRESS_OUTBYTE ( ( unsigned char ) unflushed ) ; \
for ( size_t flushi = 0 ; flushi < unflushed ; flushi + + ) { \
PKG_COMPRESS_OUTBYTE ( input [ ( i - unflushed ) + flushi ] ) ; \
} \
unflushed = 0 ; \
}
size_t pkg_compressv1 ( unsigned char * input , size_t ilength , unsigned char * output , size_t olength ) {
if ( ilength > 64 * 1024 ) {
return - 1 ;
}
size_t i = 0 ;
size_t outi = 0 ;
size_t unflushed = 0 ;
while ( i < ilength ) {
size_t mindex = - 1 ;
size_t mlength = pkg_firstlongestmatch ( input , i , input + i , ilength - i > 32 ? 32 : ilength - i , & mindex ) ;
/*if (mlength > 2 && mindex < 256) {
PKG_COMPRESS_FLUSHUNCOMPRESSED ( ) ;
PKG_COMPRESS_OUTBYTE ( 0 - ( mlength + 32 ) ) ;
PKG_COMPRESS_OUTBYTE ( mindex ) ;
i + = mlength ;
} else */ if ( mlength > 3 ) {
PKG_COMPRESS_FLUSHUNCOMPRESSED ( ) ;
PKG_COMPRESS_OUTBYTE ( 0 - mlength ) ;
PKG_COMPRESS_OUTBYTE ( mindex ) ;
PKG_COMPRESS_OUTBYTE ( mindex > > 8 ) ;
i + = mlength ;
} else { // TODO: This will need to be changed to write multiple uncompressed bytes!
if ( unflushed > = 100 ) {
PKG_COMPRESS_FLUSHUNCOMPRESSED ( ) ;
}
//PKG_COMPRESS_OUTBYTE(1);
//PKG_COMPRESS_OUTBYTE(input[i]);
/*if (unflushed == 0 && input[i] >= 'a' && input[i] <= 'z') {
PKG_COMPRESS_OUTBYTE ( 0 - ( 64 + ( input [ i ] - ' a ' ) ) ) ;
} else if ( unflushed = = 0 & & input [ i ] > = ' A ' & & input [ i ] < = ' Z ' ) {
PKG_COMPRESS_OUTBYTE ( 0 - ( 64 + 26 + ( input [ i ] - ' A ' ) ) ) ;
} else if ( unflushed = = 0 & & input [ i ] = = 0 ) {
PKG_COMPRESS_OUTBYTE ( 0 - ( 64 + 26 * 2 + 0 ) ) ;
} else if ( unflushed = = 0 & & input [ i ] = = 1 ) {
PKG_COMPRESS_OUTBYTE ( 0 - ( 64 + 26 * 2 + 1 ) ) ;
} else if ( unflushed = = 0 & & input [ i ] = = 2 ) {
PKG_COMPRESS_OUTBYTE ( 0 - ( 64 + 26 * 2 + 2 ) ) ;
} else if ( unflushed = = 0 & & input [ i ] = = 0xFF ) {
PKG_COMPRESS_OUTBYTE ( 0 - ( 64 + 26 * 2 + 3 ) ) ;
} else if ( unflushed = = 0 & & input [ i ] = = ' ' ) {
PKG_COMPRESS_OUTBYTE ( 0 - ( 64 + 26 * 2 + 4 ) ) ;
} else if ( unflushed = = 0 & & input [ i ] = = ' . ' ) {
PKG_COMPRESS_OUTBYTE ( 0 - ( 64 + 26 * 2 + 5 ) ) ;
} else if ( unflushed = = 0 & & input [ i ] = = ' , ' ) {
PKG_COMPRESS_OUTBYTE ( 0 - ( 64 + 26 * 2 + 6 ) ) ;
} else if ( unflushed = = 0 & & input [ i ] = = ' ( ' ) {
PKG_COMPRESS_OUTBYTE ( 0 - ( 64 + 26 * 2 + 7 ) ) ;
} else if ( unflushed = = 0 & & input [ i ] = = ' ) ' ) {
PKG_COMPRESS_OUTBYTE ( 0 - ( 64 + 26 * 2 + 8 ) ) ;
} else if ( unflushed = = 0 & & input [ i ] = = ' \n ' ) {
PKG_COMPRESS_OUTBYTE ( 0 - ( 64 + 26 * 2 + 9 ) ) ;
} else if ( unflushed = = 0 & & input [ i ] = = ' / ' ) {
PKG_COMPRESS_OUTBYTE ( 0 - ( 64 + 26 * 2 + 10 ) ) ;
} else if ( unflushed = = 0 & & input [ i ] = = ' ; ' ) {
PKG_COMPRESS_OUTBYTE ( 0 - ( 64 + 26 * 2 + 11 ) ) ;
} else { */
unflushed + + ;
/*}*/
i + + ;
}
}
PKG_COMPRESS_FLUSHUNCOMPRESSED ( ) ;
return outi ;
}
size_t pkg_decompressv1 ( unsigned char * input , size_t ilength , unsigned char * output , size_t olength ) {
if ( ilength > 129 * 1024 ) {
return - 1 ;
}
size_t i = 0 ;
size_t outi = 0 ;
while ( i < ilength ) {
char b = ( char ) input [ i ] ;
if ( b < 0 ) {
if ( i + 2 < ilength ) {
size_t len = 0 - b ;
size_t idx = ( ( ( size_t ) input [ i + 1 ] ) & 0xFF ) | ( ( ( ( size_t ) input [ i + 2 ] ) & 0xFF ) < < 8 ) ;
i + = 3 ;
if ( idx + len > outi ) {
return - 1 ;
}
memcpy ( output + outi , output + idx , len ) ;
outi + = len ;
} else {
return - 1 ;
}
} else {
if ( i + b > = ilength ) {
return - 1 ;
}
memcpy ( output + outi , input + i + 1 , b ) ;
i + = 1 + b ;
outi + = b ;
}
}
return outi ;
}
char * pkg_ezstrcat ( const char * a , const char * b ) {
int la = strlen ( a ) ;
int lb = strlen ( b ) ;
char * output = malloc ( la + lb + 1 ) ;
if ( output = = NULL ) {
fprintf ( stderr , " MEMORY ERROR: Failed to allocate memory for string concatenation! \n " ) ;
return NULL ;
}
int i ;
for ( i = 0 ; i < la ; i + + ) {
output [ i ] = a [ i ] ;
}
for ( ; i < la + lb ; i + + ) {
output [ i ] = b [ i - la ] ;
}
output [ i ] = 0 ;
return output ;
}
uint32_t pkg_cstringhash32 ( const char * cstring ) {
uint32_t result = 0x811C9DC5U ;
uint32_t chud = 0x01000193U ;
while ( * cstring ) {
result ^ = ( ( uint32_t ) * cstring + + ) & 0xFFU ;
result * = chud ;
}
return result ;
}
int pkg_sum_init ( pkg_sum_t * sum , char * filename , int dupfilename ) {
if ( sum = = NULL ) {
return - 1 ;
}
2025-06-08 18:09:03 +10:00
sum - > flags = 0 ;
2025-06-03 09:11:48 +00:00
sum - > cmprsize = 0 ;
sum - > extrsize = 0 ;
sum - > checksum64 = 0x811C9DC5BADC0DE5ULL ;
sum - > checksum32 = 0x811C9DC5U ;
sum - > next = NULL ;
if ( dupfilename ) {
sum - > name = strdup ( filename ) ;
if ( sum - > name = = NULL ) {
return - 1 ;
}
} else {
sum - > name = filename ;
}
return 0 ;
}
int pkg_sum_append ( pkg_sum_t * sum , void * data , pkg_usize_t size ) {
unsigned char * bytes = data ;
if ( sum = = NULL ) {
return - 1 ;
}
if ( size = = 0 ) {
return 0 ;
}
if ( data = = NULL ) {
return - 1 ;
}
pkg_usize_t chud64 = 0x0100019301000193ULL ;
unsigned int chud32 = 0x01000193U ;
for ( pkg_usize_t i = 0 ; i < size ; i + + ) {
sum - > checksum64 ^ = ( ( pkg_usize_t ) bytes [ i ] ) & 0xFFULL ;
sum - > checksum64 * = chud64 ;
sum - > checksum32 ^ = ( ( unsigned int ) bytes [ i ] ) & 0xFFU ;
sum - > checksum32 * = chud32 ;
}
sum - > extrsize + = size ;
return 0 ;
}
pkg_sum_t * pkg_archive_addheader ( pkg_archive_t * archive , const char * name ) {
if ( archive = = NULL ) {
return NULL ;
}
pkg_sum_t * newheader = malloc ( sizeof ( pkg_sum_t ) ) ;
if ( newheader = = NULL ) {
return NULL ;
}
if ( pkg_sum_init ( newheader , name , 1 ) ! = 0 ) {
free ( newheader ) ;
return NULL ;
}
pkg_sum_t * lastheader = archive - > headerlist ;
while ( lastheader ! = NULL & & lastheader - > next ! = NULL ) {
lastheader = lastheader - > next ;
}
if ( lastheader = = NULL ) {
archive - > headerlist = newheader ;
} else {
lastheader - > next = newheader ;
}
return newheader ;
}
int pkg_archive_init ( pkg_archive_t * archive ) {
if ( archive = = NULL ) {
return - 1 ;
}
archive - > version = 0 ;
archive - > headerlist = NULL ;
return 0 ;
}
size_t pkg_writefzeros ( size_t nzeros , FILE * f ) {
char zero = 0 ;
size_t nwritten = 0 ;
while ( nwritten < nzeros & & fwrite ( & zero , 1 , 1 , f ) = = 1 ) {
nwritten + + ;
}
return nwritten ;
}
int pkg_archive_writef32 ( pkg_archive_t * archive , unsigned int value , FILE * output , pkg_sum_t * sum ) {
unsigned char bytes [ 4 ] ;
bytes [ 0 ] = ( unsigned char ) value ;
bytes [ 1 ] = ( unsigned char ) ( value > > 8 ) ;
bytes [ 2 ] = ( unsigned char ) ( value > > 16 ) ;
bytes [ 3 ] = ( unsigned char ) ( value > > 24 ) ;
int nwritten = ( int ) fwrite ( bytes , 1 , 4 , output ) ;
if ( sum ! = NULL ) {
pkg_sum_append ( sum , bytes , nwritten ) ;
}
return nwritten ;
}
int pkg_archive_writef64 ( pkg_archive_t * archive , pkg_usize_t value , FILE * output , pkg_sum_t * sum ) {
int nwritten = pkg_archive_writef32 ( archive , ( unsigned int ) value , output , sum ) ;
if ( nwritten ! = 4 ) {
return nwritten ;
}
nwritten + = pkg_archive_writef32 ( archive , ( unsigned int ) ( value > > 32 ) , output , sum ) ;
return nwritten ;
}
int pkg_archive_readf32 ( pkg_archive_t * archive , unsigned int * valuevar , FILE * input , pkg_sum_t * sum ) {
unsigned char bytes [ 4 ] ;
int nread = ( int ) fread ( bytes , 1 , 4 , input ) ;
if ( nread ! = 4 ) {
fprintf ( stderr , " WARNING: fread of 4 bytes read %d bytes! \n " , nread ) ;
}
* valuevar = bytes [ 0 ] | ( ( unsigned int ) bytes [ 1 ] ) < < 8 | ( ( unsigned int ) bytes [ 2 ] ) < < 16 | ( ( unsigned int ) bytes [ 3 ] ) < < 24 ;
if ( sum ! = NULL ) {
pkg_sum_append ( sum , bytes , nread ) ;
}
return nread ;
}
int pkg_archive_readf64 ( pkg_archive_t * archive , pkg_usize_t * valuevar , FILE * input , pkg_sum_t * sum ) {
unsigned int valuelow , valuehigh ;
int nread = pkg_archive_readf32 ( archive , & valuelow , input , sum ) ;
if ( nread ! = 4 ) {
return nread ;
}
nread + = pkg_archive_readf32 ( archive , & valuehigh , input , sum ) ;
* valuevar = ( ( pkg_usize_t ) valuelow ) | ( ( ( pkg_usize_t ) valuehigh ) < < 32 ) ;
return nread ;
}
int pkg_archive_readheaders ( pkg_archive_t * archive , FILE * input ) {
char buffer [ 9 ] ;
buffer [ 8 ] = 0 ;
if ( archive = = NULL ) {
fprintf ( stderr , " INTERNAL ERROR: Invalid archive structure! \n " ) ;
return - 1 ;
}
pkg_usize_t nread = 0 ;
if ( fread ( buffer , 1 , 8 , input ) ! = 8 ) {
fprintf ( stderr , " WRITE ERROR: Reading from archive file failed! \n " ) ;
return - 1 ;
}
nread + = 8 ;
if ( strcmp ( buffer , " DEMOPKG0 " ) ) {
fprintf ( stderr , " BAD PACKAGE DATA: Magic number doesn't match! \n " ) ;
return - 1 ;
}
if ( fread ( buffer , 1 , 8 , input ) ! = 8 ) {
fprintf ( stderr , " WRITE ERROR: Reading from archive file failed! \n " ) ;
return - 1 ;
}
nread + = 8 ;
fprintf ( stderr , " Got reserved string '%s' \n " , buffer ) ;
unsigned int nfiles , headersize ;
nread + = pkg_archive_readf32 ( archive , & nfiles , input , NULL ) ; // Number of header entries
nread + = pkg_archive_readf32 ( archive , & headersize , input , NULL ) ; // Size of per-entry header
if ( headersize ! = 48 ) {
fprintf ( stderr , " BAD PACKAGE DATA: Expected a header size of 48 bytes per encoded file but got %d \n " , headersize ) ;
return - 1 ;
}
fprintf ( stderr , " Loading %d file headers... \n " , nfiles ) ;
pkg_sum_t * filehdr ;
for ( unsigned int i = 0 ; i < nfiles ; i + + ) {
filehdr = pkg_archive_addheader ( archive , " <LOADING...> " ) ;
if ( filehdr = = NULL ) {
fprintf ( stderr , " INTERNAL ERROR: Failed to add new header to archive struct! \n " ) ;
return - 1 ;
}
pkg_sum_t filehdrsum ;
if ( pkg_sum_init ( & filehdrsum , " <TMP-HDR> " , 0 ) ! = 0 ) {
fprintf ( stderr , " INTERNAL ERROR: pkg_sum_init failed! \n " ) ;
return - 1 ;
}
size_t nread2 = 0 ;
unsigned int hdrsum ;
nread2 + = pkg_archive_readf64 ( archive , & filehdr - > offset , input , & filehdrsum ) ;
nread2 + = pkg_archive_readf64 ( archive , & filehdr - > cmprsize , input , & filehdrsum ) ;
nread2 + = pkg_archive_readf64 ( archive , & filehdr - > extrsize , input , & filehdrsum ) ;
nread2 + = pkg_archive_readf32 ( archive , & filehdr - > flags , input , & filehdrsum ) ;
nread2 + = pkg_archive_readf32 ( archive , & filehdr - > checksum32 , input , & filehdrsum ) ;
nread2 + = pkg_archive_readf32 ( archive , & filehdr - > nameoffset , input , & filehdrsum ) ;
nread2 + = pkg_archive_readf32 ( archive , & filehdr - > namelength , input , & filehdrsum ) ;
nread2 + = pkg_archive_readf32 ( archive , & filehdr - > namechecksum , input , & filehdrsum ) ;
nread2 + = pkg_archive_readf32 ( archive , & hdrsum , input , NULL ) ;
if ( hdrsum ! = filehdrsum . checksum32 ) {
fprintf ( stderr , " VERIFICATION ERROR: Header checksum for file #%d in archive doesn't match. Recorded checksum 0x%X calculated checksum 0x%X! \n " , i , filehdrsum . checksum32 , hdrsum ) ;
return - 1 ;
}
//fprintf(stderr, "For file header #%d: recorded checksum 0x%X calculated checksum 0x%X!\n", i, filehdrsum.checksum32, hdrsum);
if ( nread2 ! = 48 ) {
fprintf ( stderr , " READ ERROR: Expected to read 48 bytes of file header info but read %d instead! \n " , ( int ) nread2 ) ;
return - 1 ;
}
nread + = nread2 ;
}
filehdr = archive - > headerlist ;
size_t nreadstringdata ;
int filenum = 0 ;
while ( filehdr ! = NULL ) {
size_t n = fread ( pkg_linebuffer , 1 , filehdr - > namelength , input ) ;
if ( n ! = filehdr - > namelength ) {
fprintf ( stderr , " READ ERROR: Failed while reading filename strings! \n " ) ;
return - 1 ;
}
filehdr - > name = strndup ( pkg_linebuffer , filehdr - > namelength ) ;
unsigned int namesum = pkg_cstringhash32 ( filehdr - > name ) ;
if ( namesum ! = filehdr - > namechecksum ) {
fprintf ( stderr , " VERIFICATION ERROR: Filename checksum for file #%d in archive doesn't match. Recorded checksum 0x%X calculated checksum 0x%X! \n " , filenum , filehdr - > namechecksum , namesum ) ;
return - 1 ;
}
//fprintf(stderr, "For file header #%d in archive: Recorded checksum 0x%X calculated checksum 0x%X!\n", filenum, filehdr->namechecksum, namesum);
fprintf ( stderr , " Loaded header for file #%d in archive: extracted size %lu, compressed size %lu, data checksum 0x%X, name '%s' \n " , filenum , ( long ) filehdr - > extrsize , filehdr - > cmprsize , filehdr - > checksum32 , filehdr - > name ) ;
filehdr = filehdr - > next ;
filenum + + ;
}
return 0 ;
}
int pkg_archive_writeheaders ( pkg_archive_t * archive , FILE * output , int blankrun ) {
if ( archive = = NULL ) {
fprintf ( stderr , " INTERNAL ERROR: Invalid archive structure! \n " ) ;
return - 1 ;
}
pkg_usize_t nwritten = 0 ;
if ( fwrite ( ( blankrun ? " UNFINSHD " : " DEMOPKG0 " ) , 1 , 8 , output ) ! = 8 ) {
fprintf ( stderr , " WRITE ERROR: Writing to archive file failed! \n " ) ;
return - 1 ;
}
nwritten + = 8 ;
if ( fwrite ( ( blankrun ? " PARTWRIT " : " RESERVED " ) , 1 , 8 , output ) ! = 8 ) {
fprintf ( stderr , " WRITE ERROR: Writing to archive file failed! \n " ) ;
return - 1 ;
}
nwritten + = 8 ;
unsigned int nfiles = 0 ;
pkg_sum_t * filehdr = archive - > headerlist ;
while ( filehdr ! = NULL ) {
filehdr = filehdr - > next ;
nfiles + + ;
}
nwritten + = pkg_archive_writef32 ( archive , nfiles , output , NULL ) ; // Number of header entries
nwritten + = pkg_archive_writef32 ( archive , 48 , output , NULL ) ; // Size of per-entry header
filehdr = archive - > headerlist ;
unsigned int nameoffset = 0 ;
while ( filehdr ! = NULL ) {
unsigned int namelen = ( unsigned int ) strlen ( filehdr - > name ) ;
pkg_sum_t filehdrsum ;
if ( pkg_sum_init ( & filehdrsum , " <TMP-HDR> " , 0 ) ! = 0 ) {
fprintf ( stderr , " INTERNAL ERROR: pkg_sum_init failed! \n " ) ;
return - 1 ;
}
size_t nwritten2 = 0 ;
nwritten2 + = pkg_archive_writef64 ( archive , filehdr - > offset , output , & filehdrsum ) ;
nwritten2 + = pkg_archive_writef64 ( archive , filehdr - > cmprsize , output , & filehdrsum ) ;
nwritten2 + = pkg_archive_writef64 ( archive , filehdr - > extrsize , output , & filehdrsum ) ;
nwritten2 + = pkg_archive_writef32 ( archive , filehdr - > flags , output , & filehdrsum ) ;
nwritten2 + = pkg_archive_writef32 ( archive , filehdr - > checksum32 , output , & filehdrsum ) ;
nwritten2 + = pkg_archive_writef32 ( archive , nameoffset , output , & filehdrsum ) ;
nwritten2 + = pkg_archive_writef32 ( archive , namelen , output , & filehdrsum ) ;
nwritten2 + = pkg_archive_writef32 ( archive , pkg_cstringhash32 ( filehdr - > name ) , output , & filehdrsum ) ;
nwritten2 + = pkg_archive_writef32 ( archive , filehdrsum . checksum32 , output , NULL ) ;
if ( nwritten2 ! = 48 ) {
fprintf ( stderr , " WRITE ERROR: Expected to write 48 bytes of file header info but wrote %d instead! \n " , ( int ) nwritten2 ) ;
return - 1 ;
}
nwritten + = nwritten2 ;
nameoffset + = namelen ;
filehdr = filehdr - > next ;
}
filehdr = archive - > headerlist ;
while ( filehdr ! = NULL ) {
size_t nwritten2 = 0 ;
nwritten2 = fwrite ( filehdr - > name , 1 , strlen ( filehdr - > name ) , output ) ;
if ( nwritten2 ! = strlen ( filehdr - > name ) ) {
fprintf ( stderr , " WRITE ERROR: Failed while writing filename strings! \n " ) ;
return - 1 ;
}
nwritten + = nwritten2 ;
filehdr = filehdr - > next ;
}
fprintf ( stderr , " Written %ld bytes of header \n " , ( long ) nwritten ) ;
return 0 ;
}
int pkg_archive_extractcontents ( pkg_archive_t * archive , FILE * input , const char * installroot ) {
if ( archive = = NULL | | archive - > headerlist = = NULL ) {
fprintf ( stderr , " INTERNAL ERROR: Invalid or uninitialised archive structure! \n " ) ;
return - 1 ;
}
pkg_usize_t nwritten = 0 ;
pkg_sum_t * filehdr = archive - > headerlist ;
while ( filehdr ! = NULL ) {
char * outputname = pkg_ezstrcat ( installroot , filehdr - > name ) ; // Note, this assumes one or the other includes a path separator
2025-06-08 18:09:03 +10:00
int t = filehdr - > flags & PKG_FLAGS_TYPEMASK ;
if ( t = = PKG_FLAGS_DIRECTORY ) {
2025-06-03 09:11:48 +00:00
fprintf ( stderr , " Creating directory '%s'... \n " , outputname ) ;
int mkdirresult = pkg_mkdir ( outputname ) ;
fprintf ( stderr , " mkdir of '%s' returned %d \n " , outputname , mkdirresult ) ;
2025-06-08 18:09:03 +10:00
} else if ( t = = PKG_FLAGS_PROVIDES | | t = = PKG_FLAGS_REQUIRES | | t = = PKG_FLAGS_SUGGESTS | | t = = PKG_FLAGS_CONFLICTS ) {
fprintf ( stderr , " Ignoring metadata line '%s'... \n " , filehdr - > name ) ;
2025-06-03 09:11:48 +00:00
} else {
fprintf ( stderr , " Creating file '%s'... \n " , outputname ) ;
FILE * f = fopen ( outputname , " w " ) ;
if ( f = = 0 ) {
fprintf ( stderr , " BAD FILE: Failed to open '%s' for writing! \n " , outputname ) ;
return - 1 ;
}
size_t nreadnow , nwrittennow ;
size_t remaining = filehdr - > extrsize ;
pkg_sum_t datasum ;
if ( pkg_sum_init ( & datasum , " <TMP-FILEDATA> " , 0 ) ! = 0 ) {
fprintf ( stderr , " INTERNAL ERROR: pkg_sum_init failed! \n " ) ;
return - 1 ;
}
do {
size_t part = PKG_BUFFERSIZE > remaining ? remaining : PKG_BUFFERSIZE ;
nreadnow = fread ( pkg_linebuffer , 1 , part , input ) ;
if ( nreadnow > 0 ) {
nwrittennow = fwrite ( pkg_linebuffer , 1 , nreadnow , f ) ;
if ( pkg_sum_append ( & datasum , pkg_linebuffer , nreadnow ) ! = 0 ) {
fprintf ( stderr , " INTERNAL ERROR: pkg_sum_append failed! \n " ) ;
free ( outputname ) ;
fclose ( f ) ;
return - 1 ;
}
if ( nwrittennow ! = nreadnow ) {
fprintf ( stderr , " WRITE ERROR: Error while extracting contents of '%s' from the archive! \n " , outputname ) ;
free ( outputname ) ;
fclose ( f ) ;
return - 1 ;
}
nwritten + = nwrittennow ;
remaining - = nwrittennow ;
}
} while ( remaining > 0 ) ;
fclose ( f ) ;
if ( datasum . checksum32 ! = filehdr - > checksum32 ) {
fprintf ( stderr , " VERIFICATION ERROR: File data checksums for '%s' don't match. Recorded checksum 0x%X, calculated checksum 0x%X! \n " , filehdr - > name , filehdr - > checksum32 , datasum . checksum32 ) ;
return - 1 ;
}
2025-06-08 18:09:03 +10:00
if ( t = = PKG_FLAGS_EXECUTABLE ) {
pkg_chmod_executable ( outputname ) ;
} else {
pkg_chmod_default ( outputname ) ;
}
2025-06-03 09:11:48 +00:00
}
filehdr = filehdr - > next ;
}
return 0 ;
}
int pkg_archive_writecontents ( pkg_archive_t * archive , FILE * output , const char * inputdir ) {
if ( archive = = NULL | | archive - > headerlist = = NULL ) {
fprintf ( stderr , " INTERNAL ERROR: Invalid or uninitialised archive structure! \n " ) ;
return - 1 ;
}
pkg_usize_t nwritten = 0 ;
pkg_sum_t * filehdr = archive - > headerlist ;
while ( filehdr ! = NULL ) {
if ( filehdr - > flags & PKG_FLAGS_DIRECTORY ) {
fprintf ( stderr , " Skipping writing of file contents for '%s' (is directory) \n " , filehdr - > name ) ;
} else {
char * inputname = pkg_ezstrcat ( inputdir , filehdr - > name ) ; // Note, this assumes one or the other includes a path separator
FILE * f = fopen ( inputname , " rb " ) ;
if ( f = = 0 ) {
fprintf ( stderr , " BAD FILE: Failed to open '%s' for reading! \n " , inputname ) ;
return - 1 ;
}
size_t nreadnow , nwrittennow ;
do {
nreadnow = fread ( pkg_linebuffer , 1 , PKG_BUFFERSIZE , f ) ;
if ( nreadnow > 0 ) {
nwrittennow = fwrite ( pkg_linebuffer , 1 , nreadnow , output ) ;
if ( pkg_sum_append ( filehdr , pkg_linebuffer , nreadnow ) ! = 0 ) {
fprintf ( stderr , " INTERNAL ERROR: pkg_sum_append failed! \n " ) ;
free ( inputname ) ;
fclose ( f ) ;
return - 1 ;
}
if ( nwrittennow ! = nreadnow ) {
fprintf ( stderr , " WRITE ERROR: Error while writing contents of '%s' into the archive! \n " , inputname ) ;
free ( inputname ) ;
fclose ( f ) ;
return - 1 ;
}
nwritten + = nwrittennow ;
}
} while ( nreadnow > 0 ) ;
fclose ( f ) ;
}
filehdr = filehdr - > next ;
}
return 0 ;
}
char * pkg_linein ( FILE * f , pkg_size_t * sizevar ) {
char b ;
char * buffer = pkg_linebuffer ;
pkg_size_t i = 0 ;
do {
if ( i = = PKG_BUFFERSIZE - 1 ) {
fprintf ( stderr , " BAD FILE: Text line overflows buffer size! \n " ) ;
if ( sizevar ) {
* sizevar + = i ;
}
buffer [ i ] = 0 ;
return NULL ;
}
if ( fread ( & b , 1 , 1 , f ) ! = 1 ) {
if ( sizevar ) {
* sizevar + = i ;
}
buffer [ i ] = 0 ;
if ( i = = 0 ) {
return NULL ;
}
return buffer ;
}
if ( b = = 0 | | b = = ' \n ' ) {
if ( sizevar ) {
* sizevar + = i + 1 ;
}
buffer [ i ] = 0 ;
return buffer ;
}
buffer [ i ] = b ;
i + + ;
} while ( 1 ) ;
}
int pkg_archive_uninstallcontents ( pkg_archive_t * archive , FILE * input , const char * installroot ) {
if ( archive = = NULL | | archive - > headerlist = = NULL ) {
fprintf ( stderr , " INTERNAL ERROR: Invalid or uninitialised archive structure! \n " ) ;
return - 1 ;
}
pkg_usize_t nwritten = 0 ;
pkg_sum_t * filehdr = archive - > headerlist ;
while ( filehdr ! = NULL ) {
char * outputname = pkg_ezstrcat ( installroot , filehdr - > name ) ; // Note, this assumes one or the other includes a path separator
if ( filehdr - > flags & PKG_FLAGS_DIRECTORY ) {
// Skip
} else {
fprintf ( stderr , " Removing packaged file '%s'... \n " , outputname ) ;
pkg_remove ( outputname , 0 ) ;
}
filehdr = filehdr - > next ;
}
filehdr = archive - > headerlist ;
while ( filehdr ! = NULL ) {
char * outputname = pkg_ezstrcat ( installroot , filehdr - > name ) ; // Note, this assumes one or the other includes a path separator
if ( filehdr - > flags & PKG_FLAGS_DIRECTORY ) {
fprintf ( stderr , " Removing packaged directory '%s'... \n " , outputname ) ;
pkg_remove ( outputname , 1 ) ;
} else {
// Skip (already deleted)
}
filehdr = filehdr - > next ;
}
return 0 ;
}
const char * pkg_build_linematch ( const char * line , const char * cmd ) {
while ( * cmd ) {
if ( * line = = 0 ) {
return NULL ;
}
if ( * line ! = * cmd ) {
return NULL ;
}
* line + + ;
* cmd + + ;
}
return line ;
}
int pkg_job_build ( pkg_job_t * job , char * name ) {
int n = strlen ( name ) ;
if ( n < 5 | | name [ n - 4 ] ! = ' . ' | | name [ n - 3 ] ! = ' p ' | | name [ n - 2 ] ! = ' k ' | | name [ n - 1 ] ! = ' b ' ) {
fprintf ( stderr , " BAD FILENAME: Package build file must end in .pkb \n " ) ;
return - 1 ;
}
int sepi = 0 ;
for ( int i = 0 ; name [ i ] ! = 0 ; i + + ) {
if ( name [ i ] = = ' / ' | | name [ i ] = = ' \\ ' ) {
sepi = i + 1 ;
}
}
char * cd = " ./ " ;
char * srcd ;
if ( sepi = = 0 ) {
srcd = cd ;
} else {
srcd = strndup ( name , sepi ) ;
}
char * distsubname = " dist " ; // The directory packageable files should be kept
char * distd = pkg_ezstrcat ( srcd , " dist " ) ;
char * outname = strdup ( name ) ;
outname [ strlen ( outname ) - 1 ] = ' g ' ; // "*.pkb"-> "*.pkg"
char * shortname = strndup ( name + sepi , n - ( sepi + 4 ) ) ; // Trim directory path and filename extension
2025-06-08 18:09:03 +10:00
//printf("TODO build '%s' source dirname '%s' dist dirname '%s' output name '%s' shortname '%s'...\n", name, srcd, distd, outname, shortname);
2025-06-03 09:11:48 +00:00
FILE * buildf = fopen ( name , " rb " ) ;
if ( buildf = = NULL ) {
fprintf ( stderr , " BAD FILE: Failed to open package build file '%s' for reading! \n " , name ) ;
return - 1 ;
}
char * line ;
pkg_archive_t archive ;
if ( pkg_archive_init ( & archive ) ! = 0 ) {
fprintf ( stderr , " INTERNAL ERROR: Failed to initialise archive structure! \n " ) ;
fclose ( buildf ) ;
return - 1 ;
}
while ( ( line = pkg_linein ( buildf , NULL ) ) ! = NULL ) {
fprintf ( stderr , " Processing line '%s'... \n " , line ) ;
const char * arg ;
if ( ( arg = pkg_build_linematch ( line , " provides " ) ) ! = NULL ) {
pkg_sum_t * mkdirhdr = pkg_archive_addheader ( & archive , arg ) ;
if ( mkdirhdr = = NULL ) {
fprintf ( stderr , " INTERNAL ERROR: Failed to add archive header for '%s'! \n " , line ) ;
fclose ( buildf ) ;
return - 1 ;
}
mkdirhdr - > flags | = PKG_FLAGS_PROVIDES ;
} else if ( ( arg = pkg_build_linematch ( line , " requires " ) ) ! = NULL ) {
pkg_sum_t * mkdirhdr = pkg_archive_addheader ( & archive , arg ) ;
if ( mkdirhdr = = NULL ) {
fprintf ( stderr , " INTERNAL ERROR: Failed to add archive header for '%s'! \n " , line ) ;
fclose ( buildf ) ;
return - 1 ;
}
mkdirhdr - > flags | = PKG_FLAGS_REQUIRES ;
} else if ( ( arg = pkg_build_linematch ( line , " suggests " ) ) ! = NULL ) {
pkg_sum_t * mkdirhdr = pkg_archive_addheader ( & archive , arg ) ;
if ( mkdirhdr = = NULL ) {
fprintf ( stderr , " INTERNAL ERROR: Failed to add archive header for '%s'! \n " , line ) ;
fclose ( buildf ) ;
return - 1 ;
}
mkdirhdr - > flags | = PKG_FLAGS_SUGGESTS ;
} else if ( ( arg = pkg_build_linematch ( line , " conflicts " ) ) ! = NULL ) {
pkg_sum_t * mkdirhdr = pkg_archive_addheader ( & archive , arg ) ;
if ( mkdirhdr = = NULL ) {
fprintf ( stderr , " INTERNAL ERROR: Failed to add archive header for '%s'! \n " , line ) ;
fclose ( buildf ) ;
return - 1 ;
}
mkdirhdr - > flags | = PKG_FLAGS_CONFLICTS ;
} else if ( ( arg = pkg_build_linematch ( line , " directory " ) ) ! = NULL ) {
pkg_sum_t * mkdirhdr = pkg_archive_addheader ( & archive , arg ) ;
if ( mkdirhdr = = NULL ) {
fprintf ( stderr , " INTERNAL ERROR: Failed to add archive header for '%s'! \n " , line ) ;
fclose ( buildf ) ;
return - 1 ;
}
mkdirhdr - > flags | = PKG_FLAGS_DIRECTORY ;
} else if ( ( arg = pkg_build_linematch ( line , " executable " ) ) ! = NULL ) {
pkg_sum_t * mkdirhdr = pkg_archive_addheader ( & archive , arg ) ;
if ( mkdirhdr = = NULL ) {
fprintf ( stderr , " INTERNAL ERROR: Failed to add archive header for '%s'! \n " , line ) ;
fclose ( buildf ) ;
return - 1 ;
}
mkdirhdr - > flags | = PKG_FLAGS_EXECUTABLE ;
} else if ( pkg_archive_addheader ( & archive , line ) = = NULL ) {
fprintf ( stderr , " INTERNAL ERROR: Failed to add archive header for '%s'! \n " , line ) ;
fclose ( buildf ) ;
return - 1 ;
}
}
fclose ( buildf ) ;
FILE * outputf = fopen ( outname , " w " ) ;
if ( outputf = = NULL ) {
fprintf ( stderr , " WRITE ERROR: Failed to open package build file '%s' for writing! \n " , outname ) ;
return - 1 ;
}
// The first, "blank" run only writes a placeholder of the headers
if ( pkg_archive_writeheaders ( & archive , outputf , 1 ) ! = 0 ) {
fprintf ( stderr , " ERROR WRITING HEADERS: First run of pkg_archive_writeheaders failed! \n " ) ;
return - 1 ;
}
// The files themselves are written after the blank headers, but checksums &
// sizes for the headers are calculated during writing.
if ( pkg_archive_writecontents ( & archive , outputf , distd ) ! = 0 ) {
fprintf ( stderr , " ERROR WRITING CONTENTS: The pkg_archive_writecontents function failed! \n " ) ;
return - 1 ;
}
// The output file is then closed and reopened, or on compatible systems fseek
// or something like that can be used to return to the start of the file.
fclose ( outputf ) ;
outputf = fopen ( outname , " w " ) ;
if ( outputf = = NULL ) {
fprintf ( stderr , " WRITE ERROR: Failed to open package build file '%s' for writing! \n " , outname ) ;
return - 1 ;
}
// Now at the start of the file again, we can write the final headers.
if ( pkg_archive_writeheaders ( & archive , outputf , 0 ) ! = 0 ) {
fprintf ( stderr , " ERROR WRITING HEADERS: Second run of pkg_archive_writeheaders failed! \n " ) ;
return - 1 ;
}
// And it seems we have to write the contents properly this time.
if ( pkg_archive_writecontents ( & archive , outputf , distd ) ! = 0 ) {
fprintf ( stderr , " ERROR WRITING CONTENTS: The pkg_archive_writecontents function failed! \n " ) ;
return - 1 ;
}
// Then upon this final close the file should be finished (subject to
// verification when installing later).
fclose ( outputf ) ;
if ( srcd ! = cd ) {
free ( srcd ) ;
}
free ( distd ) ;
free ( outname ) ;
free ( shortname ) ;
return 0 ;
}
char * pkg_simplefname ( char * name ) {
char * laststart = name ;
while ( * name ) {
if ( ( * name = = ' / ' | | * name = = ' \\ ' ) & & * ( name + 1 ) ! = 0 ) {
laststart = name + 1 ;
}
name + + ;
}
laststart = strdup ( laststart ) ;
if ( laststart = = NULL ) {
return NULL ;
}
for ( int i = 0 ; laststart [ i ] ! = 0 ; i + + ) {
if ( laststart [ i ] = = ' . ' | | laststart [ i ] = = ' / ' | | laststart [ i ] = = ' \\ ' ) {
laststart [ i ] = 0 ;
return laststart ;
}
}
return laststart ;
}
int pkg_job_install ( pkg_job_t * job , char * name ) {
int n = strlen ( name ) ;
if ( n < 5 | | name [ n - 4 ] ! = ' . ' | | name [ n - 3 ] ! = ' p ' | | name [ n - 2 ] ! = ' k ' | | name [ n - 1 ] ! = ' g ' ) {
fprintf ( stderr , " BAD FILENAME: Installable package file must end in .pkg \n " ) ;
return - 1 ;
}
pkg_archive_t archive ;
if ( pkg_archive_init ( & archive ) ! = 0 ) {
fprintf ( stderr , " INTERNAL ERROR: Failed to initialise archive structure! \n " ) ;
return - 1 ;
}
FILE * inputf = fopen ( name , " rb " ) ;
if ( inputf = = NULL ) {
fprintf ( stderr , " READ ERROR: Failed to open package file '%s' for reading! \n " , name ) ;
return - 1 ;
}
if ( pkg_archive_readheaders ( & archive , inputf ) ! = 0 ) {
fprintf ( stderr , " FILE ERROR: Failed to read package headers! \n " ) ;
return - 1 ;
}
if ( pkg_archive_extractcontents ( & archive , inputf , job - > installroot ) ! = 0 ) {
fprintf ( stderr , " FILE ERROR: Failed to extract package contents! \n " ) ;
return - 1 ;
}
fclose ( inputf ) ;
if ( job - > usedb ) {
inputf = fopen ( name , " rb " ) ;
if ( inputf = = NULL ) {
fprintf ( stderr , " READ ERROR: Failed to open package file '%s' for reading! \n " , name ) ;
return - 1 ;
}
char * simplename = pkg_simplefname ( name ) ;
char * dbfile = pkg_ezstrcat ( job - > dbroot , simplename ) ;
dbfile = pkg_ezstrcat ( dbfile , " .pkh " ) ;
fprintf ( stderr , " Creating installation record '%s'... \n " , dbfile ) ;
FILE * outputf = fopen ( dbfile , " w " ) ;
if ( outputf = = NULL ) {
pkg_mkdir ( job - > dbroot ) ;
outputf = fopen ( dbfile , " w " ) ;
if ( outputf = = NULL ) {
fprintf ( stderr , " READ ERROR: Failed to open db file '%s' for writing! \n " , dbfile ) ;
return - 1 ;
}
}
size_t nread ;
do {
nread = fread ( pkg_linebuffer , 1 , PKG_BUFFERSIZE , inputf ) ;
if ( nread > 0 ) {
size_t nwritten = fwrite ( pkg_linebuffer , 1 , nread , outputf ) ;
if ( nwritten ! = nread ) {
fprintf ( stderr , " WRITE ERROR: Failed while writing db file '%s'! \n " , dbfile ) ;
fclose ( inputf ) ;
fclose ( outputf ) ;
return - 1 ;
}
}
} while ( nread > 0 ) ;
fclose ( inputf ) ;
fclose ( outputf ) ;
dbfile = pkg_ezstrcat ( job - > dbroot , " pkglist.txt " ) ;
outputf = fopen ( dbfile , " a " ) ;
if ( outputf = = NULL ) {
fprintf ( stderr , " Creating database index file '%s'... \n " , dbfile ) ;
outputf = fopen ( dbfile , " w " ) ;
if ( outputf = = NULL ) {
fprintf ( stderr , " READ ERROR: Failed to open db file '%s' for writing! \n " , dbfile ) ;
return - 1 ;
}
}
fprintf ( outputf , " %s \n " , simplename ) ;
fclose ( outputf ) ;
}
return 0 ;
}
int pkg_job_remove ( pkg_job_t * job , char * name ) {
int n = strlen ( name ) ;
FILE * inputf = NULL ;
char * dbfile = NULL ;
if ( n < 5 | | name [ n - 4 ] ! = ' . ' | | name [ n - 3 ] ! = ' p ' | | name [ n - 2 ] ! = ' k ' | | ( name [ n - 1 ] ! = ' g ' & & name [ n - 1 ] ! = ' h ' ) ) {
if ( job - > usedb ) {
char * simplename = pkg_simplefname ( name ) ;
dbfile = pkg_ezstrcat ( job - > dbroot , simplename ) ;
dbfile = pkg_ezstrcat ( dbfile , " .pkh " ) ;
inputf = fopen ( dbfile , " rb " ) ;
if ( inputf = = NULL ) {
fprintf ( stderr , " READ ERROR: Failed to open package file '%s' for reading! \n " , dbfile ) ;
return - 1 ;
}
} else {
fprintf ( stderr , " BAD FILENAME: Installable package file must end in .pkg or .pkh if db is explicitly disabled \n " ) ;
return - 1 ;
}
} else {
inputf = fopen ( name , " rb " ) ;
if ( inputf = = NULL ) {
fprintf ( stderr , " READ ERROR: Failed to open package file '%s' for reading! \n " , name ) ;
return - 1 ;
}
}
pkg_archive_t archive ;
if ( pkg_archive_init ( & archive ) ! = 0 ) {
fprintf ( stderr , " INTERNAL ERROR: Failed to initialise archive structure! \n " ) ;
return - 1 ;
}
if ( pkg_archive_readheaders ( & archive , inputf ) ! = 0 ) {
fprintf ( stderr , " FILE ERROR: Failed to read package headers! \n " ) ;
return - 1 ;
}
if ( pkg_archive_uninstallcontents ( & archive , inputf , job - > installroot ) ! = 0 ) {
fprintf ( stderr , " FILE ERROR: Failed to uninstall package contents! \n " ) ;
return - 1 ;
}
fclose ( inputf ) ;
// Clean up the .pkh file after uninstalling the packaged files
if ( dbfile ) {
fprintf ( stderr , " Removing installation record '%s'... \n " , dbfile ) ;
pkg_remove ( dbfile , 0 ) ;
}
return 0 ;
}
int pkg_job_sum ( pkg_job_t * job , char * name ) {
pkg_sum_t sum ;
//fprintf(stderr, "Checking the sum of '%s'...\n", name);
if ( pkg_sum_init ( & sum , name , 0 ) ! = 0 ) {
fprintf ( stderr , " INTERNAL ERROR: pkg_sum_init failed for '%s'! \n " , name ) ;
return - 1 ;
}
FILE * inf = fopen ( name , " rb " ) ;
if ( inf = = NULL ) {
fprintf ( stderr , " BAD FILE: Failed to open file '%s' for reading! \n " , name ) ;
return - 1 ;
}
size_t nread = 0 ;
do {
nread = fread ( pkg_linebuffer , 1 , PKG_BUFFERSIZE , inf ) ;
if ( pkg_sum_append ( & sum , pkg_linebuffer , nread ) ! = 0 ) {
fprintf ( stderr , " INTERNAL ERROR: pkg_sum_append failed for '%s'! \n " , name ) ;
fclose ( inf ) ;
return - 1 ;
}
} while ( nread = = PKG_BUFFERSIZE ) ;
fclose ( inf ) ;
printf ( " %s checksum64=0x%llX checksum32=0x%X size=%llu [namesum=0x%X] \n " , sum . name , sum . checksum64 , sum . checksum32 , sum . extrsize , pkg_cstringhash32 ( sum . name ) ) ;
return 0 ;
}
int pkg_compress_f2f ( FILE * inf , FILE * outf , size_t size , pkg_sum_t * sum ) {
size_t nread = 0 ;
do {
nread = fread ( pkg_linebuffer , 1 , PKG_BUFFERSIZE , inf ) ;
if ( sum ! = NULL & & pkg_sum_append ( sum , pkg_linebuffer , nread ) ! = 0 ) {
fprintf ( stderr , " INTERNAL ERROR: pkg_sum_append failed during compression! \n " ) ;
return - 1 ;
}
if ( nread > 0 ) {
printf ( " . " ) ;
fflush ( stdout ) ;
pkg_sum_t blocksum ;
if ( pkg_sum_init ( & blocksum , " <TMP-BLOCK> " , 0 ) ! = 0 ) {
fprintf ( stderr , " INTERNAL ERROR: pkg_sum_init failed! \n " ) ;
return - 1 ;
}
size_t ncompressed = pkg_compressv1 ( pkg_linebuffer , nread , pkg_comprbuffer , PKG_COMPRBUFSIZE ) ;
if ( ncompressed < 1 | | ncompressed > PKG_COMPRBUFSIZE | | ncompressed > = 64 * 1024 ) {
fprintf ( stderr , " INTERNAL ERROR: pkg_compressv1 failed! \n " ) ;
return - 1 ;
}
if ( pkg_sum_append ( & blocksum , pkg_comprbuffer , ncompressed ) ! = 0 ) {
fprintf ( stderr , " INTERNAL ERROR: pkg_sum_append failed during compression! \n " ) ;
return - 1 ;
}
unsigned char hdr [ 4 ] ;
hdr [ 0 ] = 0xCF ;
hdr [ 1 ] = 0x11 ;
hdr [ 2 ] = ( unsigned char ) ncompressed ;
hdr [ 3 ] = ( unsigned char ) ( ncompressed > > 8 ) ;
if ( fwrite ( hdr , 1 , 4 , outf ) ! = 4 ) {
fprintf ( stderr , " WRITE ERROR: Failed while writing compressed output! \n " ) ;
return - 1 ;
}
if ( fwrite ( pkg_comprbuffer , 1 , ncompressed , outf ) ! = ncompressed ) {
fprintf ( stderr , " WRITE ERROR: Failed while writing compressed output! \n " ) ;
return - 1 ;
}
if ( pkg_archive_writef32 ( NULL , blocksum . checksum32 , outf , NULL ) ! = 4 ) {
fprintf ( stderr , " WRITE ERROR: Failed while writing compressed output! \n " ) ;
return - 1 ;
}
}
} while ( nread = = PKG_BUFFERSIZE ) ;
return 0 ;
}
int pkg_decompress_f2f ( FILE * inf , FILE * outf , size_t size , pkg_sum_t * sum ) {
size_t nread = 0 ;
size_t totalread = 0 ;
do {
if ( size ! = - 1 & & nread + 8 > size ) {
fprintf ( stderr , " INTERNAL ERROR: Buffer has trailing junk not long enough to be a compressed chunk! \n " ) ;
return - 1 ;
}
unsigned char hdr [ 4 ] ;
nread = fread ( hdr , 1 , 4 , inf ) ;
if ( nread = = 0 & & size = = - 1 ) {
// Finished reading input stream until the end, return as normal.
return 0 ;
}
if ( nread ! = 4 ) {
fprintf ( stderr , " READ ERROR: Failed at block header while decompressing file stream! \n " ) ;
return - 1 ;
}
if ( hdr [ 0 ] ! = 0xCF | | hdr [ 1 ] ! = 0x11 ) {
fprintf ( stderr , " VERIFICATION ERROR: Magic number at start of compressed data block doesn't match for compressed data! \n " ) ;
return - 1 ;
}
size_t ncompressed = ( ( ( size_t ) hdr [ 2 ] ) & 0xFF ) | ( ( ( ( size_t ) hdr [ 3 ] ) & 0xFF ) < < 8 ) ;
fprintf ( stderr , " Reading %d bytes (compressed)... \n " , ncompressed ) ;
if ( size ! = - 1 & & nread + 4 + ncompressed > size ) {
fprintf ( stderr , " INTERNAL ERROR: Buffer size does not fit compressed chunk size! \n " ) ;
return - 1 ;
}
if ( sum ! = NULL & & pkg_sum_append ( sum , pkg_linebuffer , nread ) ! = 0 ) {
fprintf ( stderr , " INTERNAL ERROR: pkg_sum_append failed during compression! \n " ) ;
return - 1 ;
}
if ( nread > 0 ) {
printf ( " . " ) ;
fflush ( stdout ) ;
pkg_sum_t blocksum ;
if ( pkg_sum_init ( & blocksum , " <TMP-BLOCK> " , 0 ) ! = 0 ) {
fprintf ( stderr , " INTERNAL ERROR: pkg_sum_init failed! \n " ) ;
return - 1 ;
}
nread + = fread ( pkg_comprbuffer , 1 , ncompressed , inf ) ;
if ( nread ! = 4 + ncompressed ) {
fprintf ( stderr , " READ ERROR: Failed mid-block while decompressing file stream! \n " ) ;
return - 1 ;
}
size_t ndecompressed = pkg_decompressv1 ( pkg_comprbuffer , ncompressed , pkg_linebuffer , PKG_BUFFERSIZE ) ;
if ( ndecompressed < 1 | | ndecompressed > PKG_BUFFERSIZE | | ndecompressed > = 64 * 1024 ) {
fprintf ( stderr , " INTERNAL ERROR: pkg_decompressv1 failed! \n " ) ;
return - 1 ;
}
if ( pkg_sum_append ( & blocksum , pkg_linebuffer , ndecompressed ) ! = 0 ) {
fprintf ( stderr , " INTERNAL ERROR: pkg_sum_append failed during compression! \n " ) ;
return - 1 ;
}
unsigned int comprsum ;
if ( pkg_archive_readf32 ( NULL , & comprsum , inf , NULL ) ! = 4 ) {
fprintf ( stderr , " READ ERROR: Failed while reading compressed input! \n " ) ;
return - 1 ;
}
size_t nwritten = fwrite ( pkg_linebuffer , 1 , ndecompressed , outf ) ;
if ( nwritten ! = ndecompressed ) {
fprintf ( stderr , " WRITE ERROR: Failed mid-block while decompressing file stream! Tried to write %d bytes but only wrote %d! \n " , ( int ) ndecompressed , ( int ) nwritten ) ;
return - 1 ;
}
nread + = 4 ;
totalread + = nread ;
}
} while ( nread > 0 & & ( size = = - 1 | | totalread < size ) ) ;
return 0 ;
}
int pkg_job_compress ( pkg_job_t * job , char * name ) {
pkg_sum_t sum ;
char * outname = pkg_ezstrcat ( name , " .cv1 " ) ;
if ( pkg_sum_init ( & sum , outname , 0 ) ! = 0 ) {
fprintf ( stderr , " INTERNAL ERROR: pkg_sum_init failed for '%s'! \n " , name ) ;
free ( outname ) ;
return - 1 ;
}
FILE * inf = fopen ( name , " rb " ) ;
if ( inf = = NULL ) {
fprintf ( stderr , " BAD FILE: Failed to open file '%s' for reading! \n " , name ) ;
free ( outname ) ;
return - 1 ;
}
FILE * outf = fopen ( outname , " w " ) ;
if ( outf = = NULL ) {
fprintf ( stderr , " WRITE ERROR: Failed to open file '%s' for writing! \n " , outname ) ;
free ( outname ) ;
fclose ( inf ) ;
return - 1 ;
}
int result = pkg_compress_f2f ( inf , outf , - 1 , & sum ) ;
fclose ( inf ) ;
fclose ( outf ) ;
if ( result < 0 ) {
fprintf ( stderr , " COMPRESSION FAILED: pkg_compress_f2f returned non-zero result! \n " ) ;
return - 1 ;
}
printf ( " %s checksum64=0x%llX checksum32=0x%X size=%llu [namesum=0x%X] \n " , sum . name , sum . checksum64 , sum . checksum32 , sum . extrsize , pkg_cstringhash32 ( sum . name ) ) ;
return 0 ;
}
int pkg_job_decompress ( pkg_job_t * job , char * name ) {
pkg_sum_t sum ;
int namelen = ( int ) strlen ( name ) ;
if ( namelen < 4 | | name [ namelen - 4 ] ! = ' . ' | | name [ namelen - 3 ] ! = ' c ' | | name [ namelen - 2 ] ! = ' v ' | | name [ namelen - 1 ] ! = ' 1 ' ) {
fprintf ( stderr , " BAD FILE: expected a file with extension .cv1 but got '%s'! \n " , name ) ;
}
char * outname = strdup ( name ) ;
outname [ namelen - 4 ] = 0 ; // Remove .cv1 extension
if ( pkg_sum_init ( & sum , outname , 0 ) ! = 0 ) {
fprintf ( stderr , " INTERNAL ERROR: pkg_sum_init failed for '%s'! \n " , name ) ;
free ( outname ) ;
return - 1 ;
}
FILE * inf = fopen ( name , " rb " ) ;
if ( inf = = NULL ) {
fprintf ( stderr , " BAD FILE: Failed to open file '%s' for reading! \n " , name ) ;
free ( outname ) ;
return - 1 ;
}
FILE * outf = fopen ( outname , " w " ) ;
if ( outf = = NULL ) {
fprintf ( stderr , " WRITE ERROR: Failed to open file '%s' for writing! \n " , outname ) ;
free ( outname ) ;
fclose ( inf ) ;
return - 1 ;
}
int result = pkg_decompress_f2f ( inf , outf , - 1 , & sum ) ;
fclose ( inf ) ;
fclose ( outf ) ;
if ( result < 0 ) {
fprintf ( stderr , " DECOMPRESSION FAILED: pkg_decompress_f2f returned non-zero result! \n " ) ;
return - 1 ;
}
printf ( " %s checksum64=0x%llX checksum32=0x%X size=%llu [namesum=0x%X] \n " , sum . name , sum . checksum64 , sum . checksum32 , sum . extrsize , pkg_cstringhash32 ( sum . name ) ) ;
return 0 ;
}
int pkg_job_subrun ( pkg_job_t * job , char * name ) {
switch ( job - > jobtype ) {
case PKG_JOBTYPE_BUILD :
return pkg_job_build ( job , name ) ;
case PKG_JOBTYPE_INSTALL :
return pkg_job_install ( job , name ) ;
case PKG_JOBTYPE_REMOVE :
return pkg_job_remove ( job , name ) ;
case PKG_JOBTYPE_SUM :
return pkg_job_sum ( job , name ) ;
case PKG_JOBTYPE_COMPRESS :
return pkg_job_compress ( job , name ) ;
case PKG_JOBTYPE_DECOMPRESS :
return pkg_job_decompress ( job , name ) ;
default :
return - 1 ;
}
}
const char * pkg_job_typename ( pkg_job_t * job ) {
switch ( job - > jobtype ) {
case PKG_JOBTYPE_BUILD :
return " build " ;
case PKG_JOBTYPE_INSTALL :
return " install " ;
case PKG_JOBTYPE_REMOVE :
return " remove " ;
case PKG_JOBTYPE_SUM :
return " sum " ;
case PKG_JOBTYPE_COMPRESS :
return " compress " ;
case PKG_JOBTYPE_DECOMPRESS :
return " decompress " ;
default :
return - 1 ;
}
}
int pkg_job_run ( pkg_job_t * job ) {
int i ;
printf ( " Running job type '%s' with %d entries \n " , pkg_job_typename ( job ) , job - > nnames ) ;
for ( i = 0 ; i < job - > nnames ; i + + ) {
int subresult = pkg_job_subrun ( job , job - > names [ i ] ) ;
if ( subresult ! = 0 ) {
fprintf ( stderr , " JOB FAILED! \n " ) ;
return subresult ;
}
}
fprintf ( stderr , " JOB DONE. \n " ) ;
return 0 ;
}
void formatinfo ( FILE * out ) {
fprintf ( out , " .pkb (package build file) format: \n " ) ;
fprintf ( out , " The build command runs on package build files (.pkb), creating associated .pkg package files. \n " ) ;
fprintf ( out , " A `.pkb` file must exist in the root of a package source directory or a `pkgs` subdirectory. \n " ) ;
fprintf ( out , " One `.pkb` file exists per package built from a source directory. \n " ) ;
fprintf ( out , " This file lists packageable files, normally placed in a 'dist' subdirectory. \n " ) ;
fprintf ( out , " A single `.pkb` file contains one such line for every installed file. \n " ) ;
fprintf ( out , " Some lines are versioning or entries for directories, plain files can just be listed. \n \n " ) ;
fprintf ( out , " .pkg (package file) format: \n " ) ;
fprintf ( out , " Package files use a custom format (not zip or tar) making management seamless. \n " ) ;
fprintf ( out , " Full headers & checksums are placed at the start of the package file. \n " ) ;
fprintf ( out , " Data for each installable file follows and can be either raw or compressed (unfinished/testing). \n \n " ) ;
fprintf ( out , " .cv1 (compression version 1) format: \n " ) ;
fprintf ( out , " This is a simple stream-of-blocks style compression format with per-block integrity checking. \n " ) ;
fprintf ( out , " These files are used to compress individual files, but the format itself may be used elsewhere. \n \n " ) ;
fprintf ( out , " .pkh (package header) format: \n " ) ;
fprintf ( out , " This is the same as the `.pkg` format except only the headers are used to track installed files. \n " ) ;
fprintf ( out , " These files are kept automatically by the package manager for it's internal database. \n \n " ) ;
fprintf ( out , " pkglist.txt format: \n " ) ;
fprintf ( out , " This is just a list of possibly-valid packages which might be installed on the system. \n " ) ;
fprintf ( out , " A package is considered installed if it's listed here AND has a valid .pkh file. \n " ) ;
fprintf ( out , " These files are kept automatically by the package manager for it's internal database. \n \n " ) ;
}
int usage ( int argc , char * * argv , int argi , char * error ) {
FILE * out = error ? stderr : stdout ;
char * prgname = argv [ 0 ] ;
fprintf ( out , " USAGE: \n \n " ) ;
fprintf ( out , " %s build <project1.pkb> [<project2.pkb>...] \n ^ to build .pkg files from .pkb files in a source/build directory \n \n " , prgname ) ;
fprintf ( out , " %s [options] install <package1.pkg> [<package2.pkg>...] \n ^ to install one or more .pkg files \n \n " , prgname ) ;
fprintf ( out , " %s [options] remove <pkgname> [<pkgname2>...] \n ^ to un-install one or more packages \n \n " , prgname ) ;
fprintf ( out , " %s sum <file1> [<file2>...] \n ^ to print checksum and size info of regular files \n \n " , prgname ) ;
fprintf ( out , " %s compress <file1> [<file2>...] \n ^ writes compressed version of file with .cv1 extension added \n \n " , prgname ) ;
fprintf ( out , " %s decompress <file1.cv1> [<file2.cv1>...] \n ^ writes decompressed version of file with .cv1 extension removed \n \n " , prgname ) ;
fprintf ( out , " %s help \n ^ displays this usage information \n \n " , prgname ) ;
fprintf ( out , " OPTIONS: \n \n " ) ;
fprintf ( out , " --root <installdir> \n ^ installs/manages packages inside a directory (uses it as a root directory) \n \n " ) ;
fprintf ( out , " --dbroot <configdir> \n ^ keeps database of installed packages in this directory, defaults to <installdir>/.pkgs/ \n \n " ) ;
fprintf ( out , " --nodb \n ^ doesn't use a database of installed packages \n \n " ) ;
formatinfo ( out ) ;
fprintf ( out , " DEMO INSTRUCTIONS: \n \n " ) ;
2025-06-08 05:51:07 +00:00
fprintf ( out , " 1. compile slabpkg.c \n 2. place the executable in a subdirectory named `dist` \n 3. create a file `slabpkg.pkb` with a line `/slabpkg` (executable path within `dist`) \n 4. run `./dist/slabpkg build slabpkg.pkb` \n 5. work out how to install it (try --root ... install) \n \n " ) ;
2025-06-03 09:11:48 +00:00
if ( error ) {
fprintf ( out , " ERROR: \n %s \n " , error ) ;
return - 1 ;
} else {
return 0 ;
}
}
int main ( int argc , char * * argv ) {
pkg_job_t job ;
int argi = 1 ;
2025-06-08 05:52:59 +00:00
printf ( " SecureLang Application Binary Package Manager 0.0.2 \n This currently doesn't include proper versioning or dependencies. \n " ) ;
2025-06-03 09:11:48 +00:00
if ( argc < 2 ) {
return usage ( argc , argv , argi , " Expected command (build, install, ...) " ) ;
}
job . installroot = NULL ;
job . dbroot = NULL ;
job . usedb = 1 ;
job . verbose = 0 ;
job . target = " / " ;
int foundoption ;
do {
foundoption = 0 ;
if ( ! strcmp ( argv [ argi ] , " --root " ) ) {
argi + + ;
if ( argi > = argc ) {
return usage ( argc , argv , argi , " Expected a directory name following the --root option. " ) ;
}
2025-06-08 18:09:03 +10:00
if ( strlen ( argv [ argi ] ) = = 0 | | ( argv [ argi ] [ strlen ( argv [ argi ] ) - 1 ] ! = ' / ' & & argv [ argi ] [ strlen ( argv [ argi ] ) - 1 ] ! = ' \\ ' ) ) {
job . installroot = pkg_ezstrcat ( argv [ argi ] , " / " ) ;
} else {
job . installroot = strdup ( argv [ argi ] ) ;
}
2025-06-03 09:11:48 +00:00
argi + + ;
} else if ( ! strcmp ( argv [ argi ] , " --dbroot " ) ) {
argi + + ;
if ( argi > = argc ) {
return usage ( argc , argv , argi , " Expected a directory name following the --dbroot option. " ) ;
}
job . installroot = strdup ( argv [ argi ] ) ;
argi + + ;
} else if ( ! strcmp ( argv [ argi ] , " --nodb " ) ) {
argi + + ;
job . usedb = 0 ;
}
} while ( foundoption ) ;
if ( job . installroot = = NULL ) {
job . installroot = strdup ( " / " ) ;
}
if ( job . usedb & & job . dbroot = = NULL ) {
job . dbroot = pkg_ezstrcat ( job . installroot , " .pkgs/ " ) ;
}
while ( job . installroot [ strlen ( job . installroot ) - 1 ] = = ' / ' | | job . installroot [ strlen ( job . installroot ) - 1 ] = = ' \\ ' ) {
job . installroot [ strlen ( job . installroot ) - 1 ] = 0 ;
}
if ( ! strcmp ( argv [ argi ] , " build " ) ) {
argi + + ;
if ( argi > = argc ) {
return usage ( argc , argv , argi , " Expected one or more package build files (.pkb) for build command . \ n " ) ;
}
job . names = argv + argi ;
job . nnames = argc - argi ;
job . jobtype = PKG_JOBTYPE_BUILD ;
return pkg_job_run ( & job ) ;
} else if ( ! strcmp ( argv [ argi ] , " install " ) ) {
argi + + ;
if ( argi > = argc ) {
return usage ( argc , argv , argi , " Expected one or more package files (.pkg) for install command . \ n " ) ;
}
job . names = argv + argi ;
job . nnames = argc - argi ;
job . jobtype = PKG_JOBTYPE_INSTALL ;
return pkg_job_run ( & job ) ;
} else if ( ! strcmp ( argv [ argi ] , " remove " ) ) {
argi + + ;
if ( argi > = argc ) {
return usage ( argc , argv , argi , " Expected one or more package names for remove command. \n " ) ;
}
job . names = argv + argi ;
job . nnames = argc - argi ;
job . jobtype = PKG_JOBTYPE_REMOVE ;
return pkg_job_run ( & job ) ;
} else if ( ! strcmp ( argv [ argi ] , " sum " ) ) {
argi + + ;
if ( argi > = argc ) {
return usage ( argc , argv , argi , " Expected one or more files for sum command. \n " ) ;
}
job . names = argv + argi ;
job . nnames = argc - argi ;
job . jobtype = PKG_JOBTYPE_SUM ;
return pkg_job_run ( & job ) ;
} else if ( ! strcmp ( argv [ argi ] , " compress " ) ) {
argi + + ;
if ( argi > = argc ) {
return usage ( argc , argv , argi , " Expected one or more files for compress command. \n " ) ;
}
job . names = argv + argi ;
job . nnames = argc - argi ;
job . jobtype = PKG_JOBTYPE_COMPRESS ;
return pkg_job_run ( & job ) ;
} else if ( ! strcmp ( argv [ argi ] , " decompress " ) ) {
argi + + ;
if ( argi > = argc ) {
return usage ( argc , argv , argi , " Expected one or more compressed files (.cv1) for decompress command . \ n " ) ;
}
job . names = argv + argi ;
job . nnames = argc - argi ;
job . jobtype = PKG_JOBTYPE_DECOMPRESS ;
return pkg_job_run ( & job ) ;
} else if ( ! strcmp ( argv [ argi ] , " help " ) ) {
return usage ( argc , argv , argi , NULL ) ;
} else {
return usage ( argc , argv , argi , " Unrecognised command " ) ;
}
2025-06-08 18:09:03 +10:00
}