Skip to content

Commit

Permalink
Allow users to choose image on first launch
Browse files Browse the repository at this point in the history
  • Loading branch information
fniephaus committed Jan 29, 2022
1 parent cf56268 commit c4391a2
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,7 @@ protected void launch(final Context.Builder contextBuilder) {
}

protected int execute(final Context.Builder contextBuilder) {
if (imagePath == null) {
imagePath = SqueakImageLocator.findImage();
}
imagePath = SqueakImageLocator.findImage(imagePath);
if (printImagePath) {
println(imagePath);
return 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,123 @@
*/
package de.hpi.swa.trufflesqueak.shared;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.URL;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Scanner;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import org.graalvm.home.HomeFinder;

public final class SqueakImageLocator {
private static final String[][] SMALLTALK_IMAGES = {
new String[]{"TruffleSqueak image", "https://github.com/hpi-swa/trufflesqueak/releases/download/21.3.0/TruffleSqueakImage-21.3.0.zip"},
new String[]{"TruffleSqueak test image", "https://github.com/hpi-swa/trufflesqueak/releases/download/21.1.0/TruffleSqueakTestImage-6.0alpha-20228b-64bit.zip"}};

/* Returns path to image file in TruffleSqueak's resources directory. */
public static String findImage() {
final Path languageHome = HomeFinder.getInstance().getLanguageHomes().get(SqueakLanguageConfig.ID);
if (languageHome == null) {
throw new RuntimeException("Unable to locate TruffleSqueak's language home.");
/* Ensures that TruffleSqueak's resources directory exists and returns path to image file. */
public static String findImage(final String userImage) {
final File resourcesDirectory = findResourcesDirectory();
try {
ensureDirectory(resourcesDirectory);
} catch (final IOException e) {
throw new RuntimeException(e);
}
if (userImage != null) {
return userImage;
}
final String imageFile = findImageFile(resourcesDirectory);
if (imageFile != null) {
return imageFile;
} else {
final Scanner userInput = new Scanner(System.in);
final PrintStream out = System.out; // ignore checkstyle
for (int i = 0; i < SMALLTALK_IMAGES.length; i++) {
out.println(String.format("%d) %s", i + 1, SMALLTALK_IMAGES[i][0]));
}
out.print("Choose Smalltalk image: ");
int selection = -1;
try {
selection = userInput.nextInt() - 1;
} catch (final NoSuchElementException e) {
// ignore
}
if (!(0 <= selection && selection < SMALLTALK_IMAGES.length)) {
throw new RuntimeException("Invalid selection. Please try again.");
}
final String[] selectedEntry = SMALLTALK_IMAGES[selection];
out.println(String.format("Downloading %s...", selectedEntry[0]));

downloadAndUnzip(selectedEntry[1], resourcesDirectory);
return Objects.requireNonNull(findImageFile(resourcesDirectory));
}
final Path resourcesDirectory = languageHome.resolve("resources");
final File resourcesDirectoryFile = resourcesDirectory.toFile();
final String[] imageFiles = resourcesDirectoryFile.list((dir, name) -> dir == resourcesDirectoryFile && name.endsWith(".image"));
}

private static String findImageFile(final File resourcesDirectory) {
final String[] imageFiles = resourcesDirectory.list((dir, name) -> dir.equals(resourcesDirectory) && name.endsWith(".image"));
if (imageFiles != null && imageFiles.length > 0) {
/* Sort imageFiles alphabetically and return the last. */
Arrays.sort(imageFiles);
return resourcesDirectory.resolve(imageFiles[imageFiles.length - 1]).toString();
return resourcesDirectory.toPath().resolve(imageFiles[imageFiles.length - 1]).toString();
} else {
throw new RuntimeException("Unable to locate an image file in TruffleSqueak's resources directory.");
return null;
}
}

private static File findResourcesDirectory() {
final Path languageHome = HomeFinder.getInstance().getLanguageHomes().get(SqueakLanguageConfig.ID);
if (languageHome == null) {
throw new RuntimeException("Unable to locate TruffleSqueak's language home.");
}
return languageHome.resolve("resources").toFile();
}

private static void downloadAndUnzip(final String url, final File destDirectory) {
try {
final BufferedInputStream bis = new BufferedInputStream(new URL(url).openStream());
unzip(bis, destDirectory);
} catch (final IOException e) {
throw new RuntimeException(e);
}
}

private static void unzip(final BufferedInputStream bis, final File destDirectory) throws IOException {
final byte[] buffer = new byte[2048];
final ZipInputStream zis = new ZipInputStream(bis);
ZipEntry zipEntry = zis.getNextEntry();
while (zipEntry != null) {
final File destFile = new File(destDirectory, zipEntry.getName());
// https://snyk.io/research/zip-slip-vulnerability
if (!destFile.getCanonicalPath().startsWith(destDirectory.getCanonicalPath() + File.separator)) {
throw new IOException("Zip entry is outside of the dest dir: " + zipEntry.getName());
}
if (zipEntry.isDirectory()) {
ensureDirectory(destFile);
} else {
ensureDirectory(destFile.getParentFile());
final FileOutputStream fos = new FileOutputStream(destFile);
int len;
while ((len = zis.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
fos.close();
}
zipEntry = zis.getNextEntry();
}
zis.closeEntry();
zis.close();
}

private static void ensureDirectory(final File directory) throws IOException {
if (!directory.isDirectory() && !directory.mkdirs()) {
throw new IOException("Failed to create directory " + directory);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public static final class SqueakContextOptions {

public SqueakContextOptions(final Env env) {
final OptionValues options = env.getOptions();
imagePath = options.get(ImagePath);
imagePath = options.get(ImagePath).isEmpty() ? null : options.get(ImagePath);
imageArguments = options.get(ImageArguments).isEmpty() ? new String[0] : options.get(ImageArguments).split(",");
isHeadless = options.get(Headless);
isQuiet = options.get(Quiet);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ public SqueakDisplayInterface getDisplay() {
public String getImagePath() {
if (imagePath == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
setImagePath(options.imagePath.isEmpty() ? SqueakImageLocator.findImage() : options.imagePath);
setImagePath(SqueakImageLocator.findImage(options.imagePath));
}
return imagePath;
}
Expand Down

0 comments on commit c4391a2

Please sign in to comment.