Merge pull request #155 from Blizzard/unzip

add zip.extract(src, dst) method.
This commit is contained in:
starkos 2015-07-31 16:31:17 -04:00
commit 6f2d3b9f18
4 changed files with 301 additions and 12 deletions

View File

@ -5,29 +5,74 @@
*/
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
#include "premake.h"
#if PLATFORM_WINDOWS
#include <direct.h>
#include <errno.h>
#endif
int do_mkdir(const char* path)
{
struct stat sb;
char sub_path[1024];
int i, length;
// if it already exists, return.
if (stat(path, &sb) == 0)
return 1;
// find the parent folder name.
length = strlen(path);
for (i = length - 1; i >= 0; --i)
{
if (path[i] == '/' || path[i] == '\\')
break;
}
// if we found one, create it.
if (i > 0)
{
memcpy(sub_path, path, i);
sub_path[i] = '\0';
#if PLATFORM_WINDOWS
if (sub_path[i - 1] == ':')
{
sub_path[i + 0] = '/';
sub_path[i + 1] = '\0';
}
#endif
if (!do_mkdir(sub_path))
return 0;
}
// now finally create the actual folder we want.
#if PLATFORM_WINDOWS
return _mkdir(path) == 0;
#else
return mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0;
#endif
}
int os_mkdir(lua_State* L)
{
int z;
const char* path = luaL_checkstring(L, 1);
#if PLATFORM_WINDOWS
z = CreateDirectory(path, NULL);
#else
z = (mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0);
#endif
int z = do_mkdir(path);
if (!z)
{
lua_pushnil(L);
lua_pushfstring(L, "unable to create directory '%s'", path);
return 2;
}
else
{
lua_pushboolean(L, 1);
return 1;
}
lua_pushboolean(L, 1);
return 1;
}

View File

@ -94,6 +94,13 @@ static const luaL_Reg http_functions[] = {
};
#endif
#ifdef PREMAKE_COMPRESSION
static const luaL_Reg zip_functions[] = {
{ "extract", zip_extract },
{ NULL, NULL }
};
#endif
/**
* Initialize the Premake Lua environment.
*/
@ -111,6 +118,10 @@ int premake_init(lua_State* L)
luaL_register(L, "http", http_functions);
#endif
#ifdef PREMAKE_COMPRESSION
luaL_register(L, "zip", zip_functions);
#endif
/* push the application metadata */
lua_pushstring(L, LUA_COPYRIGHT);
lua_setglobal(L, "_COPYRIGHT");

View File

@ -127,6 +127,10 @@ int http_get(lua_State* L);
int http_download(lua_State* L);
#endif
#ifdef PREMAKE_COMPRESSION
int zip_extract(lua_State* L);
#endif
/* Engine interface */
int premake_init(lua_State* L);
int premake_execute(lua_State* L, int argc, const char** argv, const char* script);

229
src/host/zip_extract.c Normal file
View File

@ -0,0 +1,229 @@
/**
* \file os_unzip.c
* \brief Unzip file using libzip library.
* \author battle.net -- abrunasso.int@blizzard.com
*/
#include "premake.h"
#ifdef PREMAKE_COMPRESSION
#include <zip.h>
#ifdef WIN32
#include <direct.h>
#include <io.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>
// File Attribute for Unix
#define FA_IFIFO 0010000 /* named pipe (fifo) */
#define FA_IFCHR 0020000 /* character special */
#define FA_IFDIR 0040000 /* directory */
#define FA_IFBLK 0060000 /* block special */
#define FA_IFREG 0100000 /* regular */
#define FA_IFLNK 0120000 /* symbolic link */
#define FA_IFSOCK 0140000 /* socket */
#define FA_ISUID 0004000 /* set user id on execution */
#define FA_ISGID 0002000 /* set group id on execution */
#define FA_ISTXT 0001000 /* sticky bit */
#define FA_IRWXU 0000700 /* RWX mask for owner */
#define FA_IRUSR 0000400 /* R for owner */
#define FA_IWUSR 0000200 /* W for owner */
#define FA_IXUSR 0000100 /* X for owner */
#define FA_IRWXG 0000070 /* RWX mask for group */
#define FA_IRGRP 0000040 /* R for group */
#define FA_IWGRP 0000020 /* W for group */
#define FA_IXGRP 0000010 /* X for group */
#define FA_IRWXO 0000007 /* RWX mask for other */
#define FA_IROTH 0000004 /* R for other */
#define FA_IWOTH 0000002 /* W for other */
#define FA_IXOTH 0000001 /* X for other */
#define FA_ISVTX 0001000 /* save swapped text even after use */
// ----------------------------------------------------------------------------
static int is_symlink(zip_uint8_t opsys, zip_uint32_t attrib)
{
if (opsys == ZIP_OPSYS_DOS)
return (attrib & 0x400) == 0x400; // FILE_ATTRIBUTE_REPARSE_POINT
if (opsys == ZIP_OPSYS_UNIX)
return ((attrib >> 16) & FA_IFLNK) == FA_IFLNK;
return 0;
}
static int is_directory(zip_uint8_t opsys, zip_uint32_t attrib)
{
if (opsys == ZIP_OPSYS_DOS)
return (attrib & 0x10) == 0x10; // FILE_ATTRIBUTE_DIRECTORY
if (opsys == ZIP_OPSYS_UNIX)
return ((attrib >> 16) & FA_IFDIR) == FA_IFDIR;
return 0;
}
static int write_link(const char* filename, const char* bytes, size_t count)
{
#if PLATFORM_POSIX
return symlink(bytes, filename);
#else
FILE* fp = fopen(filename, "wb");
if (fp == NULL)
{
printf("Error creating file:\n %s\n", filename);
return -1;
}
fwrite(bytes, sizeof(char), count, fp);
fclose(fp);
return 0;
#endif
}
extern int do_mkdir(const char* path);
static void parse_path(const char* full_name, char* filename, char* directory)
{
char *ssc;
size_t orig_length = strlen(full_name);
size_t l = 0;
size_t dir_size;
ssc = strstr(full_name, "/");
do
{
l = strlen(ssc) + 1;
full_name = &full_name[strlen(full_name) - l + 2];
ssc = strstr(full_name, "/");
} while (ssc);
// full_name currently pointing to beginning of filename
memcpy(filename, full_name, strlen(full_name));
filename[strlen(full_name)] = 0; // Null terminate it
dir_size = orig_length - strlen(filename);
// full_name points to beginning of original string(with directory)
full_name = &full_name[strlen(full_name) - orig_length];
// Extract directory from full name by removing filename
memcpy(directory, full_name, dir_size);
directory[dir_size] = 0;
}
static int extract(const char* src, const char* destination)
{
int err = 0;
FILE *fp = NULL;
struct zip *z_archive = zip_open(src, 0, &err);
int i;
zip_int64_t entries;
char buffer[4096];
char appended_full_name[512];
char directory[512];
char filename[512];
size_t result;
zip_int64_t bytes_read;
if (!z_archive)
{
printf("%s does not exist\n", src);
return -1;
}
for (i = 0, entries = zip_get_num_entries(z_archive, 0); i < entries; ++i)
{
struct zip_file* zf = zip_fopen_index(z_archive, i, 0);
if (!zf)
continue;
zip_uint8_t opsys;
zip_uint32_t attrib;
zip_file_get_external_attributes(z_archive, i, 0, &opsys, &attrib);
const char* full_name = zip_get_name(z_archive, i, 0);
sprintf(appended_full_name, "%s/%s", destination, full_name);
parse_path(appended_full_name, filename, directory);
do_mkdir(directory);
// is this a symbolic link?
if (is_symlink(opsys, attrib))
{
bytes_read = zip_fread(zf, buffer, sizeof(buffer));
buffer[bytes_read] = '\0';
if (write_link(appended_full_name, buffer, bytes_read) != 0)
{
printf(" Failed to create symbolic link [%s->%s]\n", appended_full_name, buffer);
return -1;
}
} else
{
// If blank filename it's just a directory so create it and move on
if (!is_directory(opsys, attrib) && strlen(filename) > 0)
{
// mark as read-write, so we can overwrite the file if it already exists.
chmod(appended_full_name, 0666);
fp = fopen(appended_full_name, "wb");
if (fp == NULL)
{
printf("Error reading file:\n %s\n", appended_full_name);
return -1;
}
for(;;)
{
// Read content from zipped file
bytes_read = zip_fread(zf, buffer, sizeof(buffer));
if (bytes_read == 0) break;
// Write that content to file
result = fwrite(buffer, sizeof(char), (size_t)bytes_read, fp);
// If all bytes read weren't written, report error
if (result != (size_t)bytes_read)
{
printf(" Writing data to %s failed\n %d bytes were written\n %d bytes were attempted to be written\n File may be corrupt\n",
appended_full_name, (int)result, (int)bytes_read);
return -1;
}
}
fclose(fp);
// mark read-only, but maintain the other properties.
if (opsys == ZIP_OPSYS_UNIX)
chmod(appended_full_name, (attrib >> 16) & ~0222);
else
chmod(appended_full_name, 0444);
}
}
// Cleanup
zip_fclose(zf);
}
zip_close(z_archive);
return 0;
}
int zip_extract(lua_State* L)
{
const char* src = luaL_checkstring(L, 1);
const char* dst = luaL_checkstring(L, 2);
int res = extract(src, dst);
lua_pushnumber(L, res);
return 1;
}
#endif