/* Convert a UTF-8 string to an ANSI string, also attempting to get the MS-DOS
   ShortFileName if the string is a filename. ShortFileName allows Python 2.7 to
   accept filenames which cannot encode in the current ANSI codepage.

   Preserves the filename's original basename, since the bootloader code depends on
   the unmodified basename. Assumes that the basename can be encoded using the current
   ANSI codepage.

   This is a workaround for <https://github.com/pyinstaller/pyinstaller/issues/298>.

   Copies the converted string to `dest`, which must be a buffer
   of at least PATH_MAX characters. Returns 'dest' if successful.

   Returns NULL and logs error reason if encoding fails.
 */
char * pyi_win32_utf8_to_mbs_sfn_keep_basename(char * dest, const char * src) {
    char * mbs_buffer;
    char * mbs_sfn_buffer;
    char basename[PATH_MAX];
    char dirname[PATH_MAX];

    /* Convert path to mbs*/
    mbs_buffer = pyi_win32_utf8_to_mbs(NULL, src, 0);
    if(NULL == mbs_buffer) {
        return NULL;
    }

    /* Convert path again to mbs, this time with SFN */
    mbs_sfn_buffer = pyi_win32_utf8_to_mbs_sfn(NULL, src, 0);
    if(NULL == mbs_sfn_buffer) {
        free(mbs_buffer);
        return NULL;
    }

    pyi_path_basename(basename, mbs_buffer);
    pyi_path_dirname(dirname, mbs_sfn_buffer);
    pyi_path_join(dest, dirname, basename);
    free(mbs_buffer);
    free(mbs_sfn_buffer);
    return dest;
}
Beispiel #2
0
/*
 * Return absolute path to homepath. It is the directory containing executable.
 */
void
pyi_path_homepath(char *homepath, const char *thisfile)
{
    /* Fill in here (directory of thisfile). */
    pyi_path_dirname(homepath, thisfile);
    VS("LOADER: homepath is %s\n", homepath);
}
/* Decide if the dependency identified by item is in a onedir or onfile archive
 * then call the appropriate function.
 */
static int _extract_dependency(ARCHIVE_STATUS *archive_pool[], const char *item)
{
    ARCHIVE_STATUS *status = NULL;
    ARCHIVE_STATUS *archive_status = archive_pool[0];
    char path[PATH_MAX];
    char filename[PATH_MAX];
    char srcpath[PATH_MAX];
    char archive_path[PATH_MAX];

    char dirname[PATH_MAX];

    VS("LOADER: Extracting dependencies\n");
    if (splitName(path, filename, item) == -1)
        return -1;

    pyi_path_dirname(dirname, path);

    /* We need to identify three situations: 1) dependecies are in a onedir archive
     * next to the current onefile archive, 2) dependencies are in a onedir/onefile
     * archive next to the current onedir archive, 3) dependencies are in a onefile
     * archive next to the current onefile archive.
     */
    VS("LOADER: Checking if file exists\n");
    // TODO implement pyi_path_join to accept variable length of arguments for this case.
    if (checkFile(srcpath, "%s%s%s%s%s", archive_status->homepath, PYI_SEPSTR, dirname, PYI_SEPSTR, filename) == 0) {
        VS("LOADER: File %s found, assuming is onedir\n", srcpath);
        if (copyDependencyFromDir(archive_status, srcpath, filename) == -1) {
            FATALERROR("Error coping %s\n", filename);
            return -1;
        }
    // TODO implement pyi_path_join to accept variable length of arguments for this case.
    } else if (checkFile(srcpath, "%s%s%s%s%s%s%s", archive_status->homepath, PYI_SEPSTR, "..", PYI_SEPSTR, dirname, PYI_SEPSTR, filename) == 0) {
        VS("LOADER: File %s found, assuming is onedir\n", srcpath);
        if (copyDependencyFromDir(archive_status, srcpath, filename) == -1) {
            FATALERROR("Error coping %s\n", filename);
            return -1;
        }
    } else {
        VS("LOADER: File %s not found, assuming is onefile.\n", srcpath);
        // TODO implement pyi_path_join to accept variable length of arguments for this case.
        if ((checkFile(archive_path, "%s%s%s.pkg", archive_status->homepath, PYI_SEPSTR, path) != 0) &&
            (checkFile(archive_path, "%s%s%s.exe", archive_status->homepath, PYI_SEPSTR, path) != 0) &&
            (checkFile(archive_path, "%s%s%s", archive_status->homepath, PYI_SEPSTR, path) != 0)) {
            FATALERROR("Archive not found: %s\n", archive_path);
            return -1;
        }

        if ((status = _get_archive(archive_pool, archive_path)) == NULL) {
            FATALERROR("Archive not found: %s\n", archive_path);
            return -1;
        }
        if (extractDependencyFromArchive(status, filename) == -1) {
            FATALERROR("Error extracting %s\n", filename);
            free(status);
            return -1;
        }
    }

    return 0;
}
Beispiel #4
0
/*
 * Return full path to the current executable.
 * Executable is the .exe created by pyinstaller: path/myappname.exe
 *
 * execfile - buffer where to put path to executable.
 * appname - usually the item argv[0].
 */
int pyi_path_executable(char *execfile, const char *appname)
{
    char buffer[PATH_MAX];

#ifdef WIN32
    char dos83_buffer[PATH_MAX];
    stb__wchar wchar_buffer[PATH_MAX];
    stb__wchar wchar_dos83_buffer[PATH_MAX];
    char basename[PATH_MAX];
    char dirname[PATH_MAX];

    /* Windows has special function to obtain path to executable.
     * We EXPLICTLY use wide-string API in the bootloader because
     * otherwise it is impossible to represent non-ASCII character
     * from a different character set. For instance, let's say I have
     * a Latin-1 (Europe Windows), and I want to run a PyInstaller
     * program in a path with japanese character; there is no way to
     * represent that path with ANSI API in Windows, because ANSI API
     * would only support the local charset (Latin-1).
     */
	if (!GetModuleFileNameW(NULL, wchar_buffer, PATH_MAX)) {
		FATALERROR("System error - unable to load!");
		return -1;
	}
    /* Convert wchar_t to utf8. Just use type char as usual. */
    stb_to_utf8(buffer, wchar_buffer, PATH_MAX);

    /*
     * Use 8.3 filename (dos 8.3 or short filename)
     * to overcome the Python and PyInstaller limitation
     * to run with foreign characters in directory names.
     *
     * If 8.3 filename does not exist, original vaule is just copied
     * to the supplied buffer. 8.3 filename might not be available
     * for some networking file systems.
     *
     * This is workaround for <http://www.pyinstaller.org/ticket/298>.
     */
    GetShortPathNameW(wchar_buffer, wchar_dos83_buffer, PATH_MAX);
    /* Convert wchar_t to utf8 just use char as usual. */
    stb_to_utf8(dos83_buffer, wchar_dos83_buffer, PATH_MAX);

    /*
     * Construct proper execfile -  83_DIRNAME + full_basename.
     * GetShortPathName() makes also the basename (appname.exe) shorter.
     *
     * However, bootloader code depends on unmodified basename.
     * Using basename from original path should fix this.
     * It is supposed that basename does not contain any foreign characters.
     *
     * Reuse 'buffer' variable.
     */
    pyi_path_basename(basename, buffer);
    pyi_path_dirname(dirname, dos83_buffer);
    pyi_path_join(buffer, dirname, basename);

#elif __APPLE__
    uint32_t length = sizeof(buffer);

    /* Mac OS X has special function to obtain path to executable. */
    if (_NSGetExecutablePath(buffer, &length) != 0) {
        FATALERROR("System error - unable to load!");
		return -1;
    }
#else
    /* Fill in thisfile. */
    #ifdef __CYGWIN__
    if (strncasecmp(&appname[strlen(appname)-4], ".exe", 4)) {
        strcpy(execfile, appname);
        strcat(execfile, ".exe");
        PI_SetProgramName(execfile);
    }
    else
    #endif /* __CYGWIN__ */
    PI_SetProgramName(appname);
    strcpy(buffer, PI_GetProgramFullPath());
#endif
    /*
     * Ensure path to executable is absolute.
     * 'execfile' starting with ./ might break some modules when changing
     * the CWD.From 'execfile' is constructed 'homepath' and homepath is used
     * for LD_LIBRARY_PATH variavle. Relative LD_LIBRARY_PATH is a security
     * problem.
     */
    if(stb_fullpath(execfile, PATH_MAX, buffer) == false) {
        VS("LOADER: executable is %s\n", execfile);
        return -1;
    }
 
    VS("LOADER: executable is %s\n", execfile);

	return 0;
}