Calibre Portable: Create an exe installer

This commit is contained in:
Kovid Goyal 2012-09-16 13:36:39 +05:30
parent 2112ee3e17
commit e5e5da661e
9 changed files with 5374 additions and 7 deletions

BIN
icons/install.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -141,7 +141,7 @@ class GoogleCode(Base):# {{{
# The pattern to match filenames for the files being uploaded and
# extract version information from them. Must have a named group
# named version
filename_pattern=r'{appname}-(?:portable-)?(?P<version>.+?)(?:-(?:i686|x86_64|32bit|64bit))?\.(?:zip|exe|msi|dmg|tar\.bz2|tar\.xz|txz|tbz2)'
filename_pattern=r'{appname}-(?:portable-installer-)?(?P<version>.+?)(?:-(?:i686|x86_64|32bit|64bit))?\.(?:zip|exe|msi|dmg|tar\.bz2|tar\.xz|txz|tbz2)'
):
self.username, self.password, = username, password

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,382 @@
// XUnzip.h Version 1.3
//
// Authors: Mark Adler et al. (see below)
//
// Modified by: Lucian Wischik
// lu@wischik.com
//
// Version 1.0 - Turned C files into just a single CPP file
// - Made them compile cleanly as C++ files
// - Gave them simpler APIs
// - Added the ability to zip/unzip directly in memory without
// any intermediate files
//
// Modified by: Hans Dietrich
// hdietrich@gmail.com
//
///////////////////////////////////////////////////////////////////////////////
//
// Lucian Wischik's comments:
// --------------------------
// THIS FILE is almost entirely based upon code by info-zip.
// It has been modified by Lucian Wischik.
// The original code may be found at http://www.info-zip.org
// The original copyright text follows.
//
///////////////////////////////////////////////////////////////////////////////
//
// Original authors' comments:
// ---------------------------
// This is version 2002-Feb-16 of the Info-ZIP copyright and license. The
// definitive version of this document should be available at
// ftp://ftp.info-zip.org/pub/infozip/license.html indefinitely.
//
// Copyright (c) 1990-2002 Info-ZIP. All rights reserved.
//
// For the purposes of this copyright and license, "Info-ZIP" is defined as
// the following set of individuals:
//
// Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois,
// Jean-loup Gailly, Hunter Goatley, Ian Gorman, Chris Herborth, Dirk Haase,
// Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz,
// David Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko,
// Steve P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs,
// Kai Uwe Rommel, Steve Salisbury, Dave Smith, Christian Spieler,
// Antoine Verheijen, Paul von Behren, Rich Wales, Mike White
//
// This software is provided "as is", without warranty of any kind, express
// or implied. In no event shall Info-ZIP or its contributors be held liable
// for any direct, indirect, incidental, special or consequential damages
// arising out of the use of or inability to use this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. Redistributions of source code must retain the above copyright notice,
// definition, disclaimer, and this list of conditions.
//
// 2. Redistributions in binary form (compiled executables) must reproduce
// the above copyright notice, definition, disclaimer, and this list of
// conditions in documentation and/or other materials provided with the
// distribution. The sole exception to this condition is redistribution
// of a standard UnZipSFX binary as part of a self-extracting archive;
// that is permitted without inclusion of this license, as long as the
// normal UnZipSFX banner has not been removed from the binary or disabled.
//
// 3. Altered versions--including, but not limited to, ports to new
// operating systems, existing ports with new graphical interfaces, and
// dynamic, shared, or static library versions--must be plainly marked
// as such and must not be misrepresented as being the original source.
// Such altered versions also must not be misrepresented as being
// Info-ZIP releases--including, but not limited to, labeling of the
// altered versions with the names "Info-ZIP" (or any variation thereof,
// including, but not limited to, different capitalizations),
// "Pocket UnZip", "WiZ" or "MacZip" without the explicit permission of
// Info-ZIP. Such altered versions are further prohibited from
// misrepresentative use of the Zip-Bugs or Info-ZIP e-mail addresses or
// of the Info-ZIP URL(s).
//
// 4. Info-ZIP retains the right to use the names "Info-ZIP", "Zip", "UnZip",
// "UnZipSFX", "WiZ", "Pocket UnZip", "Pocket Zip", and "MacZip" for its
// own source and binary releases.
//
///////////////////////////////////////////////////////////////////////////////
#ifndef XUNZIP_H
#define XUNZIP_H
#ifndef XZIP_H
DECLARE_HANDLE(HZIP); // An HZIP identifies a zip file that has been opened
#endif
typedef DWORD ZRESULT;
// return codes from any of the zip functions. Listed later.
#define ZIP_HANDLE 1
#define ZIP_FILENAME 2
#define ZIP_MEMORY 3
typedef struct
{ int index; // index of this file within the zip
char name[MAX_PATH]; // filename within the zip
DWORD attr; // attributes, as in GetFileAttributes.
FILETIME atime,ctime,mtime;// access, create, modify filetimes
long comp_size; // sizes of item, compressed and uncompressed. These
long unc_size; // may be -1 if not yet known (e.g. being streamed in)
} ZIPENTRY;
typedef struct
{ int index; // index of this file within the zip
TCHAR name[MAX_PATH]; // filename within the zip
DWORD attr; // attributes, as in GetFileAttributes.
FILETIME atime,ctime,mtime;// access, create, modify filetimes
long comp_size; // sizes of item, compressed and uncompressed. These
long unc_size; // may be -1 if not yet known (e.g. being streamed in)
} ZIPENTRYW;
///////////////////////////////////////////////////////////////////////////////
//
// OpenZip()
//
// Purpose: Open an existing zip archive file
//
// Parameters: z - archive file name if flags is ZIP_FILENAME; for other
// uses see below
// len - for memory (ZIP_MEMORY) should be the buffer size;
// for other uses, should be 0
// flags - indicates usage, see below; for files, this will be
// ZIP_FILENAME
//
// Returns: HZIP - non-zero if zip archive opened ok, otherwise 0
//
HZIP OpenZip(void *z, unsigned int len, DWORD flags);
// OpenZip - opens a zip file and returns a handle with which you can
// subsequently examine its contents. You can open a zip file from:
// from a pipe: OpenZip(hpipe_read,0, ZIP_HANDLE);
// from a file (by handle): OpenZip(hfile,0, ZIP_HANDLE);
// from a file (by name): OpenZip("c:\\test.zip",0, ZIP_FILENAME);
// from a memory block: OpenZip(bufstart, buflen, ZIP_MEMORY);
// If the file is opened through a pipe, then items may only be
// accessed in increasing order, and an item may only be unzipped once,
// although GetZipItem can be called immediately before and after unzipping
// it. If it's opened i n any other way, then full random access is possible.
// Note: pipe input is not yet implemented.
///////////////////////////////////////////////////////////////////////////////
//
// GetZipItem()
//
// Purpose: Get information about an item in an open zip archive
//
// Parameters: hz - handle of open zip archive
// index - index number (0 based) of item in zip
// ze - pointer to a ZIPENTRY (if ANSI) or ZIPENTRYW struct
// (if Unicode)
//
// Returns: ZRESULT - ZR_OK if success, otherwise some other value
//
#ifdef _UNICODE
#define GetZipItem GetZipItemW
#else
#define GetZipItem GetZipItemA
#endif
ZRESULT GetZipItemA(HZIP hz, int index, ZIPENTRY *ze);
ZRESULT GetZipItemW(HZIP hz, int index, ZIPENTRYW *ze);
// GetZipItem - call this to get information about an item in the zip.
// If index is -1 and the file wasn't opened through a pipe,
// then it returns information about the whole zipfile
// (and in particular ze.index returns the number of index items).
// Note: the item might be a directory (ze.attr & FILE_ATTRIBUTE_DIRECTORY)
// See below for notes on what happens when you unzip such an item.
// Note: if you are opening the zip through a pipe, then random access
// is not possible and GetZipItem(-1) fails and you can't discover the number
// of items except by calling GetZipItem on each one of them in turn,
// starting at 0, until eventually the call fails. Also, in the event that
// you are opening through a pipe and the zip was itself created into a pipe,
// then then comp_size and sometimes unc_size as well may not be known until
// after the item has been unzipped.
///////////////////////////////////////////////////////////////////////////////
//
// FindZipItem()
//
// Purpose: Find item by name and return information about it
//
// Parameters: hz - handle of open zip archive
// name - name of file to look for inside zip archive
// ic - TRUE = case insensitive
// index - pointer to index number returned, or -1
// ze - pointer to a ZIPENTRY (if ANSI) or ZIPENTRYW struct
// (if Unicode)
//
// Returns: ZRESULT - ZR_OK if success, otherwise some other value
//
#ifdef _UNICODE
#define FindZipItem FindZipItemW
#else
#define FindZipItem FindZipItemA
#endif
ZRESULT FindZipItemA(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRY *ze);
ZRESULT FindZipItemW(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRYW *ze);
// FindZipItem - finds an item by name. ic means 'insensitive to case'.
// It returns the index of the item, and returns information about it.
// If nothing was found, then index is set to -1 and the function returns
// an error code.
///////////////////////////////////////////////////////////////////////////////
//
// UnzipItem()
//
// Purpose: Find item by index and unzip it
//
// Parameters: hz - handle of open zip archive
// index - index number of file to unzip
// dst - target file name of unzipped file
// len - for memory (ZIP_MEMORY. length of buffer;
// otherwise 0
// flags - indicates usage, see below; for files, this will be
// ZIP_FILENAME
//
// Returns: ZRESULT - ZR_OK if success, otherwise some other value
//
ZRESULT UnzipItem(HZIP hz, int index, void *dst, unsigned int len, DWORD flags);
// UnzipItem - given an index to an item, unzips it. You can unzip to:
// to a pipe: UnzipItem(hz,i, hpipe_write,0,ZIP_HANDLE);
// to a file (by handle): UnzipItem(hz,i, hfile,0,ZIP_HANDLE);
// to a file (by name): UnzipItem(hz,i, ze.name,0,ZIP_FILENAME);
// to a memory block: UnzipItem(hz,i, buf,buflen,ZIP_MEMORY);
// In the final case, if the buffer isn't large enough to hold it all,
// then the return code indicates that more is yet to come. If it was
// large enough, and you want to know precisely how big, GetZipItem.
// Note: zip files are normally stored with relative pathnames. If you
// unzip with ZIP_FILENAME a relative pathname then the item gets created
// relative to the current directory - it first ensures that all necessary
// subdirectories have been created. Also, the item may itself be a directory.
// If you unzip a directory with ZIP_FILENAME, then the directory gets created.
// If you unzip it to a handle or a memory block, then nothing gets created
// and it emits 0 bytes.
///////////////////////////////////////////////////////////////////////////////
//
// CloseZip()
//
// Purpose: Close an open zip archive
//
// Parameters: hz - handle to an open zip archive
//
// Returns: ZRESULT - ZR_OK if success, otherwise some other value
//
ZRESULT CloseZip(HZIP hz);
// CloseZip - the zip handle must be closed with this function.
unsigned int FormatZipMessage(ZRESULT code, char *buf,unsigned int len);
// FormatZipMessage - given an error code, formats it as a string.
// It returns the length of the error message. If buf/len points
// to a real buffer, then it also writes as much as possible into there.
// These are the result codes:
#define ZR_OK 0x00000000 // nb. the pseudo-code zr-recent is never returned,
#define ZR_RECENT 0x00000001 // but can be passed to FormatZipMessage.
// The following come from general system stuff (e.g. files not openable)
#define ZR_GENMASK 0x0000FF00
#define ZR_NODUPH 0x00000100 // couldn't duplicate the handle
#define ZR_NOFILE 0x00000200 // couldn't create/open the file
#define ZR_NOALLOC 0x00000300 // failed to allocate some resource
#define ZR_WRITE 0x00000400 // a general error writing to the file
#define ZR_NOTFOUND 0x00000500 // couldn't find that file in the zip
#define ZR_MORE 0x00000600 // there's still more data to be unzipped
#define ZR_CORRUPT 0x00000700 // the zipfile is corrupt or not a zipfile
#define ZR_READ 0x00000800 // a general error reading the file
// The following come from mistakes on the part of the caller
#define ZR_CALLERMASK 0x00FF0000
#define ZR_ARGS 0x00010000 // general mistake with the arguments
#define ZR_NOTMMAP 0x00020000 // tried to ZipGetMemory, but that only works on mmap zipfiles, which yours wasn't
#define ZR_MEMSIZE 0x00030000 // the memory size is too small
#define ZR_FAILED 0x00040000 // the thing was already failed when you called this function
#define ZR_ENDED 0x00050000 // the zip creation has already been closed
#define ZR_MISSIZE 0x00060000 // the indicated input file size turned out mistaken
#define ZR_PARTIALUNZ 0x00070000 // the file had already been partially unzipped
#define ZR_ZMODE 0x00080000 // tried to mix creating/opening a zip
// The following come from bugs within the zip library itself
#define ZR_BUGMASK 0xFF000000
#define ZR_NOTINITED 0x01000000 // initialisation didn't work
#define ZR_SEEK 0x02000000 // trying to seek in an unseekable file
#define ZR_NOCHANGE 0x04000000 // changed its mind on storage, but not allowed
#define ZR_FLATE 0x05000000 // an internal error in the de/inflation code
// e.g.
//
// SetCurrentDirectory("c:\\docs\\stuff");
// HZIP hz = OpenZip("c:\\stuff.zip",0,ZIP_FILENAME);
// ZIPENTRY ze; GetZipItem(hz,-1,&ze); int numitems=ze.index;
// for (int i=0; i<numitems; i++)
// { GetZipItem(hz,i,&ze);
// UnzipItem(hz,i,ze.name,0,ZIP_FILENAME);
// }
// CloseZip(hz);
//
//
// HRSRC hrsrc = FindResource(hInstance,MAKEINTRESOURCE(1),RT_RCDATA);
// HANDLE hglob = LoadResource(hInstance,hrsrc);
// void *zipbuf=LockResource(hglob);
// unsigned int ziplen=SizeofResource(hInstance,hrsrc);
// HZIP hz = OpenZip(zipbuf, ziplen, ZIP_MEMORY);
// - unzip to a membuffer -
// ZIPENTRY ze; int i; FindZipItem(hz,"file.dat",&i,&ze);
// char *ibuf = new char[ze.unc_size];
// UnzipItem(hz,i, ibuf, ze.unc_size,ZIP_MEMORY);
// delete[] buf;
// - unzip to a fixed membuff -
// ZIPENTRY ze; int i; FindZipItem(hz,"file.dat",&i,&ze);
// char ibuf[1024]; ZIPRESULT zr=ZR_MORE; unsigned long totsize=0;
// while (zr==ZR_MORE)
// { zr = UnzipItem(hz,i, ibuf,1024,ZIP_MEMORY);
// unsigned long bufsize=1024; if (zr==ZR_OK) bufsize=ze.unc_size-totsize;
// totsize+=bufsize;
// }
// - unzip to a pipe -
// HANDLE hthread=CreateWavReaderThread(&hread,&hwrite);
// FindZipItem(hz,"sound.wav",&i,&ze);
// UnzipItem(hz,i, hwrite,0,ZIP_HANDLE);
// CloseHandle(hwrite);
// WaitForSingleObject(hthread,INFINITE);
// CloseHandle(hread); CloseHandle(hthread);
// - finished -
// CloseZip(hz);
// // note: no need to free resources obtained through Find/Load/LockResource
//
//
// SetCurrentDirectory("c:\\docs\\pipedzipstuff");
// HANDLE hread,hwrite; CreatePipe(&hread,&hwrite);
// CreateZipWriterThread(hwrite);
// HZIP hz = OpenZip(hread,0,ZIP_HANDLE);
// for (int i=0; ; i++)
// { ZIPENTRY ze; ZRESULT res = GetZipItem(hz,i,&ze);
// if (res!=ZE_OK) break; // no more
// UnzipItem(hz,i, ze.name,0,ZIP_FILENAME);
// }
// CloseZip(hz);
//
// Now we indulge in a little skullduggery so that the code works whether
// the user has included just zip or both zip and unzip.
// Idea: if header files for both zip and unzip are present, then presumably
// the cpp files for zip and unzip are both present, so we will call
// one or the other of them based on a dynamic choice. If the header file
// for only one is present, then we will bind to that particular one.
HZIP OpenZipU(void *z,unsigned int len,DWORD flags);
ZRESULT CloseZipU(HZIP hz);
unsigned int FormatZipMessageU(ZRESULT code, char *buf,unsigned int len);
bool IsZipHandleU(HZIP hz);
#define OpenZip OpenZipU
#ifdef XZIP_H
#undef CloseZip
#define CloseZip(hz) (IsZipHandleU(hz)?CloseZipU(hz):CloseZipZ(hz))
#else
#define CloseZip CloseZipU
#define FormatZipMessage FormatZipMessageU
#endif
#endif //XUNZIP_H

View File

@ -37,7 +37,7 @@ class Win32(VMInstaller):
SHUTDOWN_CMD = ['shutdown.exe', '-s', '-f', '-t', '0']
def sign_msi(self):
print ('Signing .msi ...')
print ('Signing installers ...')
raw = open(self.VM).read()
vmx = re.search(r'''launch_vmware\(['"](.+?)['"]''', raw).group(1)
subprocess.check_call(['vmrun', '-T', 'ws', '-gu', 'kovid', '-gp',
@ -56,7 +56,7 @@ class Win32(VMInstaller):
self.warn('Failed to freeze')
raise SystemExit(1)
installer = 'dist/%s-portable-%s.zip'%(__appname__, __version__)
installer = 'dist/%s-portable-installer-%s.exe'%(__appname__, __version__)
subprocess.check_call(('scp',
'xp_build:build/%s/%s'%(__appname__, installer), 'dist'))
if not os.path.exists(installer):

View File

@ -23,6 +23,7 @@ SW = r'C:\cygwin\home\kovid\sw'
IMAGEMAGICK = os.path.join(SW, 'build', 'ImageMagick-6.7.6',
'VisualMagick', 'bin')
CRT = r'C:\Microsoft.VC90.CRT'
LZMA = r'Q:\easylzma\build\easylzma-0.0.8'
VERSION = re.sub('[a-z]\d+', '', __version__)
WINVER = VERSION+'.0'
@ -89,6 +90,7 @@ class Win32Freeze(Command, WixMixIn):
self.remove_CRT_from_manifests()
self.create_installer()
self.build_portable()
self.build_portable_installer()
def remove_CRT_from_manifests(self):
'''
@ -311,7 +313,8 @@ class Win32Freeze(Command, WixMixIn):
subprocess.check_call([r'C:\Program Files\7-Zip\7z.exe', 'a', '-r',
'-scsUTF-8', '-sfx', 'winfrozen', 'winfrozen'], cwd=self.base)
def embed_resources(self, module, desc=None):
def embed_resources(self, module, desc=None, extra_data=None,
product_description=None):
icon_base = self.j(self.src_root, 'icons')
icon_map = {'calibre':'library', 'ebook-viewer':'viewer',
'lrfviewer':'viewer', 'calibre-portable':'library'}
@ -320,6 +323,8 @@ class Win32Freeze(Command, WixMixIn):
bname = self.b(module)
internal_name = os.path.splitext(bname)[0]
icon = icon_map.get(internal_name, 'command-prompt')
if internal_name.startswith('calibre-portable-'):
icon = 'install'
icon = self.j(icon_base, icon+'.ico')
if desc is None:
defdesc = 'A dynamic link library' if file_type == 'DLL' else \
@ -327,6 +332,8 @@ class Win32Freeze(Command, WixMixIn):
desc = DESCRIPTIONS.get(internal_name, defdesc)
license = 'GNU GPL v3.0'
def e(val): return val.replace('"', r'\"')
if product_description is None:
product_description = __appname__ + ' - E-book management'
rc = template.format(
icon=icon,
file_type=e(file_type),
@ -338,11 +345,13 @@ class Win32Freeze(Command, WixMixIn):
product_version=e(WINVER.replace('.', ',')),
product_version_str=e(__version__),
product_name=e(__appname__),
product_description=e(__appname__+' - E-book management'),
product_description=e(product_description),
legal_copyright=e(license),
legal_trademarks=e(__appname__ + \
' is a registered U.S. trademark number 3,666,525')
)
if extra_data:
rc += '\nextra extra "%s"'%extra_data
tdir = self.obj_dir
rcf = self.j(tdir, bname+'.rc')
with open(rcf, 'wb') as f:
@ -371,6 +380,45 @@ class Win32Freeze(Command, WixMixIn):
self.info(p.stderr.read())
sys.exit(1)
def build_portable_installer(self):
base = self.portable_base
zf = self.a(self.j('dist', 'calibre-portable-%s.zip.lz'%VERSION))
usz = os.path.getsize(zf)
def cc(src, obj):
cflags = '/c /EHsc /MT /W4 /Ox /nologo /D_UNICODE /DUNICODE'.split()
cflags.append(r'/I%s\include'%LZMA)
cflags.append('/DUNCOMPRESSED_SIZE=%d'%usz)
if self.newer(obj, [src]):
self.info('Compiling', obj)
cmd = [msvc.cc] + cflags + ['/Fo'+obj, src]
self.run_builder(cmd)
src = self.j(self.src_root, 'setup', 'installer', 'windows',
'portable-installer.cpp')
obj = self.j(self.obj_dir, self.b(src)+'.obj')
xsrc = self.j(self.src_root, 'setup', 'installer', 'windows',
'XUnzip.cpp')
xobj = self.j(self.obj_dir, self.b(xsrc)+'.obj')
cc(src, obj)
cc(xsrc, xobj)
exe = self.j('dist', 'calibre-portable-installer-%s.exe'%VERSION)
if self.newer(exe, [obj, xobj]):
self.info('Linking', exe)
cmd = [msvc.linker] + ['/INCREMENTAL:NO', '/MACHINE:X86',
'/LIBPATH:'+self.obj_dir, '/SUBSYSTEM:WINDOWS',
'/LIBPATH:'+(LZMA+r'\lib\Release'),
'/RELEASE',
'/ENTRY:wWinMainCRTStartup',
'/OUT:'+exe, self.embed_resources(exe,
desc='Calibre Portable Installer', extra_data=zf,
product_description='Calibre Portable Installer'),
xobj, obj, 'User32.lib', 'Shell32.lib', 'easylzma_s.lib',
'Ole32.lib', 'Shlwapi.lib', 'Kernel32.lib']
self.run_builder(cmd)
os.remove(zf)
def build_portable(self):
base = self.portable_base
if os.path.exists(base):
@ -403,9 +451,12 @@ class Win32Freeze(Command, WixMixIn):
os.mkdir(self.j(base, 'Calibre Settings'))
name = '%s-portable-%s.zip'%(__appname__, __version__)
with zipfile.ZipFile(self.j('dist', name), 'w', zipfile.ZIP_DEFLATED) as zf:
name = self.j('dist', name)
with zipfile.ZipFile(name, 'w', zipfile.ZIP_STORED) as zf:
self.add_dir_to_zip(zf, base, 'Calibre Portable')
subprocess.check_call([LZMA + r'\bin\elzma.exe', '-9', '--lzip', name])
def add_dir_to_zip(self, zf, path, prefix=''):
'''
Add a directory recursively to the zip file with an optional prefix.

View File

@ -370,6 +370,14 @@ Run
Python setup.py build
cp -r build/lib.win32-*/* /cygdrive/c/Python27/Lib/site-packages/
easylzma
----------
Get it from http://lloyd.github.com/easylzma/ (use the trunk version)
Run cmake and build the Visual Studio solution (generates CLI tools and dll and
static lib automatically)
calibre
---------

View File

@ -0,0 +1,547 @@
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif
#include <Windows.h>
#include <Shlobj.h>
#include <Shlwapi.h>
#include <Shellapi.h>
#include <wchar.h>
#include <stdio.h>
#include <io.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <easylzma/decompress.h>
#include "XUnzip.h"
#define BUFSIZE 4096
// Error handling {{{
static void show_error(LPCWSTR msg) {
MessageBeep(MB_ICONERROR);
MessageBox(NULL, msg, L"Error", MB_OK|MB_ICONERROR);
}
static void show_detailed_error(LPCWSTR preamble, LPCWSTR msg, int code) {
LPWSTR buf;
buf = (LPWSTR)LocalAlloc(LMEM_ZEROINIT, sizeof(WCHAR)*
(wcslen(msg) + wcslen(preamble) + 80));
_snwprintf_s(buf,
LocalSize(buf) / sizeof(WCHAR), _TRUNCATE,
L"%s\r\n %s (Error Code: %d)\r\n",
preamble, msg, code);
show_error(buf);
LocalFree(buf);
}
static void show_zip_error(LPCWSTR preamble, LPCWSTR msg, ZRESULT code) {
LPWSTR buf;
buf = (LPWSTR)LocalAlloc(LMEM_ZEROINIT, sizeof(WCHAR)*
(wcslen(preamble) + wcslen(msg) + 80));
_snwprintf_s(buf,
LocalSize(buf) / sizeof(WCHAR), _TRUNCATE,
L"%s\r\n %s %s (Error Code: %X)\r\n",
preamble, msg, code);
show_error(buf);
LocalFree(buf);
}
static void show_last_error_crt(LPCWSTR preamble) {
WCHAR buf[BUFSIZE];
int err = 0;
_get_errno(&err);
_wcserror_s(buf, BUFSIZE, err);
show_detailed_error(preamble, buf, err);
}
static void show_last_error(LPCWSTR preamble) {
WCHAR *msg = NULL;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPWSTR)&msg,
0, NULL );
show_detailed_error(preamble, msg, (int)dw);
}
// }}}
// Load, decompress and extract data {{{
static BOOL load_data(LPVOID *data, DWORD *sz) {
HRSRC rsrc;
HGLOBAL h;
rsrc = FindResourceW(NULL, L"extra", L"extra");
if (rsrc == NULL) { show_last_error(L"Failed to find portable data in exe"); return false; }
h = LoadResource(NULL, rsrc);
if (h == NULL) { show_last_error(L"Failed to load portable data from exe"); return false; }
*data = LockResource(h);
if (*data == NULL) { show_last_error(L"Failed to lock portable data in exe"); return false; }
*sz = SizeofResource(NULL, rsrc);
if (sz == 0) { show_last_error(L"Failed to get size of portable data in exe"); return false; }
return true;
}
static BOOL unzip(HZIP zipf, int nitems, IProgressDialog *pd) {
int i = 0;
ZRESULT res;
ZIPENTRYW ze;
for (i = 0; i < nitems; i++) {
res = GetZipItem(zipf, i, &ze);
if (res != ZR_OK) { show_zip_error(L"Failed to get zip item", L"", res); return false;}
res = UnzipItem(zipf, i, ze.name, 0, ZIP_FILENAME);
if (res != ZR_OK) { show_zip_error(L"Failed to extract zip item:", ze.name, res); return false;}
pd->SetLine(2, ze.name, true, NULL);
pd->SetProgress(i, nitems);
}
CloseZip(zipf);
return true;
}
static HANDLE temp_file(LPWSTR name) {
UINT res;
HANDLE h;
res = GetTempFileNameW(L".", L"portable_data", 0, name);
if (res == 0) { show_last_error(L"Failed to create temporary file to decompress portable data"); return INVALID_HANDLE_VALUE; }
h = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE) { show_last_error(L"Failed to open temp file t decomress portable data"); }
return h;
}
struct DataStream
{
const unsigned char *in_data;
size_t in_len;
HANDLE out;
IProgressDialog *pd;
};
static int
input_callback(void *ctx, void *buf, size_t * size)
{
size_t rd = 0;
struct DataStream * ds = (struct DataStream *) ctx;
rd = (ds->in_len < *size) ? ds->in_len : *size;
if (rd > 0) {
memcpy(buf, (void*) ds->in_data, rd);
ds->in_data += rd;
ds->in_len -= rd;
}
*size = rd;
return 0;
}
static size_t
output_callback(void *ctx, const void *buf, size_t size)
{
struct DataStream * ds = (struct DataStream *) ctx;
DWORD written = 0;
if (size > 0) {
if (!WriteFile(ds->out, buf, size, &written, NULL)) {
show_last_error(L"Failed to write uncompressed data to temp file");
return 0;
}
written = SetFilePointer(ds->out, 0, NULL, FILE_CURRENT);
ds->pd->SetProgress(written, UNCOMPRESSED_SIZE);
}
return size;
}
static BOOL decompress(LPVOID src, DWORD src_sz, HANDLE out, IProgressDialog *pd) {
elzma_decompress_handle h;
struct DataStream ds;
int rc;
h = elzma_decompress_alloc();
if (h == NULL) { show_error(L"Out of memory"); return false; }
ds.in_data = (unsigned char*)src;
ds.in_len = src_sz;
ds.out = out;
ds.pd = pd;
rc = elzma_decompress_run(h, input_callback, (void *) &ds, output_callback,
(void *) &ds, ELZMA_lzip);
if (rc != ELZMA_E_OK) {
elzma_decompress_free(&h);
show_zip_error(L"Failed to decompress portable data", L"", rc);
return false;
}
elzma_decompress_free(&h);
return true;
}
static BOOL extract(LPVOID cdata, DWORD csz) {
HANDLE h;
WCHAR tempnam[MAX_PATH+1] = {0};
BOOL ret = true;
HZIP zipf;
ZIPENTRYW ze;
ZRESULT res;
int nitems;
HRESULT hr;
IProgressDialog *pd = NULL;
hr = CoCreateInstance(CLSID_ProgressDialog, NULL,
CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pd));
if (FAILED(hr)) { show_error(L"Failed to create progress dialog"); return false; }
pd->SetTitle(L"Extracting Calibre Portable");
pd->SetLine(1, L"Decompressing data...", true, NULL);
h = temp_file(tempnam);
if (h == INVALID_HANDLE_VALUE) return false;
pd->StartProgressDialog(NULL, NULL, PROGDLG_NORMAL | PROGDLG_AUTOTIME | PROGDLG_NOCANCEL, NULL);
if (!decompress(cdata, csz, h, pd)) { ret = false; goto end; }
SetFilePointer(h, 0, NULL, FILE_BEGIN);
zipf = OpenZip(h, 0, ZIP_HANDLE);
if (zipf == 0) { show_last_error(L"Failed to open zipped portable data"); ret = false; goto end; }
res = GetZipItem(zipf, -1, &ze);
if (res != ZR_OK) { show_zip_error(L"Failed to get count of items in portable data", L"", res); ret = false; goto end;}
nitems = ze.index;
pd->SetLine(1, L"Copying files...", true, NULL);
if (!unzip(zipf, nitems, pd)) { ret = false; goto end; }
end:
pd->StopProgressDialog();
pd->Release();
CloseHandle(h);
DeleteFile(tempnam);
return ret;
}
// }}}
// Find calibre portable directory and install/upgrade into it {{{
static BOOL directory_exists( LPCWSTR path )
{
if( _waccess_s( path, 0 ) == 0 )
{
struct _stat status;
_wstat( path, &status );
return (status.st_mode & S_IFDIR) != 0;
}
return FALSE;
}
static BOOL file_exists( LPCWSTR path )
{
if( _waccess_s( path, 0 ) == 0 )
{
struct _stat status;
_wstat( path, &status );
return (status.st_mode & S_IFREG) != 0;
}
return FALSE;
}
static LPWSTR get_directory_from_user() {
WCHAR name[MAX_PATH+1] = {0};
LPWSTR path = NULL;
PIDLIST_ABSOLUTE ret;
path = (LPWSTR)calloc(2*MAX_PATH, sizeof(WCHAR));
if (path == NULL) { show_error(L"Out of memory"); return NULL; }
int image = 0;
BROWSEINFO bi = { NULL, NULL, name,
L"Select the folder where you want to install or update Calibre Portable",
BIF_RETURNONLYFSDIRS | BIF_DONTGOBELOWDOMAIN | BIF_USENEWUI,
NULL, NULL, image };
ret = SHBrowseForFolder(&bi);
if (ret == NULL) {
return NULL;
}
if (!SHGetPathFromIDList(ret, path)) {
show_detailed_error(L"The selected folder is not valid: ", name, 0);
return NULL;
}
return path;
}
static bool is_dots(LPCWSTR name) {
return wcscmp(name, L".") == 0 || wcscmp(name, L"..") == 0;
}
static bool rmtree(LPCWSTR path) {
SHFILEOPSTRUCTW op;
WCHAR buf[4*MAX_PATH + 2] = {0};
if (GetFullPathName(path, 4*MAX_PATH, buf, NULL) == 0) return false;
op.hwnd = NULL;
op.wFunc = FO_DELETE;
op.pFrom = buf;
op.pTo = NULL;
op.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMMKDIR;
op.fAnyOperationsAborted = false;
op.hNameMappings = NULL;
op.lpszProgressTitle = NULL;
return SHFileOperationW(&op) == 0;
}
static BOOL find_portable_dir(LPCWSTR base, LPWSTR *result, BOOL *existing) {
WCHAR buf[4*MAX_PATH] = {0};
_snwprintf_s(buf, 4*MAX_PATH, _TRUNCATE, L"%s\\calibre-portable.exe", base);
*existing = true;
if (file_exists(buf)) {
*result = _wcsdup(base);
if (*result == NULL) { show_error(L"Out of memory"); return false; }
return true;
}
WIN32_FIND_DATA fdFile;
HANDLE hFind = NULL;
_snwprintf_s(buf, 4*MAX_PATH, _TRUNCATE, L"%s\\*", base);
if((hFind = FindFirstFileEx(buf, FindExInfoStandard, &fdFile, FindExSearchLimitToDirectories, NULL, 0)) != INVALID_HANDLE_VALUE) {
do {
if(is_dots(fdFile.cFileName)) continue;
if(fdFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
_snwprintf_s(buf, 4*MAX_PATH, _TRUNCATE, L"%s\\%s\\calibre-portable.exe", base, fdFile.cFileName);
if (file_exists(buf)) {
*result = _wcsdup(buf);
if (*result == NULL) { show_error(L"Out of memory"); return false; }
PathRemoveFileSpec(*result);
FindClose(hFind);
return true;
}
}
} while(FindNextFile(hFind, &fdFile));
FindClose(hFind);
}
*existing = false;
_snwprintf_s(buf, 4*MAX_PATH, _TRUNCATE, L"%s\\Calibre Portable", base);
if (!CreateDirectory(buf, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) {
show_last_error(L"Failed to create Calibre Portable folder");
return false;
}
*result = _wcsdup(buf);
if (*result == NULL) { show_error(L"Out of memory"); return false; }
return true;
}
static LPWSTR make_unpack_dir() {
WCHAR buf[4*MAX_PATH] = {0};
LPWSTR ans = NULL;
if (directory_exists(L"_unpack_calibre_portable"))
rmtree(L"_unpack_calibre_portable");
if (!CreateDirectory(L"_unpack_calibre_portable", NULL) && GetLastError() != ERROR_ALREADY_EXISTS) {
show_last_error(L"Failed to create temporary folder to unpack into");
return ans;
}
if (!GetFullPathName(L"_unpack_calibre_portable", 4*MAX_PATH, buf, NULL)) {
show_last_error(L"Failed to resolve path");
return NULL;
}
ans = _wcsdup(buf);
if (ans == NULL) show_error(L"Out of memory");
return ans;
}
static BOOL move_program() {
if (MoveFileEx(L"Calibre Portable\\calibre-portable.exe",
L"..\\calibre-portable.exe", MOVEFILE_REPLACE_EXISTING) == 0) {
show_last_error(L"Failed to move calibre-portable.exe, make sure calibre is not running");
return false;
}
if (directory_exists(L"..\\Calibre")) {
if (!rmtree(L"..\\Calibre")) {
show_error(L"Failed to delete the Calibre program folder. Make sure calibre is not running.");
return false;
}
}
if (MoveFileEx(L"Calibre Portable\\Calibre", L"..\\Calibre", 0) == 0) {
show_last_error(L"Failed to move calibre program folder");
return false;
}
if (!directory_exists(L"..\\Calibre Library")) {
MoveFileEx(L"Calibre Portable\\Calibre Library", L"..\\Calibre Library", 0);
}
if (!directory_exists(L"..\\Calibre Settings")) {
MoveFileEx(L"Calibre Portable\\Calibre Settings", L"..\\Calibre Settings", 0);
}
return true;
}
// }}}
static void launch_calibre() {
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
if (CreateProcess(_wcsdup(L"calibre-portable.exe"), NULL,
NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_PROCESS_GROUP,
NULL, NULL, &si, &pi)
== 0) {
show_last_error(L"Failed to launch calibre portable");
}
// Close process and thread handles.
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
LPVOID cdata = NULL;
DWORD csz = 0;
int ret = 0, argc;
HRESULT hr;
LPWSTR tgt = NULL, dest = NULL, *argv, unpack_dir = NULL;
BOOL existing = false, launch = false;
WCHAR buf[4*MAX_PATH] = {0}, mb_msg[4*MAX_PATH] = {0}, fdest[4*MAX_PATH] = {0};
if (!load_data(&cdata, &csz)) return 0;
hr = CoInitialize(NULL);
if (FAILED(hr)) { show_error(L"Failed to initialize COM"); return 0; }
// Get the target directory for installation
argv = CommandLineToArgvW(GetCommandLine(), &argc);
if (argv == NULL) { show_last_error(L"Failed to get command line"); return 0; }
if (argc > 1) {
tgt = argv[1];
} else {
tgt = get_directory_from_user();
if (tgt == NULL) goto end;
}
if (!directory_exists(tgt)) {
show_detailed_error(L"The specified directory does not exist: ",
tgt, 1);
goto end;
}
// Ensure the path to Calibre Portable is not too long
do {
if (!find_portable_dir(tgt, &dest, &existing)) goto end;
if (GetFullPathName(dest, MAX_PATH*4, fdest, NULL) == 0) {
show_last_error(L"Failed to resolve target folder");
goto end;
}
free(dest); dest = NULL;
if (wcslen(fdest) > 58) {
_snwprintf_s(buf, 4*MAX_PATH, _TRUNCATE,
L"Path to Calibre Portable (%s) too long. Must be less than 59 characters.", fdest);
if (!existing) RemoveDirectory(fdest);
show_error(buf);
tgt = get_directory_from_user();
if (tgt == NULL) goto end;
}
} while (wcslen(fdest) > 58);
// Confirm the user wants to upgrade
if (existing) {
_snwprintf_s(mb_msg, 4*MAX_PATH, _TRUNCATE,
L"An existing install of Calibre Portable was found at %s. Do you want to upgrade it?",
fdest);
if (MessageBox(NULL, mb_msg,
L"Upgrade Calibre Portable?", MB_ICONEXCLAMATION | MB_YESNO | MB_TOPMOST) != IDYES)
goto end;
}
// Make a temp dir to unpack into
if (!SetCurrentDirectoryW(fdest)) { show_detailed_error(L"Failed to change to unzip directory: ", fdest, 0); goto end; }
if ( (unpack_dir = make_unpack_dir()) == NULL ) goto end;
if (!SetCurrentDirectoryW(unpack_dir)) { show_detailed_error(L"Failed to change to unpack directory: ", fdest, 0); goto end; }
// Extract files
if (!extract(cdata, csz)) goto end;
// Move files from temp dir to the install dir
if (!move_program()) goto end;
SetCurrentDirectoryW(L"..");
_snwprintf_s(mb_msg, 4*MAX_PATH, _TRUNCATE,
L"Calibre Portable successfully installed to %s. Launch calibre?",
fdest);
launch = MessageBox(NULL, mb_msg,
L"Success", MB_ICONINFORMATION | MB_YESNO | MB_TOPMOST) == IDYES;
end:
if (unpack_dir != NULL) { rmtree(unpack_dir); free(unpack_dir); }
CoUninitialize();
if (launch) launch_calibre();
return 0;
}

View File

@ -30,7 +30,7 @@ def installers():
installers = list(map(installer_name, ('dmg', 'msi', 'tar.bz2')))
installers.append(installer_name('tar.bz2', is64bit=True))
installers.insert(0, 'dist/%s-%s.tar.xz'%(__appname__, __version__))
installers.append('dist/%s-portable-%s.zip'%(__appname__, __version__))
installers.append('dist/%s-portable-installer-%s.exe'%(__appname__, __version__))
return installers
def installer_description(fname):