Exemplo n.º 1
0
def erase():
    """Remove hamstall."""
    if not (config.exists(config.full("~/.hamstall/hamstall.py"))):
        print("hamstall not detected so not removed!")
        generic.leave()
    config.vprint('Removing source line from bashrc')
    config.remove_line("~/.hamstall/.bashrc", "~/{}".format(config.read_config("ShellFile")), "word")
    config.vprint("Removing .desktop files")
    for prog in config.db["programs"]:
        if config.db["programs"][prog]["desktops"]:
            for d in config.db["programs"][prog]["desktops"]:
                try:
                    os.remove(config.full("~/.local/share/applications/{}.desktop".format(d)))
                except FileNotFoundError:
                    pass
    config.vprint('Removing hamstall directory')
    rmtree(config.full('~/.hamstall'))
    try:
        rmtree("/tmp/hamstall-temp")
    except FileNotFoundError:
        pass
    print("Hamstall has been removed from your system.")
    print('Please restart your terminal.')
    config.unlock()
    sys.exit(0)
Exemplo n.º 2
0
def ask_file(question):
    """Get User Input for File.

    Get user input for a file

    Args:
        question (str): Question to ask user

    Returns:
        str: Path to file
    
    """
    if config.mode == "cli":
        f = "asdf"
        while not config.exists(config.full(f)):
            f = input(question)
        return config.full(f)
    elif config.mode == "gui":
        layout = [
            [sg.Text(question)],
            [sg.InputText(key="answer"), sg.FileBrowse()],
            [sg.Button("Submit")]
        ]
        window = sg.Window("tarstall-gui", layout, disable_close=True)
        while True:
            event, values = window.read()
            if event == "Submit":
                window.Close()
                return values["answer"]
Exemplo n.º 3
0
def file_browser(root_dir):
    """File Browser.

    File browser for CLI that allows the choosing of files in folders.

    Args:
        root_dir (str): Path to top directory. Anything above this will be unaccessible
    
    Returns:
        str: path/to/file/from/root_dir/file.txt (Path to the selected file from root_dir (NOT FROM / !!!!)

    """
    root_dir = config.full(root_dir)
    os.chdir(root_dir)
    all_files = os.listdir()
    folders = []
    files = []
    for f in all_files:
        if os.path.isdir("./{}".format(f)):
            folders.append(f)
        else:
            files.append(f)
    msg = "Folders: " + ' '.join(folders) + "\n" + "Files: " + ' '.join(files)
    file_chosen = 'Cool fact. This line was originally written on line 163.'
    current_folder_path = []
    while file_chosen not in files:
        all_files = os.listdir()
        folders = []
        files = []
        for f in all_files:
            if os.path.isdir("./{}".format(f)):
                folders.append(f)
            else:
                files.append(f)
        msg = "Folders: " + ' '.join(folders) + "\n" + "Files: " + ' '.join(files)
        file_chosen = ask(msg + '\n\nPlease enter a file listed above. If you would like to cancel, type exit. If you would like to go up a directory, type "..": ')
        if file_chosen == "exit":
            return None
        elif file_chosen in folders:
            os.chdir(config.full("./{}".format(file_chosen)))
            current_folder_path.append(file_chosen)
        elif file_chosen == "..":
            if os.getcwd() == root_dir:
                pprint("\nCan't go up a directory!\n")
            else:
                os.chdir(config.full(".."))
    if current_folder_path != []:
        extra_slash = "/"
    else:
        extra_slash = ""
    return "/".join(current_folder_path) + extra_slash + file_chosen
Exemplo n.º 4
0
def install(program, overwrite=False):
    """Install Archive.

    Takes an archive and installs it.

    Args:
        program (str): Path to archive to install
        overwrite (bool): Whether or not to assume the program is already installed and to overwite it

    """
    if not config.check_bin("rsync") and overwrite:
        print("rsync not installed! Please install it.")
        generic.leave(1)
    program_internal_name = config.name(program)
    if config.char_check(program_internal_name):
        print("Error! Archive name contains a space or #!")
        generic.leave(1)
    config.vprint("Removing old temp directory (if it exists!)")
    try:
        rmtree(config.full("/tmp/hamstall-temp"))  # Removes temp directory (used during installs)
    except FileNotFoundError:
        pass
    config.vprint("Creating new temp directory")
    os.mkdir(config.full("/tmp/hamstall-temp"))  # Creates temp directory for extracting archive
    config.vprint("Extracting archive to temp directory")
    file_extension = config.extension(program)
    program = config.spaceify(program)
    command_to_go = create_command(file_extension, program)
    config.vprint('File type detected: ' + file_extension)
    try:
        os.system(command_to_go)  # Extracts program archive
    except:
        print('Failed to run command: ' + command_to_go + "!")
        print("Program installation halted!")
        generic.leave(1)
    config.vprint('Checking for folder in folder')
    if os.path.isdir(config.full('/tmp/hamstall-temp/' + program_internal_name + '/')):
        config.vprint('Folder in folder detected! Using that directory instead...')
        source = config.full('/tmp/hamstall-temp/' + program_internal_name) + '/'
        dest = config.full('~/.hamstall/bin/')
    else:
        config.vprint('Folder in folder not detected!')
        source = config.full('/tmp/hamstall-temp') + '/'
        dest = config.full('~/.hamstall/bin/' + program_internal_name + "/")
    config.vprint("Moving program to directory")
    if overwrite:
        if verbose:
            verbose_flag = "v"
        else:
            verbose_flag = ""
        call(["rsync", "-a{}".format(verbose_flag), source, dest])
    else:
        move(source, dest)
    config.vprint("Adding program to hamstall list of programs")
    config.vprint('Removing old temp directory...')
    try:
        rmtree(config.full("/tmp/hamstall-temp"))
    except FileNotFoundError:
        config.vprint('Temp folder not found so not deleted!')
    finish_install(program_internal_name)
Exemplo n.º 5
0
def test_uninstall():
    prog_manage.uninstall("package")
    assert config.check_line(
        "export PATH=$PATH:~/.tarstall/bin/package # package",
        "~/.tarstall/.bashrc", "fuzzy") is False
    assert os.path.isfile(
        config.full("~/.tarstall/bin/package/test.sh")) is False
Exemplo n.º 6
0
def rename(program):
    new_name = "!"
    while not new_name.replace("_", "").replace("-", "").isalnum():
        new_name = input("Please enter the name you would like to change this program to: ")
        if not new_name.replace("_", "").replace("-", "").isalnum():
            print("Alphanumeric characters, dashes, and underscores only, please!")
    for d in config.db["programs"][program]["desktops"]:
        config.replace_in_file("/.hamstall/bin/{}".format(program), "/.hamstall/bin/{}".format(new_name), 
        "~/.local/share/applications/{}.desktop".format(d))
    config.db["programs"][new_name] = config.db["programs"].pop(program)
    config.replace_in_file("export PATH=$PATH:~/.hamstall/bin/" + program, 
    "export PATH=$PATH:~/.hamstall/bin/" + new_name, "~/.hamstall/.bashrc")
    config.replace_in_file("'cd " + config.full('~/.hamstall/bin/' + program),
    "'cd " + config.full('~/.hamstall/bin/' + new_name), "~/.hamstall/.bashrc")
    config.replace_in_file("# " + program, "# " + new_name, "~/.hamstall/.bashrc")
    move(config.full("~/.hamstall/bin/" + program), config.full("~/.hamstall/bin/" + new_name))
    return new_name
Exemplo n.º 7
0
def first_time_setup(sym):
    """First Time Setup.

    Sets up hamstall for the first time.

    Args:
        sym (bool): Used for testing. If True, installed py's will be symlinked to originals, not copied.
        False means it will be copied and not symlinked.

    """
    if config.exists(config.full('~/.hamstall/hamstall.py')):
        print('Please don\'t run first time setup on an already installed system!')
        generic.leave()
    print('Installing hamstall to your system...')
    try:
        os.mkdir(config.full("~/.hamstall"))
    except FileExistsError:
        rmtree(config.full("~/.hamstall"))
        os.mkdir(config.full("~/.hamstall"))
    try:
        os.mkdir(config.full("/tmp/hamstall-temp/"))
    except FileExistsError:
        rmtree(config.full("/tmp/hamstall-temp"))
        os.mkdir(config.full("/tmp/hamstall-temp/"))
    os.mkdir(config.full("~/.hamstall/bin"))
    config.create("~/.hamstall/database")
    create_db()
    config.create("~/.hamstall/.bashrc")  # Create directories and files
    files = os.listdir()
    for i in files:
        i_num = len(i) - 3
        if i[i_num:len(i)] == '.py':
            if sym:
                os.symlink(os.getcwd() + "/" + i, config.full("~/.hamstall/" + i))
            else:
                try:
                    copyfile(i, config.full('~/.hamstall/' + i))
                except FileNotFoundError:
                    print("A file is missing that was attempted to be copied! Install halted!")
                    generic.leave(1)
    config.add_line("source ~/.hamstall/.bashrc\n", "~/{}".format(config.read_config("ShellFile")))
    config.add_line("alias hamstall='python3 ~/.hamstall/hamstall.py'\n", "~/.hamstall/.bashrc")  # Add bashrc line
    print('First time setup complete!')
    print('Please run the command "source ~/{}" or restart your terminal.'.format(config.read_config("ShellFile")))
    print('Afterwards, you may begin using hamstall with the hamstall command!')
    generic.leave()
Exemplo n.º 8
0
def test_write_db():
    old_db = config.db
    config.db.update({"test": "here"})
    config.write_db()
    old_db.update({"test": "here"})
    with open(config.full("~/.hamstall/database")) as f:
        db = json.load(f)
    assert old_db == db
Exemplo n.º 9
0
def test_erase():
    assert prog_manage.erase() == "Erased"
    assert os.path.isfile(config.full("~/.tarstall/tarstall.py")) is False
    try:
        assert config.check_line("source ~/.tarstall/.bashrc", "~/.bashrc",
                                 "fuzzy") is False
    except FileNotFoundError:
        try:
            config.check_line("source ~/.tarstall/.zshrc", "~/.zshrc",
                              "fuzzy") is False
        except FileNotFoundError:
            raise AssertionError("Please use bash or zsh for testing!")
Exemplo n.º 10
0
def dirinstall(program_path, program_internal_name, overwrite=False):
    """Install Directory.

    Installs a directory as a program

    Args:
        program_path (str): Path to directory to install
        program_internal_name (str): Name of program
        overwrite (bool): Whether or not to assume the program is already installed and to overwite it

    """
    if not config.check_bin("rsync") and overwrite:
        print("rsync not installed! Please install it.")
        generic.leave(1)
    config.vprint("Moving folder to hamstall destination")
    if overwrite:
        call(["rsync", "-a", program_path, config.full("~/.hamstall/bin/{}".format(program_internal_name))])
        rmtree(program_path)
    else:
        move(program_path, config.full("~/.hamstall/bin/"))
    finish_install(program_internal_name)
Exemplo n.º 11
0
def uninstall(program):
    """Uninstall a Program.

    Args:
        program (str): Name of program to uninstall

    """
    config.vprint("Removing program files")
    rmtree(config.full("~/.hamstall/bin/" + program + '/'))
    config.vprint("Removing program from PATH and any binlinks for the program")
    config.remove_line(program, "~/.hamstall/.bashrc", 'poundword')
    config.vprint("Removing program desktop files")
    if config.db["programs"][program]["desktops"]:
        for d in config.db["programs"][program]["desktops"]:
            try:
                os.remove(config.full("~/.local/share/applications/{}.desktop".format(d)))
            except FileNotFoundError:
                pass
    config.vprint("Removing program from hamstall list of programs")
    del config.db["programs"][program]
    print("Uninstall complete!")
    return
Exemplo n.º 12
0
def update(silent=False):
    """Update Hamstall.

    Checks to see if we should update hamstall, then does so if one is available

    Args:
        silent (bool): Whether or not to not provide user feedback. Defaults to False.

    """
    if not can_update:
        print("requests not found! Can't update!")
        if silent:
            return
        else:
            generic.leave(1)
    """Update hamstall after checking for updates"""
    prog_version_internal = config.get_version('prog_internal_version')
    config.vprint("Checking version on GitHub")
    final_version = get_online_version('prog')
    config.vprint('Installed internal version: ' + str(prog_version_internal))
    config.vprint('Version on GitHub: ' + str(final_version))
    if final_version > prog_version_internal:
        print("An update has been found! Installing...")
        config.vprint('Removing old hamstall pys...')
        os.chdir(config.full("~/.hamstall"))
        files = os.listdir()
        for i in files:
            i_num = len(i) - 3
            if i[i_num:len(i)] == '.py':
                os.remove(config.full('~/.hamstall/' + i))
        config.vprint("Downloading new hamstall pys..")
        download_files(['hamstall.py', 'generic.py', 'config.py', 'config.py', 'prog_manage.py'], '~/.hamstall/')
        config.db["version"]["prog_internal_version"] = final_version
    elif final_version < prog_version_internal:
        if not silent:
            print("hamstall version newer than latest online version! Something might be wrong...")
    else:
        if not silent:
            print("No update found!")
Exemplo n.º 13
0
def gitinstall(git_url, program_internal_name, overwrite=False):
    """Git Install.

    Installs a program from a URL to a Git repository

    Args:
        git_url (str): URL to Git repository
        program_internal_name (str): Name of program to use
        overwrite (bool): Whether or not to assume the program is already installed and to overwite it

    """
    if not config.check_bin("rsync") and overwrite:
        print("rsync not installed! Please install it.")
        generic.leave(1)
    config.vprint("Verifying that the input is a URL...")
    if re.match(r"https://\w.\w", git_url) is None or " " in git_url or "\\" in git_url:
        print("Invalid URL!")
        generic.leave()
    config.vprint("Checking for .git extension")
    if config.extension(git_url) != ".git":
        print("The URL must end in .git!")
        generic.leave(1)
    config.vprint("Downloading git repository")
    if overwrite:
        try:
            rmtree(config.full("/tmp/hamstall-temp"))  # Removes temp directory (used during installs)
        except FileNotFoundError:
            pass
        os.mkdir("/tmp/hamstall-temp")
        os.chdir("/tmp/hamstall-temp")
    else:
        os.chdir(config.full("~/.hamstall/bin"))
    err = call(["git", "clone", git_url])
    if err != 0:
        print("Error detected! Installation halted.")
        generic.leave(1)
    if overwrite:
        call(["rsync", "-a", "/tmp/hamstall-temp/{}/".format(program_internal_name), config.full("~/.hamstall/bin/{}".format(program_internal_name))])
    finish_install(program_internal_name)
Exemplo n.º 14
0
def binlink(program_internal_name):
    """Link Program.

    Creates an alias that cd's into a program directory before running a file in the program

    Args:
        program_internal_name (str): Name of program to create a binlink for

    """
    while True:
        files = os.listdir(config.full('~/.hamstall/bin/' + program_internal_name + '/'))
        print(' '.join(files))
        file_chosen = 'Cool fact. This line was originally written on line 163.'
        while file_chosen not in files:  # Get file to binlink from user
            file_chosen = input('Please enter a file listed above. If you would like to cancel, type exit: ')
            if file_chosen == "exit":
                return
        line_to_add = 'alias ' + file_chosen + "='cd " + config.full('~/.hamstall/bin/' + program_internal_name) + \
                      '/ && ./' + file_chosen + "' # " + program_internal_name + "\n"
        config.vprint("Adding alias to bashrc")
        config.add_line(line_to_add, "~/.hamstall/.bashrc")
        yn = generic.get_input('Would you like to continue adding files to be run directly? [y/N]', ['y', 'n'], 'n')
        if yn == 'n':
            return
Exemplo n.º 15
0
def download_files(files, folder):
    """Download List of Files.
    
    Args:
        files (str[]): List of files to obtain from hamstall repo
        folder (str): Folder to put files in

    """
    if not can_update:
        print("Cannot download files if the request library isn't installed!")
        generic.leave(1)
    for i in files:
        r = requests.get(
            "https://raw.githubusercontent.com/hammy3502/hamstall/{}/".format(config.db["version"]["branch"]) + i)
        open(config.full(folder + i), 'wb').write(r.content)
Exemplo n.º 16
0
def manage(program):
    """Manage Installed Program.

    Args:
        program (str): Internal name of program to manage

    """
    while True:
        print("Enter an option to manage " + program + ":")
        print("b - Create binlinks for " + program)
        print("p - Add " + program + " to PATH")
        print("n - Rename " + program)
        print("u - Uninstall " + program)
        print("r - Remove all binlinks + PATHs for " + program)
        print("d - Create a .desktop file for " + program)
        print("rd - Remove a .desktop file for " + program)
        print("c - Run a command inside " + program + "'s directory")
        print("s - Launch a shell inside " + program + "'s directory")
        print("E - Exit program management")
        option = generic.get_input("[b/p/n/u/r/d/rd/c/s/E]", ['b', 'p', 'n', 'u', 'r', 'd', 'c', 'rd', 's', 'e'], 'e')
        if option == 'b':
            binlink(program)
        elif option == 'p':
            pathify(program)
        elif option == 'n':
            program = rename(program)
        elif option == 'u':
            uninstall(program)
            generic.leave()
        elif option == 'r':
            config.remove_line(program, "~/.hamstall/.bashrc", 'poundword')
        elif option == 'd':
            create_desktop(program)
        elif option == 'rd':
            remove_desktop(program)
        elif option == 'c':
            command(program)
        elif option == 's':
            print("When you exit the shell, you will be returned to here.")
            os.chdir(config.full("~/.hamstall/bin/" + program + "/"))
            call(["/bin/bash"])
        elif option == 'e':
            generic.leave()
Exemplo n.º 17
0
def remove_desktop(program):
    """Remove .desktop

    Removes a .desktop file assosciated with a program and its corresponding entry in the database
    This process is walked through with the end-user

    Args:
        program (str): Program to remove

    """
    if not config.db["programs"][program]["desktops"]:
        print("Program has no .desktop files!")
    else:
        print("Desktops: ")
        for d in config.db["programs"][program]["desktops"]:
            print(d)
        inp = "/ choose desktop"
        while not (inp in config.db["programs"][program]["desktops"]) and inp != "exit":
            inp = input("Please enter the desktop you would like to remove or type \"exit\" to exit: ")
        try:
            os.remove(config.full("~/.local/share/applications/{}.desktop".format(inp)))
        except FileNotFoundError:
            pass
        config.db["programs"][program]["desktops"].remove(inp)
Exemplo n.º 18
0
def test_erase():
    with pytest.raises(SystemExit):
        prog_manage.erase()
    assert os.path.isfile(config.full("~/.hamstall/hamstall.py")) is False
    assert config.check_line("source ~/.hamstall/.bashrc", "~/.bashrc", "fuzzy") is False
Exemplo n.º 19
0
    "running)",
    action="store_true")
group.add_argument('-c',
                   '--config',
                   help="Change hamstall options",
                   action="store_true")
args = parser.parse_args()  # Parser stuff

if config.locked():  # Lock check
    config.vprint(
        "Lock file detected at /tmp/hamstall-lock. " +
        "Delete this file if you are completely sure no other instances of hamstall are running!"
    )
    if args.remove_lock:
        try:
            os.remove(config.full("/tmp/hamstall-lock"))
            print("Lock removed!")
        except FileNotFoundError:
            print("Lock doesn't exist, so not removed!")
        generic.leave()
    else:
        print(
            "Another instance of hamstall is probably running! Execution halted!"
        )
        sys.exit(2)
else:
    config.lock()

username = getpass.getuser()  # Root check
if username == 'root':
    print(
Exemplo n.º 20
0
def branch_wizard():
    """Switch Branches."""
    if get_online_version("prog", "master") <= 18:
        extra_warning = """
hamstall stable release 1.2.0 hasn't happened yet!
You cannot reset to the older version of hamstall! You must stay on this version!
###############
        """
    else:
        extra_warning = ""
    print("""\n\n
####WARNING####
WARNING: You are changing branches of hamstall!
Changing from master to beta means you may receive updates that contain bugs, some extremely severe!
Changing from beta to master means you will either HAVE ALL OF YOUR HAMSTALL PROGRAMS DELETED
or you will have to STAY ON THE UPDATE YOU CURRENTLY HAVE UNTIL MASTER CATCHES UP!

Switching branches will trigger an immediate update of hamstall!
###############
{extra_warning}
Select a branch:
m - Master branch. Less bugs, more stable, wait for updates.
b - Beta branch. More bugs, less stable, updates asap.
E - Exit branch wizard and don't change branches.
    """.format(extra_warning=extra_warning))
    ans = generic.get_input("[m/b/E] ", ['m', 'b', 'e'], 'e')
    if ans == 'e':
        print("Not changing branches!")
        generic.leave()
    elif ans == 'm' and config.db["version"]["branch"] == "master":
        print("Already on the master branch, not switching!")
        generic.leave()
    elif ans == 'b' and config.db["version"]["branch"] == "beta":
        print("Already on the beta branch, not switching!")
        generic.leave()
    else:
        check = input('Type "YES" (without the quotes) to confirm the branch switch! ')
        if check != "YES":
            print("Cancelling branch switch.")
            generic.leave()
        if ans == 'm':
            branch = "master"
            if not config.check_bin("git"):
                print("Git is not installed! Your branch can be switched, but downgrading is impossible! "
                "If you would like to exit here, type \"y\"!")
                if generic.get_input("", ['y','n'], 'n') == 'y':
                    print("Branch has not been changed!")
                    generic.leave(1)
                else:
                    print("Continuing with branch switch.")
        elif ans == 'b':
            branch = "beta"
        print("Changing branches and updating hamstall!")
        config.vprint("Switching branch and writing change to file")
        config.db["version"]["branch"] = branch
        config.write_db()
        if branch == "beta":
            config.vprint("Updating hamstall...")
            update(True)
            generic.leave(0)
        elif branch == "master":
            if get_online_version("prog", "master") <= 18:
                print("Cannot downgrade; staying on this version until master catches up!")
                generic.leave(0)
            print("Would you like to downgrade? If you do, all hamstall programs will be deleted!")
            dr = generic.get_input("If you don't, hamstall will remain at its current version until master is at a newer release! [y/N]",
            ['y', 'n'], 'n')
            if dr == 'y':
                config.vprint("Deleting and re-installing hamstall.")
                os.chdir(config.full("~/.hamstall"))
                config.vprint("Removing old hamstall .pys")
                for i in os.listdir():
                    i_num = len(i) - 3
                    if i[i_num:len(i)] == '.py':
                        try:
                            os.remove(i)
                        except FileNotFoundError:
                            pass
                try:
                    rmtree("/tmp/hamstall-temp")
                except FileNotFoundError:
                    pass
                os.mkdir("/tmp/hamstall-temp")
                os.chdir("/tmp/hamstall-temp")
                config.vprint("Cloning hamstall from the master branch")
                call(["git", "clone", "https://github.com/hammy3502/hamstall.git"])
                os.chdir("/tmp/hamstall-temp/hamstall")
                config.vprint("Adding new hamstall .pys")
                for i in os.listdir():
                    i_num = len(i) - 3
                    if i[i_num:len(i)] == '.py':
                        copyfile(i, config.full('~/.hamstall/' + i))
                config.vprint("Removing old database and programs.")
                try:
                    os.remove(config.full("~/.hamstall/database"))
                except FileNotFoundError:
                    pass
                try:
                    rmtree(config.full("~/.hamstall/bin"))
                except FileNotFoundError:
                    pass
                os.mkdir(config.full("~/.hamstall/bin"))
                print("Please run hamstall again to re-create the database!")
                config.unlock()
                config.db = {"refresh": True}
                config.write_db()
                sys.exit(0)
Exemplo n.º 21
0
def create_desktop(program_internal_name):
    """Create Desktop.

    Walks the user through creating a .desktop file for a program

    Args:
        program_internal_name (str): Name of program as stored in the database

    """
    files = os.listdir(config.full('~/.hamstall/bin/' + program_internal_name + '/'))
    print(' '.join(files))
    program_file = '/Placeholder/'
    config.vprint("Getting user inputs")
    while program_file not in files:  # Get file to binlink from user
        program_file = input('Please enter a file listed above. If you would like to cancel, type exit: ')
        if program_file == "exit":
            return
    desktop_name = "{}-{}".format(program_file, program_internal_name)
    if config.exists("~/.local/share/applications/{}.desktop".format(desktop_name)):
        print("Desktop file already exists!")
        return
    exec_path = config.full("~/.hamstall/bin/{}/{}".format(program_internal_name, program_file))
    path = config.full("~/.hamstall/bin/{}/".format(program_internal_name))
    comment = "/"
    while not comment.replace(" ", "").isalnum() and comment != "":
        comment = input("Please input a comment for the application: ")
    if comment == "":
        comment = program_internal_name
    icon = ";"
    while not icon.replace("-", "").replace("_", "").replace("/", "").isalnum() and icon != "":
        icon = input("Enter the path to an icon, the name of the icon, or press ENTER for no icon! ")
    if icon != "":
        icon = "Icon=" + icon
    terminal = generic.get_input("Should this program launch a terminal to run it in? [y/N]", ['y', 'n'], 'n')
    if terminal.lower() == 'y':
        should_terminal = "True"
    else:
        should_terminal = "False"
    name = "/"
    while not name.replace(" ", "").isalnum() and name != "":
        name = input("Please enter a name: ")
    if name == "":
        name = program_internal_name
    ans = " "
    chosen_categories = []
    categories = ["audio", "video", "development", "education", "game", "graphics", "network", "office", "science",
                  "settings", "system", "utility", "end"]
    while ans.lower() != "end":
        print("Please enter categories, one at a time, from the list of .desktop categories below (defaults to "
              "Utility). Type \"end\" to end category selection. \n")
        print(", ".join(categories))
        ans = generic.get_input("", categories, "Utility")
        if ans.capitalize() in chosen_categories or ans == "end":
            pass
        else:
            ans = ans.capitalize()
            chosen_categories.append(ans)
            if ans in ["Audio", "Video"] and not ("AudioVideo" in chosen_categories):
                chosen_categories.append("AudioVideo")
    if not chosen_categories:
        chosen_categories = ["Utility"]
    cats = ";".join(chosen_categories) + ";"  # Get categories for the .desktop
    to_write = """
[Desktop Entry]
Name={name}
Comment={comment}
Path={path}
Exec={exec_path}
{icon}
Terminal={should_terminal}
Type=Application
Categories={categories}
""".format(name=name, comment=comment, exec_path=exec_path,
           should_terminal=should_terminal, categories=cats,
           icon=icon, path=path)
    os.chdir(config.full("~/.local/share/applications/"))
    config.create("./{}.desktop".format(desktop_name))
    with open(config.full("./{}.desktop".format(desktop_name)), 'w') as f:
        f.write(to_write)
    config.db["programs"][program_internal_name]["desktops"].append(desktop_name)
    print("\nDesktop file created!")