330 lines
12 KiB
Python
Executable file
330 lines
12 KiB
Python
Executable file
#! /usr/bin/python3
|
|
# -*- coding: UTF-8 -*-
|
|
|
|
"""Make the fillpdf program available on this computer."""
|
|
|
|
import os
|
|
import os.path
|
|
import argparse
|
|
import shutil
|
|
import sys
|
|
import subprocess # for cloning
|
|
from constants import (CONFIGFILE, CONFIG_INSTALLDIR,
|
|
CONFIG_FORMDIR, CONFIG_SEPARATOR)
|
|
|
|
FORMS_DIRNAME = "TU-Dokumente"
|
|
FORMS_URL = ("https://gitlab.math.tu-dresden.de/s1140568/" + FORMS_DIRNAME +
|
|
".git")
|
|
SCRIPTS_TO_INSTALL = ["fillform", "reise", "uninstallfillpdf"]
|
|
|
|
|
|
def getPath():
|
|
"""Get list of directories that are in the PATH environment variable.
|
|
|
|
Returns:
|
|
List of directories as strings.
|
|
If loading path variable did not work, this list is empty.
|
|
|
|
"""
|
|
return os.environ.get('PATH', "").split(':') # "" is the default value
|
|
|
|
|
|
def checkLinkAccess(dir):
|
|
"""Tell if I can create a link there.
|
|
|
|
Try to create a link. If there was no error and indeed there
|
|
is a link, remove the link and return True.
|
|
Otherwise return False.
|
|
|
|
"""
|
|
# find a filename that is not used yet:
|
|
linkloc = os.path.join(dir, "z")
|
|
while os.path.lexists(linkloc):
|
|
linkloc = linkloc + "z"
|
|
if len(linkloc) > len(dir) + 100:
|
|
print("Found files with names with 1 to 100 z's in " +
|
|
"directory " + dir +
|
|
" Probably error.")
|
|
return False
|
|
try:
|
|
# try to create a link to the arbitrary position .
|
|
os.symlink(os.curdir, linkloc)
|
|
except FileNotFoundError as fnfe:
|
|
# the directory dir does not exist
|
|
return False
|
|
except PermissionError as pe:
|
|
# a cannot create a link there
|
|
return False
|
|
else:
|
|
try:
|
|
os.remove(linkloc)
|
|
except FileNotFoundError as fnfe:
|
|
print("In order to check if I have write access to the " +
|
|
"directory " + dir + " I created a link " + linkloc +
|
|
" there." +
|
|
" I cannot remove it so do it yourself.")
|
|
except PermissionError as pe:
|
|
print("In order to check if I have write access to the " +
|
|
"directory " + dir + " I created a link " + linkloc +
|
|
" there." +
|
|
" I cannot remove it so do it yourself.")
|
|
return True
|
|
|
|
|
|
def defaultPath():
|
|
"""Choose a suitable default directory for installing.
|
|
|
|
Try to access the path variable.
|
|
If possible use first entry where I can create a symlink and is of the form:
|
|
/usr/local/bin
|
|
~/bin
|
|
~/.config/bin
|
|
|
|
If not possible use first where I can create a link.
|
|
|
|
"""
|
|
pathdirs = getPath()
|
|
# check if sudo/ root:
|
|
rootdir = os.path.join("/", "usr", "local", "bin")
|
|
if rootdir in pathdirs and checkLinkAccess(rootdir):
|
|
return rootdir
|
|
for userdir in [os.path.join(os.environ.get("HOME", ""), ".local", "bin"),
|
|
os.path.join(os.environ.get("HOME", ""), "bin")]:
|
|
if userdir in pathdirs:
|
|
if checkLinkAccess(userdir):
|
|
return userdir
|
|
try:
|
|
os.mkdirs(userdir)
|
|
except OSError:
|
|
# already exists
|
|
# some other error, do not use this
|
|
# print("Debug: userdir link creation failed")
|
|
pass
|
|
else:
|
|
if checkLinkAccess(userdir):
|
|
return userdir
|
|
else:
|
|
# something did not work
|
|
print("Debug: userdir link creation check failed " +
|
|
"after creating dir")
|
|
else:
|
|
print("Debug: " + userdir + " is not in the path. It should be" +
|
|
" on a typical system.")
|
|
# print("Debug: non of the typical directories worked. " +
|
|
# "Use the first possible.")
|
|
for directory in pathdirs:
|
|
if checkLinkAccess(directory):
|
|
return directory
|
|
print("""No suitable path for installing was found. Do one of the following:
|
|
• execute this script as sudo/ root
|
|
• add a directory where you have write access in the path
|
|
• specify a directory via the --path option and add this directory"""
|
|
+ " to the path.")
|
|
raise FileNotFoundError("No directory for installation found.")
|
|
|
|
|
|
def createProgramSymlinks(where):
|
|
"""Create symlinks to some of scripts in this project.
|
|
|
|
Arguments:
|
|
where: the directory where to create the symlinks.
|
|
|
|
Raises:
|
|
multiple possibilities of error
|
|
|
|
"""
|
|
here = os.path.dirname(__file__)
|
|
origloc = 0
|
|
linkloc = 1
|
|
# dict { script : (where it is, where the link should be) }
|
|
# print("Debug: here: ", here)
|
|
scripts = {script : (os.path.abspath(
|
|
os.path.join(here, script + ".py")),
|
|
os.path.join(where, script))
|
|
for script in SCRIPTS_TO_INSTALL}
|
|
# print("Debug: scripts: ", scripts)
|
|
todoscripts = []
|
|
# check if the scripts exist, i.e. I am at the right place
|
|
for script in scripts:
|
|
# F_OK = does file exist?
|
|
if not os.access(scripts[script][origloc], os.F_OK):
|
|
raise FileNotFoundError("I cannot find the script " + script +
|
|
" in " + scripts[script][origloc] + ".")
|
|
if os.access(scripts[script][linkloc], os.F_OK, follow_symlinks=False):
|
|
if os.path.islink(scripts[script][linkloc]):
|
|
if not os.path.exists(scripts[script][linkloc]):
|
|
# os.path.exists says False on broken symlinks
|
|
raise FileExistsError("The symlink " +
|
|
scripts[script][linkloc] +
|
|
" already exists." +
|
|
" It is a broken symlink." +
|
|
" You may like to delete it " +
|
|
"and start " +
|
|
" this install script again.")
|
|
elif os.path.samefile(os.readlink(scripts[script][linkloc]),
|
|
scripts[script][origloc]):
|
|
# print("Debug: link " + script +
|
|
# " already links correctly.")
|
|
# nothing to do: link already links correctly
|
|
pass
|
|
else:
|
|
raise FileExistsError("The symlink "
|
|
+ scripts[script][linkloc] +
|
|
" already exists and points " +
|
|
"somewhere else.")
|
|
else:
|
|
raise FileExistsError("The file "
|
|
+ scripts[script][linkloc] +
|
|
" already exists.")
|
|
else:
|
|
todoscripts.append(script)
|
|
for script in todoscripts:
|
|
try:
|
|
os.symlink(scripts[script][origloc], scripts[script][linkloc])
|
|
except NotImplementedError:
|
|
print("The python doc says this happens only on Windows" +
|
|
" previous to Windows 6.0 (Vista). You should really " +
|
|
"know better than using Windows.")
|
|
raise
|
|
except OSError as e:
|
|
print("Apparently you do not have the permissions to create" +
|
|
" a symlink in the directory " + where +
|
|
". Call this install script with --path <dir>" +
|
|
" with a <dir> where you have the permissions and" +
|
|
" that is in the path.")
|
|
raise
|
|
|
|
|
|
def writeConfig(installdir, formdir):
|
|
"""Write the config file.
|
|
|
|
Try to write to the file $HOME/.config/.fillpdfrc.
|
|
|
|
arguments:
|
|
installdir: where the programs are installed
|
|
formdir: where the form files are
|
|
|
|
write one line per info, no line if info is None
|
|
"""
|
|
try:
|
|
with open(CONFIGFILE, "a+") as configfile:
|
|
# a = append
|
|
# if you use only the last entries in the configfile this is OK
|
|
# and you do not overwrite what might be interesting to
|
|
# the user
|
|
# + = create if not exist
|
|
if installdir is not None:
|
|
print(CONFIG_INSTALLDIR + CONFIG_SEPARATOR +
|
|
os.path.abspath(installdir), file=configfile)
|
|
else:
|
|
print("Debug: No installdir in config saved.")
|
|
if formdir is not None:
|
|
print(CONFIG_FORMDIR + CONFIG_SEPARATOR +
|
|
os.path.abspath(formdir), file=configfile)
|
|
else:
|
|
print("Debug: No directory for the form files " +
|
|
"in config saved.")
|
|
except:
|
|
print("Debug: Error at writing configfile:")
|
|
raise
|
|
else:
|
|
pass
|
|
# print("Debug: config file written")
|
|
|
|
|
|
def parse():
|
|
"""Create the argument parser (including the help) and parse the arguments.
|
|
|
|
Options/ Arguments:
|
|
--path directory
|
|
--formdir directory
|
|
|
|
"""
|
|
parser = argparse.ArgumentParser(
|
|
description=("Install fillpdf on this computer " +
|
|
"and show help how to start using it."))
|
|
# formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
# epilog=("To start go to the new directory and run " +
|
|
# "'./fillform Antrag.pdf'"))
|
|
parser.add_argument("--path", default=None,
|
|
help=("The directory where the scripts should " +
|
|
"be installed. Should be in the path. " +
|
|
"If not specified, you will be asked " +
|
|
"where to install."))
|
|
parser.add_argument("--formdir", default=None,
|
|
help=("The directory where the config (form) files " +
|
|
"for the pdf forms are. " +
|
|
"If not specified, look next to the " +
|
|
"parent directory of this script " +
|
|
"and if it is not there pull from gitlab."))
|
|
return vars(parser.parse_args())
|
|
|
|
|
|
def defaultConfig():
|
|
"""Check if ../TU-Dokumente exists.
|
|
If not, try to clone TU-Dokumente here and return this new directory.
|
|
|
|
"""
|
|
here = os.path.dirname(__file__)
|
|
nextTo = os.path.join(here, "..", FORMS_DIRNAME)
|
|
if os.path.exists(nextTo):
|
|
return nextTo
|
|
nextTo = os.path.join(here, FORMS_DIRNAME)
|
|
if os.path.exists(nextTo):
|
|
# already in current dir
|
|
return nextTo
|
|
try:
|
|
clone = subprocess.Popen([
|
|
"git", "clone", FORMS_URL],
|
|
stderr=subprocess.PIPE,
|
|
stdout=subprocess.PIPE)
|
|
try:
|
|
errorcode = clone.wait(timeout=30)
|
|
except TimeoutExpired:
|
|
print("Cloning the form files from " +
|
|
FORMS_URL + " took too long.")
|
|
else:
|
|
if errorcode != 0:
|
|
print("Debug: cloning the form files from " +
|
|
FORMS_URL + " did not work with errorcode " +
|
|
str(errorcode) + ".")
|
|
else:
|
|
# return newly cloned and created directory
|
|
return os.path.normpath(os.path.join(here,
|
|
FORMS_DIRNAME))
|
|
except Exception as e:
|
|
print("Trying to clone the form file repo failed in the" +
|
|
" following way:", e)
|
|
# no sensible default found
|
|
return None
|
|
|
|
|
|
def checkpdftk():
|
|
"""Check if pdftk is installed.
|
|
|
|
Without pdftk the software is useless so tell the user to install
|
|
it if necessary.
|
|
"""
|
|
if not shutil.which("pdftk"):
|
|
print("This software suite requires pdftk. Install it, such that" +
|
|
" the correct binary of the name pdftk is available in the "+
|
|
"PATH. Otherwise you get cryptic error messages.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
args = parse()
|
|
installdir = args["path"]
|
|
if installdir is None:
|
|
installdir = defaultPath()
|
|
formdir = args["formdir"]
|
|
if formdir is None:
|
|
formdir = defaultConfig()
|
|
# getting None again is OK
|
|
print("Chose directory " + installdir + " for installing. ",
|
|
"(Installing is just creating symlinks to the scripts.)")
|
|
createProgramSymlinks(installdir)
|
|
writeConfig(installdir, formdir)
|
|
checkpdftk()
|
|
print("Everything successfully installed. You should be able to " +
|
|
"start fillform, reise and uninstall-fillpdf now.")
|
|
|