dexed/cesetup/cesetup.d

303 lines
9.5 KiB
D

module cesetup;
import std.stdio;
import std.file: mkdir, exists, remove, rmdir, getSize, FileException;
import std.stream: File, FileMode;
import std.process: environment, executeShell;
import std.path: dirSeparator;
import std.string: strip, toLower, center, leftJustify, rightJustify;
import std.getopt;
version(X86) version(linux) version = nux32;
version(X86_64) version(linux) version = nux64;
version(X86) version(Windows)version = win32;
version(win32) enum exeExt = ".exe";
else enum exeExt = "";
alias ImpType = immutable ubyte[];
alias ResType = immutable Resource;
struct Resource
{
ImpType data;
immutable string destName;
immutable bool isExe;
}
ResType coedit = Resource(cast(ImpType) import("coedit" ~ exeExt), "coedit" ~ exeExt, true);
ResType cesyms = Resource(cast(ImpType) import("cesyms" ~ exeExt), "cesyms" ~ exeExt, true);
ResType cetodo = Resource(cast(ImpType) import("cetodo" ~ exeExt), "cetodo" ~ exeExt, true);
ResType dcd_server = Resource(cast(ImpType) import("dcd-server" ~ exeExt), "dcd-server" ~ exeExt, true);
ResType dcd_client = Resource(cast(ImpType) import("dcd-client" ~ exeExt), "dcd-client" ~ exeExt, true);
ResType icon = Resource(cast(ImpType) import("coedit.ico"), "coedit.ico", false);
ResType png = Resource(cast(ImpType) import("coedit.png"), "coedit.png", false);
ResType celic = Resource(cast(ImpType) import("coedit.license.txt"), "coedit.license.txt", false);
ResType dcdlic = Resource(cast(ImpType) import("dcd.license.txt"), "dcd.license.txt", false);
static struct Formater
{
private enum width = 48;
private static __gshared char[] separator;
static this()
{
separator.length = width + 4;
separator[] = '-';
separator[0] = '+';
separator[$-1] = '+';
}
static void justify(char A, string s)()
{
static assert (s.length <= width, "too long to fit on a line...");
static if (A == 'L')
writeln("| ", leftJustify(s, width, ' '), " |");
else static if (A == 'C')
writeln("| ", center(s, width, ' '), " |");
else static if (A == 'R')
writeln("| ", rightJustify(s, width, ' '), " |");
else static assert(0, "invalid justification, L|C|R expected");
}
static void separate(){separator.writeln;}
static void emptyLine(){justify!('L', "");}
}
static immutable string exePath, appDataPath, shortCutPath;
version(win32){} else immutable bool asSu;
static this()
{
version(win32)
{
exePath = environment.get("PROGRAMFILES") ~ r"\Coedit\";
appDataPath = environment.get("APPDATA") ~ r"\Coedit\";
shortCutPath = environment.get("USERPROFILE") ~ r"\Desktop\";
}
else
{
asSu = environment.get("SUDO_USER") != "";
if (asSu)
{
exePath = "/usr/bin";
appDataPath = "/home/" ~ environment.get("SUDO_USER") ~ "/.config/Coedit/";
shortCutPath = "/usr/share/applications/";
}
else
{
exePath = "/home/" ~ environment.get("USER") ~ "/bin/";
appDataPath = "/home/" ~ environment.get("USER") ~ "/.config/Coedit/";
shortCutPath = "/home/" ~ environment.get("USER") ~ "/.local/share/applications/";
}
}
}
void main(string[] args)
{
bool nodcd;
bool uninstall;
getopt(args, config.passThrough,
"nodcd", &nodcd,
"u|uninstall", &uninstall
);
Formater.separate;
if (!uninstall) Formater.justify!('C', "Coedit 1 update 2 - setup");
else Formater.justify!('C', "Coedit uninstaller");
Formater.separate;
version(win32) Formater.justify!('L', "the setup program must be run as admin");
else
{
if(!asSu) Formater.justify!('L', "Coedit can also be setup globally (sudo)");
else Formater.justify!('L', "Coedit will be accessible from all the accounts");
}
Formater.separate;
Formater.justify!('L', "options:");
Formater.emptyLine;
if (!uninstall)
{
if (!nodcd) Formater.justify!('L', "--nodcd: skip DCD setup");
Formater.justify!('L', "-u: uninstall");
}
else if (!nodcd) Formater.justify!('L', "--nodcd: do not remove DCD");
Formater.justify!('L', "press A to abort or another key to start...");
Formater.separate;
const string inp = readln.strip;
if (inp.toLower == "a") return;
Formater.separate;
size_t failures;
if(!uninstall)
{
if (installResource(coedit, exePath))
Formater.justify!('L', "Coedit main application extracted");
else failures++;
if (installResource(cesyms, exePath))
Formater.justify!('L', "Coedit symbol list builder extracted");
else failures++;
if (installResource(cetodo, exePath))
Formater.justify!('L', "Coedit todo comment parser extracted");
else failures++;
if (installResource(celic, appDataPath))
Formater.justify!('L', "Coedit license file extracted");
else failures++;
if (installResource(icon, appDataPath))
Formater.justify!('L', "Coedit icon file extracted");
else failures++;
if (installResource(png, appDataPath))
Formater.justify!('L', "Coedit big png logo extracted");
else failures++;
if (!nodcd)
{
if (installResource(dcd_server, exePath))
Formater.justify!('L', "Completion daemon server extracted");
else failures++;
if (installResource(dcd_client, exePath))
Formater.justify!('L', "Completion daemon client extracted");
else failures++;
if (installResource(dcdlic, appDataPath))
Formater.justify!('L', "Completion daemon license extracted");
else failures++;
}
Formater.separate;
if (failures)
Formater.justify!('L', "there are ERRORS, plz contact the support");
else
{
version(win32) win32PostInstall();
else nuxPostInstall();
Formater.justify!('L', "the files are correctly extracted...");
}
Formater.emptyLine;
Formater.justify!('R', "...press a key to exit");
Formater.separate;
readln;
}
else
{
failures += !uninstallResource(coedit, exePath);
failures += !uninstallResource(cesyms, exePath);
failures += !uninstallResource(cetodo, exePath);
failures += !uninstallResource(celic, appDataPath);
failures += !uninstallResource(icon, appDataPath);
failures += !uninstallResource(png, appDataPath);
if (!nodcd)
{
failures += !uninstallResource(dcd_client, exePath);
failures += !uninstallResource(dcd_server, exePath);
failures += !uninstallResource(dcdlic, appDataPath);
}
version(win32)
{
try rmdir(exePath);
catch(FileException e) failures++;
}
if (failures)
Formater.justify!('L', "there are ERRORS, plz contact the support");
else
{
version(win32) win32PostUninstall();
else nuxPostUninstall();
Formater.justify!('L', "the files are correctly removed...");
}
Formater.emptyLine;
Formater.justify!('R', "...press a key to exit");
Formater.separate;
readln;
}
}
string extractedName(Resource resource, string path)
{
return path ~ dirSeparator ~ resource.destName;
}
bool installResource(Resource resource, string path)
{
if (!path.exists)
mkdir(path);
if (!path.exists)
return false;
try
{
const string fname = extractedName(resource, path);
File f = new File(fname, FileMode.OutNew);
f.write(resource.data);
f.close;
version(win32) {}
else if (resource.isExe && fname.exists)
executeShell("chmod a+x " ~ fname);
}
catch (Exception e)
return false;
return true;
}
bool uninstallResource(Resource resource, string path)
{
const string fname = extractedName(resource, path);
if (!fname.exists) return true;
try remove(fname);
catch (FileException e) return false;
return true;
}
void nuxPostInstall()
{
File f = new File(shortCutPath ~ "coedit.desktop", FileMode.OutNew);
f.writeLine("[Desktop Entry]");
f.writeLine("Name=coedit");
f.writeLine("Exec=coedit %f");
f.writeLine("Icon=" ~ appDataPath ~ "/coedit.png");
f.writeLine("Type=Application");
f.writeLine("Categories=Utility;Application;Development;");
f.writeLine("Terminal=false");
f.close;
}
void nuxPostUninstall()
{
try remove(shortCutPath ~ "coedit.desktop");
catch (FileException e) {}
}
void win32PostInstall()
{
// note: this is not a true shortcut, other options are
// - create a true lnk by generating and executing a vbs
// - use winapi...
string target = exePath ~ "coedit.exe";
File f = new File(shortCutPath ~ "Coedit.url", FileMode.OutNew);
f.writeLine("[InternetShortcut]");
f.writeString("URL=");
f.writeLine("\"" ~ target ~ "\"");
f.writeString("IconFile=");
f.writeLine("\"" ~ target ~ "\"");
f.writeLine("IconIndex=0");
f.close;
}
void win32PostUninstall()
{
try remove(shortCutPath ~ "Coedit.url");
catch (FileException e) {}
}