summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/support/tlcockpit/src/main/scala/TeXLive/TlmgrProcess.scala
blob: 7d84e6dd0086277081df1e06aba137f87c3bcffd (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
package TeXLive

import java.io._

import TeXLive.OsTools._

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

class TlmgrProcess(updout: String => Unit, upderr: String => Unit) {
  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 = "kpsewhich -var-value SELFAUTOPARENT".!!.trim
  // println("tlroot ==" + tlroot + "==")

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

  def send_command(input: String): Unit = {
    try {
      assert(!inputString.isSet)
      inputString.put(input)
    } catch {
      case exc: Throwable =>
        upderr("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 = {
    // process creation
    if (process == null) {
      val procIO = new ProcessIO(inputFn(_), outputFn(_, updout), outputFn(_, upderr))
      val processBuilder: ProcessBuilder = Seq({if (isWindows) "tlmgr.bat" else "tlmgr"}, "-v", "--machine-readable", "shell")
      process = processBuilder.run(procIO)
    }
    process.isAlive()
  }

  def cleanup(): Unit = {
    if (process != null) {
      send_command("quit")
      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")
          // println("writing " + input + " to process")
          writer.flush()
        }
      }
      stdin.close()
    } catch {
      case exc: Throwable =>
        stdin.close()
        // println("Exception in inputFn thread: " + exc + "\n")
        upderr("Input thread: Exception: " + exc + "\n")
    }
  }

  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
        // println("DDD did read " + line + " from process")
        try {
          updfun(line)
        } catch {
          case exc: Throwable =>
            upderr("Update output line function failed, continuing anyway. Exception: " + exc)
        }
      }
      outStr.close()
    } catch {
      case exc: Throwable =>
        outStr.close()
        upderr("Output thread: Exception: " + exc + "\n")
    }
  }
}