summaryrefslogtreecommitdiff
path: root/support/tlcockpit/src/main/scala/TeXLive/TlmgrProcess.scala
blob: 3040d3abf748f73f159ae28893c0c0205a4184d8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// TLCockpit
// Copyright 2017-2018 Norbert Preining
// Licensed according to GPLv3+
//
// Front end for tlmgr

package TeXLive

import java.io._

import TeXLive.OsTools._
import com.typesafe.scalalogging.LazyLogging

import scala.collection.mutable.ArrayBuffer
import scala.concurrent.SyncVar
import scala.sys.process._

class TlmgrProcess(updout: String => Unit, upderr: String => Unit)  extends LazyLogging  {
  val inputString = new SyncVar[String]                 // used for the tlmgr process input
  // val outputString = new SyncVar[String]                // used for the tlmgr process output
  // val errorBuffer: StringBuffer = new StringBuffer()    // buffer used for both tmgr process error console AND logging

  val tlroot: String = "kpsewhich -var-value SELFAUTOPARENT".!!.trim
  logger.debug("tlroot ==" + tlroot + "==")

  // set in only one place, in the main thread
  var process: Process = _

  def send_command(input: String): Unit = {
    logger.debug(s"send_command: ${input}")
    try {
      assert(!inputString.isSet)
      inputString.put(input)
    } catch {
      case exc: Throwable =>
        logger.warn("Main thread: " +
          (if (exc.isInstanceOf[NoSuchElementException]) "Timeout" else "Exception: " + exc))
    }
  }

  def isAlive(): Boolean = {
    if (process != null)
      process.isAlive()
    else
      // return true on not-started process
      true
  }

  def start_process(): Boolean = {
    logger.debug("tlmgr process: entering starting process")
    // process creation
    if (process == null) {
      logger.debug("tlmgr process: start_process: creating procIO")
      val procIO = new ProcessIO(inputFn, outputFn(_, updout), outputFn(_, upderr))
      val startCmd =
        if (isWindows) {
          logger.debug("detected Windows, using tlmgr.bat")
          Seq("tlmgr.bat", "-v", "--machine-readable", "shell")
        } else if (isCygwin) {
          logger.debug("detected Cygwin, using bash -l -c tlmgr")
          Seq("bash", "-l", "-c", "tlmgr -v --machine-readable shell")
        } else {
          logger.debug("detected Unixish, using tlmgr")
          Seq("tlmgr", "-v", "--machine-readable", "shell")
        }
      val processBuilder: ProcessBuilder = startCmd
      logger.debug("tlmgr process: start_process: running new tlmgr process")
      process = processBuilder.run(procIO)
    }
    logger.debug("tlmgr process: start_process: checking for being alive")
    process.isAlive()
  }

  def cleanup(): Unit = {
    if (process != null) {
      logger.debug("tlmgr process: cleanup - sending quit")
      send_command("quit")
      logger.debug("tlmgr process: cleanup - sleeping 2s")
      Thread.sleep(1000)
      logger.debug("tlmgr process: cleanup - destroying process")
      process.destroy()
    }
  }

  /* The standard input passing function */
  private[this] def inputFn(stdin: OutputStream): Unit = {
    val writer = new BufferedWriter(new OutputStreamWriter(stdin))
    try {
      var input = ""
      while (true) {
        input = inputString.take()
        if (input == "quit") {
          stdin.close()
          return
        } else {
          writer.write(input + "\n")
          logger.trace("writing " + input + " to process")
          writer.flush()
        }
      }
      stdin.close()
    } catch {
      case exc: Throwable =>
        logger.warn("Exception in inputFn thread: " + exc + "\n")
        stdin.close()
    }
  }

  private[this] def outputFn(outStr: InputStream, updfun: String => Unit): Unit = {
    val reader = new BufferedReader(new InputStreamReader(outStr))
    try {
      var line: String = ""
      while (true) {
        line = reader.readLine
        logger.trace("DDD did read " + line + " from process")
        try {
          updfun(line)
        } catch {
          case exc: Throwable =>
            logger.debug("Update output line function failed, continuing anyway. Exception: " + exc)
        }
      }
      outStr.close()
    } catch {
      case exc: Throwable =>
        logger.warn("Exception in outputFn thread: " + exc + "\n")
        outStr.close()
    }
  }
}