// IfsTelnetPress.bcpl -- Executive Press-printing commands
// Copyright Xerox Corporation 1979, 1980, 1981

// Last modified November 30, 1981  10:15 AM by Taft

get "Ifs.decl"
get "IfsPress.decl"
get "IfsFiles.decl"
get "IfsDirs.decl"
get "IfsRs.decl"
get "Pup0.decl"
get "Streams.d"

external
[
// outgoing procedures
ExecPress; EnqueuePressRequest;

// incoming procedures
TelnetCommandLoop; TelnetSubcommandPrompt; TelnetAborting; TelnetConfirm
CollectFilenames; NextFile; DestroyFGD; Subcommands
CreateKeywordTable; InsertKeyword; DestroyKeywordTable
GetString; GetNumber
IFSOpenFile; OpenIFSStream; CloseIFSStream; GetDIF; WheelCall
EnumeratePupAddresses; ResetStrings
FileLength; SetFilePos; KsBufferAddress; CurrentPos; WriteBlock
ExtractSubstring; ConcatenateStrings; CopyString; StringCompare
Ws; Wss; Puts; Closes; Errors; IFSPrintError; DoubleIncrement; PutTemplate
SysAllocateZero; SysFree; FreePointer; MoveBlock; Zero
ReadCalendar; TruePredicate

// incoming statics
@pps; dsp; CtxRunning
]

//----------------------------------------------------------------------------
let ExecPress(cs) be
//----------------------------------------------------------------------------
// Press <filenames> [, <subcommands> ]
[
unless pps>>PPS.enable do Errors(cs, 0)

Wss(cs, " (files) ")
let fgd = CollectFilenames(cs)

let options = vec lenPressOpt; Zero(options, lenPressOpt)
options>>PressOpt.copies = 1
options>>PressOpt.requestorName =
 ExtractSubstring(CtxRunning>>RSCtx.userInfo>>UserInfo.userName)

let dif = GetDIF(options>>PressOpt.requestorName, true) // find only real DIF
if dif ne 0 then
   [
   if dif>>DIF.defaultPrinter.length ne 0 then
      [
      options>>PressOpt.host = ExtractSubstring(lv dif>>DIF.defaultPrinter)
      PutTemplate(dsp, "*n[Sending to $S]", options>>PressOpt.host)
      ]
   SysFree(dif)
   ]

let warn = options>>PressOpt.host eq 0 & not Subcommands(fgd)
   [ // repeat
   if warn then
      Ws("*n[Use the 'Server' sub-command to specify a printing server]")
   if options>>PressOpt.host eq 0 % Subcommands(fgd) then
      [
      let kt = CreateKeywordTable(5)
      InsertKeyword(kt, "Server")!0 = 0
      InsertKeyword(kt, "Copies")!0 = 1
      InsertKeyword(kt, "Printed-by")!0 = 2
      InsertKeyword(kt, "Duplex")!0 = 3
      InsertKeyword(kt, "Password")!0 = 4
      TelnetCommandLoop(kt, TelnetSubcommandPrompt(), true, options, 0, 0,
       PressSubcommand)
      DestroyKeywordTable(kt)
      ]
   warn = true
   ] repeatuntil options>>PressOpt.host ne 0 % TelnetAborting()

ResetStrings()
until TelnetAborting() do
   [
   let fd = NextFile(fgd)
   if fd eq 0 break
   let ec = EnqueuePressRequest(fd, options, TelnetConfirm, true)
   if ec ne 0 then IFSPrintError(dsp, ec);
   ]
FreePointer(lv options>>PressOpt.host, lv options>>PressOpt.requestorName,
 lv options>>PressOpt.holdPassword)
DestroyFGD(fgd)
]

//----------------------------------------------------------------------------
and EnqueuePressRequest(fd, options, ConfirmProc, display) = valof
//----------------------------------------------------------------------------
[
//  (Send misc text to dsp if 'display' is true.)
let ec = nil
let stream = OpenIFSStream(fd, lv ec)
if display ne 0 then
   PutTemplate(dsp, "*n $S -- ", lv fd>>FD.dr>>DR.pathName)
if stream eq 0 then resultis ec

// Check if file is in Press format.
let pos = vec 1
FileLength(stream, pos)
let ok = (pos!1 & 777B) eq 0 & (pos!0 ne 0 % pos!1 ne 0)
let nPages = nil
if ok then
   [
   DoubleIncrement(pos, -512)
   SetFilePos(stream, pos)
   let ddv = KsBufferAddress(stream) + CurrentPos(stream) rshift 1
   ok = ok & ddv>>DDV.Passwd eq PressPasswd
   nPages = ddv>>DDV.nParts-1
   ]
CloseIFSStream(stream)
unless ok do resultis ecPressFormat
if display then PutTemplate(dsp, "$D-page document", nPages)

unless ConfirmProc() do resultis 0

// Construct PQE
let pqe = SysAllocateZero(maxLenPQE)
pqe>>PQE.length = offset PQE.vlArgs/16
pqe>>PQE.fileName = AppendStringToPQE(pqe, lv fd>>FD.dr>>DR.pathName)
pqe>>PQE.requestorName = AppendStringToPQE(pqe,
 options>>PressOpt.requestorName)
pqe>>PQE.status = ecPQEPending
pqe>>PQE.copies = options>>PressOpt.copies
pqe>>PQE.pages = nPages
ReadCalendar(lv pqe>>PQE.time)
if options>>PressOpt.holdPassword ne 0 then
 pqe>>PQE.holdPassword = AppendStringToPQE(pqe,options>>PressOpt.holdPassword)
pqe>>PQE.duplex = options>>PressOpt.duplex
if pqe>>PQE.duplex % pqe>>PQE.holdPassword ne 0 then pqe>>PQE.spruce11 = true;

// Attempt to append it to the Press queue file for host
for i = 1 to 10 do
   [
   stream = WheelCall(IFSOpenFile, options>>PressOpt.host, lv ec, modeAppend,
    0, 0, 0, "System>Press")
   if stream ne 0 then
      [
      WriteBlock(stream, pqe, pqe>>PQE.length)
      Closes(stream)
      pps>>PPS.somethingToDo = true
      pps>>PPS.timer = 0
      break
      ]
   if ec ne ecFileBusy break
   ]
SysFree(pqe)
if ec ne 0 & display then Ws("*n -- failed:");
resultis ec;
]

//----------------------------------------------------------------------------
and PressSubcommand(cs, entry, options) be
//----------------------------------------------------------------------------
[
Puts(dsp, $*s)
switchon entry!0 into
   [
   case 0:  // Server
      [
      let host = GetString(cs, 0, Wss, "host name or net#host#")
      let port = vec lenPort
      if EnumeratePupAddresses(host, 0, port, true) ne 0 %
       port>>Port.host eq 0 then
         [ SysFree(host); Errors(cs, 0) ]
      FreePointer(lv options>>PressOpt.host)
      options>>PressOpt.host = host
      endcase
      ]
   case 1:  // Copies
      [
      let copies = GetNumber(cs)
      if copies le 0 % copies gr 1000 then Errors(cs, 0)
      options>>PressOpt.copies = copies
      endcase
      ]
   case 2:  // Printed-by
      [
      let name = GetString(cs, 0, Wss, "user name")
      if name>>String.length gr maxLenRequestorName then
         [ SysFree(name); Errors(cs, 0); ]
      FreePointer(lv options>>PressOpt.requestorName)
      options>>PressOpt.requestorName = name
      endcase
      ]
   case 3:
      [
      options>>PressOpt.duplex = true
      endcase
      ]
   case 4:
      [
      let password = GetString(cs, 0, Wss, "password")
      if password>>String.length gr maxLenHoldPassword then
         [ SysFree(password); Errors(cs, 0); ]
      FreePointer(lv options>>PressOpt.holdPassword)
      options>>PressOpt.holdPassword = password
      endcase
      ]
   ]
]

//----------------------------------------------------------------------------
and AppendStringToPQE(pqe, string) = valof
//----------------------------------------------------------------------------
// Appends string to vlArgs portion of pqe, updates pqe.length, and
// returns the pqe-relative offset of the copied string.
// pqe had better be long enough, as no bounds check is done.
[
let lenPQE = pqe>>PQE.length
let lenString = string>>String.length rshift 1 +1
MoveBlock(pqe+lenPQE, string, lenString)
pqe>>PQE.length = lenPQE+lenString
resultis lenPQE
]