(DEFINE-FILE-INFO :PACKAGE "INTERLISP" :READTABLE "INTERLISP" :BASE 10)

(FILECREATED "29-Apr-2026 23:49:14" {MEDLEY}<library>tedit>TEDIT-FILE.;684 174888 

      :EDIT-BY rmk

      :CHANGES-TO (FNS \TEDIT.INTERPRET.MCCS.SHIFTS)

      :PREVIOUS-DATE "24-Apr-2026 21:09:13" {MEDLEY}<library>tedit>TEDIT-FILE.;683)


(PRETTYCOMPRINT TEDIT-FILECOMS)

(RPAQQ TEDIT-FILECOMS
       [(DECLARE%: EVAL@COMPILE DONTCOPY (CONSTANTS (\PieceDescriptorLOOKS 0)
                                                (\PieceDescriptorOBJECT 1)
                                                (\PieceDescriptorPARA 2)
                                                (\PieceDescriptorPAGEFRAME 3)
                                                (\PieceDescriptorCHARLOOKSLIST 4)
                                                (\PieceDescriptorPARALOOKSLIST 5)
                                                (\PieceDescriptorSAFEOBJECT 6)
                                                (\PieceDescriptorMETAINFO 7)
                                                (\PieceDescriptorPROPERTIES 8))
               (EXPORT (MACROS \SMALLPIN \SMALLPOUT))
               (RECORDS \TEDIT.FILETRAILER))
        (COMS 
              (* ;; "Public entries ")

              (FNS TEDIT.GET TEDIT.FORMATTEDFILEP TEDIT.FILEDATE TEDIT.INCLUDE TEDIT.RAW.INCLUDE 
                   TEDIT.PUT TEDIT.PUT.STREAM)
              (FNS \TEDIT.GET.FOREIGN.FILE \TEDIT.GET.UNFORMATTED.FILE \TEDIT.GET.FORMATTED.FILE 
                   \TEDIT.FORMATTEDSTREAMP \ARBIN \ATMIN \DWIN \STRINGIN \TEDIT.GET.TRAILER 
                   \TEDIT.CACHEFILE)
              [COMS 
                    (* ;; 
  "Until CL:COMPILE-FILE and any others are updated, They should use the public TEDIT.FORMATTEDFILEP")

                    (P (MOVD? '\TEDIT.GET.TRAILER '\TEDIT.FORMATTEDP1]
              (FNS \TEDIT.GET.PIECES3 \TEDIT.GET.PROPS3 \TEDIT.MAKE.STRINGPIECE)
              (FNS \TEDIT.GET.UNFORMATTED.FILE.MCCS \TEDIT.INTERPRET.MCCS.SHIFTS 
                   \TEDIT.CONVERT.XCCSTOMCCS \TEDIT.RUN.TO.STRINGPIECE)
                                                             (* ; "MCCS")
              (FNS \TEDIT.GET.UNFORMATTED.FILE.UTF8)
                                                             (* ; "UTF-8")
              (FNS \TEDIT.GET.CHARLOOKS.LIST \TEDIT.GET.SINGLE.CHARLOOKS \TEDIT.GET.CHARLOOKS 
                   \TEDIT.GET.PARALOOKS.INDEX \TEDIT.GET.CHARLOOKS.INDEX)
              (FNS \TEDIT.GET.PARALOOKS.LIST \TEDIT.GET.SINGLE.PARALOOKS)
              (FNS \TEDIT.GET.OBJECT))
        (COMS 
              (* ;; "Putting pageframe functions are on TEDIT-PAGE)")

              (FNS \TEDIT.PUT.PCTB \TEDIT.PUT.PCTB.PIECEDATA \TEDIT.PUT.TRAILER 
                   \TEDIT.PUT.PCTB.MERGEABLE \TEDIT.PUT.UTF8.SPLITPIECES \TEDIT.PUT.MCCS.SPLITPIECES
                   \TEDIT.PUT.PCTB.NEXTNEW \TEDIT.INSERT.NEWPIECES \TEDIT.PUTRESET \ARBOUT \ATMOUT 
                   \DWOUT \STRINGOUT)
              (FNS \TEDIT.PUT.CHARLOOKS.LIST \TEDIT.PUT.SINGLE.CHARLOOKS \TEDIT.PUT.CHARLOOKS 
                   \TEDIT.PUT.CHARLOOKS1 \TEDIT.PUT.OBJECT)
              (FNS \TEDIT.PUT.PARALOOKS.LIST \TEDIT.PUT.SINGLE.PARALOOKS \TEDIT.PUT.PARALOOKS))
        (GLOBALVARS TEDIT.INPUT.FORMATS *TEDIT-FILE-READTABLE*)
        (FNS TEDITFROMLISPSOURCE SHELLSCRIPTP TEDITFROMSHELLSCRIPT)
        (INITVARS (TEDIT.SOURCE.LINELENGTH 110)
               (TEDIT.SOURCE.NLINES 30))
        (ADDVARS (TEDIT.INPUT.FORMATS (LISPSOURCEFILEP TEDITFROMLISPSOURCE)
                        (SHELLSCRIPTP TEDITFROMSHELLSCRIPT)))
        (INITVARS                                            (* ; 
                                         "For consistent reading and writing of info on TEdit files.")
               (*TEDIT-FILE-READTABLE* (COPYREADTABLE \ORIGREADTABLE])
(DECLARE%: EVAL@COMPILE DONTCOPY 
(DECLARE%: EVAL@COMPILE 

(RPAQQ \PieceDescriptorLOOKS 0)

(RPAQQ \PieceDescriptorOBJECT 1)

(RPAQQ \PieceDescriptorPARA 2)

(RPAQQ \PieceDescriptorPAGEFRAME 3)

(RPAQQ \PieceDescriptorCHARLOOKSLIST 4)

(RPAQQ \PieceDescriptorPARALOOKSLIST 5)

(RPAQQ \PieceDescriptorSAFEOBJECT 6)

(RPAQQ \PieceDescriptorMETAINFO 7)

(RPAQQ \PieceDescriptorPROPERTIES 8)


(CONSTANTS (\PieceDescriptorLOOKS 0)
       (\PieceDescriptorOBJECT 1)
       (\PieceDescriptorPARA 2)
       (\PieceDescriptorPAGEFRAME 3)
       (\PieceDescriptorCHARLOOKSLIST 4)
       (\PieceDescriptorPARALOOKSLIST 5)
       (\PieceDescriptorSAFEOBJECT 6)
       (\PieceDescriptorMETAINFO 7)
       (\PieceDescriptorPROPERTIES 8))
)

(* "FOLLOWING DEFINITIONS EXPORTED")(DECLARE%: EVAL@COMPILE 

(PUTPROPS \SMALLPIN MACRO (OPENLAMBDA (STREAM)
                            (SIGNED (create WORD
                                           HIBYTE _ (\BIN STREAM)
                                           LOBYTE _ (\BIN STREAM))
                                   BITSPERWORD)))

(PUTPROPS \SMALLPOUT MACRO (OPENLAMBDA (STREAM W)            (* ; "Signed smallp, unlike \WOUT")
                             (\BOUT STREAM (LOGAND 255 (LRSH W 8)))
                             (\BOUT STREAM (LOGAND W 255))))
)

(* "END EXPORTED DEFINITIONS")


(DECLARE%: EVAL@COMPILE

(RECORD \TEDIT.FILETRAILER (PIECESTART TRAILERSIZE VERSION PCCOUNT IDATE PROPS))
)
)



(* ;; "Public entries ")

(DEFINEQ

(TEDIT.GET
  [LAMBDA (TSTREAM FILE UNFORMATTED? PROPS)                  (* ; "Edited 17-Jul-2025 00:19 by rmk")
                                                             (* ; "Edited 19-Apr-2025 10:31 by rmk")
                                                             (* ; "Edited  6-Apr-2025 14:26 by rmk")
                                                             (* ; "Edited 14-Mar-2025 11:52 by rmk")
                                                             (* ; "Edited 26-Aug-2024 16:15 by rmk")
                                                             (* ; "Edited 11-Aug-2024 12:13 by rmk")
                                                             (* ; "Edited 29-Jun-2024 16:30 by rmk")
                                                             (* ; "Edited 18-May-2024 16:31 by rmk")
                                                             (* ; "Edited 12-May-2024 21:33 by rmk")
                                                             (* ; "Edited 17-Mar-2024 18:17 by rmk")
                                                             (* ; "Edited 29-Apr-2024 10:15 by rmk")
                                                             (* ; "Edited 15-Mar-2024 13:34 by rmk")
                                                             (* ; "Edited 21-Jan-2024 23:13 by rmk")
                                                             (* ; "Edited 22-Sep-2023 20:16 by rmk")
                                                             (* ; "Edited 18-Sep-2023 16:41 by rmk")
                                                             (* ; "Edited  9-Sep-2023 17:24 by rmk")
                                                            (* ; "Edited 19-May-2001 11:43 by rmk:")
                                                             (* ; "Edited 19-Apr-93 13:12 by jds")

    (* ;; "A new file overwrites the textstream,textobj, and window of the one being edited.  We have to make a new TEXTOBJ because we don't want the new file to inherit random properties (like READONLY etc.  (Not sure about BEING-EDITED, that may only have been used for window-creation)")

    (SETQ TSTREAM (TEXTSTREAM TSTREAM))
    (RESETLST
        (PROG ((TEXTOBJ (TEXTOBJ TSTREAM))
               FSTREAM GETFN MAINWINDOW ACTIVE PROC NTSTREAM NTEXTOBJ TEDITCREATED)
              [RESETSAVE (\TEDIT.MARKACTIVE TEXTOBJ "Get")
                     '(PROGN (\TEDIT.MARKINACTIVE OLDVALUE]
              (CL:WHEN [AND (GETTOBJ TEXTOBJ \DIRTY)
                            (PROGN (TEDIT.PROMPTCLEAR TEXTOBJ)
                                   (NOT (MOUSECONFIRM "Not saved yet; LEFT go Get anyway." T
                                               (GETTOBJ TEXTOBJ PROMPTWINDOW]

                  (* ;; "Only do the GET if he knows he'll zorch himself.")

                  (RETURN))
              [SETQ FILE (\TEDIT.MAKEFILENAME (OR FILE (TEDIT.GETINPUT TEXTOBJ "GET from: "
                                                              (OR (GETTEXTPROP TEXTOBJ 
                                                                         'LASTGETFILENAME)
                                                                  (\TEDIT.LIKELY.FILENAME TEXTOBJ]
              (CL:UNLESS FILE
                  (TEDIT.PROMPTPRINT TEXTOBJ "No input file--aborted" T T)
                  (RETURN))
              (CL:WHEN [AND (SETQ GETFN (GETTEXTPROP TEXTOBJ 'GETFN))
                            (EQ 'DON'T (APPLY* GETFN TSTREAM (FULLNAME FILE)
                                              'BEFORE]       (* ; 
                                                      "He doesn't want this document put.  Bail out.")
                  (RETURN))

         (* ;; "")

              (SETQ FSTREAM (\TEDIT.OPENTEXTFILE FILE))
              (CL:UNLESS (AND (STREAMP FSTREAM)
                              (\GETSTREAM FSTREAM 'INPUT T)) (* ; 
                                        "Didn't find it but save the name as a hint for the next try")
                  (PUTTEXTPROP TEXTOBJ 'LASTGETFILENAME FILE)
                  (TEDIT.PROMPTPRINT TEXTOBJ (CONCAT FILE " not found--aborted")
                         T)
                  (RETURN NIL))
              (RESETLST
                  (RESETSAVE (TTYDISPLAYSTREAM (OR (AND (NEQ 'DON'T (GETTOBJ TEXTOBJ PROMPTWINDOW))
                                                        (GETTOBJ TEXTOBJ PROMPTWINDOW))
                                                   PROMPTWINDOW)))

                  (* ;; "New file is good, clean out the old stuff")

                  (\TEDIT.NOSEL TSTREAM)
                  (\TEDIT.TEXTCLOSEF TEXTOBJ)                (* ; 
                                                             "Close the old files, still in TXTFILE")

                  (* ;; "")

                  (* ;; "Open a textstream NTSTREAM on the new file, then reconnect its textobj to the old TSTREAM and window")

                  (SETQ ACTIVE (GETTEXTPROP TEXTOBJ 'ACTIVE))
                  (SETQ MAINWINDOW (\TEDIT.MAINW TEXTOBJ))
                  (CL:WHEN MAINWINDOW
                      (TEDIT.KILL TEXTOBJ)
                      (SETQ TEDITCREATED (WINDOWPROP MAINWINDOW 'TEDITCREATED)))
                  (CL:WHEN UNFORMATTED?
                      (push PROPS 'CLEARGET T))
                  (SETQ NTSTREAM (OPENTEXTSTREAM FSTREAM MAINWINDOW NIL NIL PROPS))
                  (SETQ NTEXTOBJ (fetch (TEXTSTREAM TEXTOBJ) of NTSTREAM))
                  (replace (TEXTSTREAM TEXTOBJ) of TSTREAM with NTEXTOBJ)
                  (SETTOBJ NTEXTOBJ STREAMHINT TSTREAM)
                  (\TEDIT.TEXTSETFILEPTR TSTREAM 0)
                  (CL:WHEN MAINWINDOW
                      (\TEDIT.UPDATE.TITLE NTEXTOBJ)         (* ; "find and set the title")
                      (WINDOWPROP MAINWINDOW 'TEDITCREATED TEDITCREATED)
                                                             (* ; "To keep the default region")
                      (WINDOWPROP MAINWINDOW 'TEXTSTREAM TSTREAM))
                  (CL:WHEN ACTIVE (TEDIT TSTREAM)))
              (CL:WHEN GETFN
                  (APPLY* GETFN TSTREAM (FULLNAME (GETTOBJ TEXTOBJ TXTFILE))
                         'AFTER))
              (RETURN TSTREAM)))])

(TEDIT.FORMATTEDFILEP
  [LAMBDA (FILE)                                             (* ; "Edited 26-Mar-2024 22:10 by rmk")
                                                             (* ; "Edited 18-Jan-2024 10:29 by rmk")
                                                             (* ; "Edited 13-Jan-2024 11:57 by rmk")
                                                             (* ; "Edited 12-Jul-2023 23:35 by rmk")

    (* ;; "If FILE is a Tedit formatted stream or the name of a Tedit formatted file, returns a pair consistening of its version number and piececount.  Piececount is probably useless.")

    (CL:WHEN FILE
        (RESETLST
            (LET ((STREAM (GETSTREAM FILE 'INPUT T)))
                 [if STREAM
                     then [RESETSAVE (GETFILEPTR FILE)
                                 `(PROGN (SETFILEPTR ,FILE OLDVALUE]
                   else (RESETSAVE (SETQ STREAM (OPENSTREAM (CL:IF (STREAMP FILE)
                                                                (FULLNAME FILE)
                                                                FILE)
                                                       'INPUT))
                               `(PROGN (CLOSEF? OLDVALUE]
                 (\TEDIT.GET.TRAILER STREAM))))])

(TEDIT.FILEDATE
  [LAMBDA (FILE INTEGER)                                     (* ; "Edited  8-Sep-2025 18:26 by rmk")
                                                             (* ; "Edited 26-Mar-2024 21:37 by rmk")
                                                             (* ; "Edited 18-Jan-2024 10:26 by rmk")
                                                             (* ; "Edited 13-Jan-2024 10:20 by rmk")
                                                             (* ; "Edited 19-Dec-2023 10:13 by rmk")
                                                             (* ; "Edited  6-Dec-2023 20:11 by rmk")
                                                             (* ; "Edited 28-Sep-2023 22:47 by rmk")

    (* ;; "If FILE is a Tedit-format file, returns its save date if it is stamped in the file, otherwise its file-system creation date as an integer or string.  NIL if not a Tedit file.")

    (* ;; "FILE must be random access.  If not, then presumably we first have to fetch the last 5+4+8 bytes to someplace else.")

    (LET [(IDATE (CAR (NTH (TEDIT.FORMATTEDFILEP FILE)
                           5]
         (CL:WHEN IDATE
             (CL:IF INTEGER
                 IDATE
                 (GDATE IDATE)))])

(TEDIT.INCLUDE
  [LAMBDA (TSTREAM FILE START END SAFE PLAINTEXT)            (* ; "Edited  8-Feb-2025 20:56 by rmk")
                                                             (* ; "Edited 25-Nov-2024 20:17 by rmk")
                                                             (* ; "Edited 22-Sep-2024 18:43 by rmk")
                                                             (* ; "Edited 11-Aug-2024 12:30 by rmk")
                                                             (* ; "Edited  7-Jul-2024 22:03 by rmk")
                                                             (* ; "Edited  2-Jul-2024 10:48 by rmk")
                                                             (* ; "Edited 29-Jun-2024 10:29 by rmk")
                                                             (* ; "Edited 22-May-2024 14:03 by rmk")
                                                             (* ; "Edited 29-Apr-2024 10:17 by rmk")
                                                             (* ; "Edited 17-Mar-2024 12:06 by rmk")
                                                             (* ; "Edited 16-Feb-2024 23:54 by rmk")
                                                             (* ; "Edited 13-Jan-2024 09:39 by rmk")
                                                             (* ; "Edited 12-Nov-2023 12:29 by rmk")
                                                             (* ; "Edited 23-Jul-2023 15:30 by rmk")
                                                             (* ; "Edited 16-Jul-2023 10:18 by rmk")
                                                             (* ; "Edited 21-Jun-2023 17:46 by rmk")
                                                            (* ; "Edited 19-May-2001 11:43 by rmk:")
                                                             (* ; 
                                                        "Edited  1-Jun-93 11:31 by sybalsky:mv:envos")

    (* ;; "Obtain a file name, and include that file's contents at the place where the caret is.")

    (* ;; "This is a documented entry, but SAFE wasn't described there and I (RMK) added PLAINTEXT to collapse with TEDIT.INCLUDE.RAW.")

    (* ;; 
   "Returns the length of the input, if the insertion happened, NIL if there was no place to put it.")

    (* ;; "")

    (* ;; "This assumes that START and END are file positions (defaulting to 0 and length), not character numbers.")

    (* ;; "")

    (* ;; "If SAFE, the caller is taking responsibility for closing FILE when its contents are no longer needed (e.g. DOC-OBJECTS inclusions).  Otherwise, the contents are copied to a NODIRCORE that is owned by this TSTREAM, and then FILE is closed here if it wasn't previously open.  (This may not be accurate, unless FILE was actually an open stream and not a name?)")

    (SETQ TSTREAM (TEXTSTREAM TSTREAM))
    (RESETLST
        (PROG ((TOOBJ (fetch (TEXTSTREAM TEXTOBJ) of TSTREAM))
               TSEL FSEL WASOPEN FTSTREAM NDCSTREAM (FROMFILE FILE))
              [RESETSAVE (\TEDIT.MARKACTIVE TOOBJ "Include")
                     `(PROGN (\TEDIT.MARKINACTIVE OLDVALUE]
              (SETQ TSEL (TEXTSEL TOOBJ))
              (CL:UNLESS (GETSEL TSEL SET)
                  (TEDIT.PROMPTPRINT TOOBJ "Please select a destination for the included text" T)
                  (RETURN NIL))
              (CL:WHEN (\TEDIT.READONLY TOOBJ NIL (FGETSEL TSEL CH#))
                     (RETURN NIL))

         (* ;; "We know where the new text is supposed to go. Where is it coming from?")

              (CL:UNLESS FROMFILE
                  (SETQ FROMFILE (\TEDIT.MAKEFILENAME (TEDIT.GETINPUT TOOBJ "Include from: ")))
                  (CL:UNLESS FROMFILE
                      (TEDIT.PROMPTPRINT TOOBJ "No file to include--aborted]" T)
                      (RETURN)))
              (CL:UNLESS (OR (STREAMP FROMFILE)
                             (OR (FINDFILE FROMFILE)
                                 (FINDFILE-WITH-EXTENSIONS FROMFILE NIL *TEDIT-EXTENSIONS*)))
                                                             (* ; "File not found")
                  (TEDIT.PROMPTPRINT TOOBJ (CONCAT FROMFILE " not found--include aborted")
                         T T)
                  (RETURN))

         (* ;; "")

         (* ;; "Now we have the FROMFILE, which may be a stream.")

              (CL:UNLESS START (SETQ START 0))
              (CL:UNLESS END
                  (SETQ END (GETFILEINFO FILE 'LENGTH)))
              (CL:UNLESS (IGEQ END START)
                  (TEDIT.PROMPTPRINT TOOBJ "Negative number of characters to include--aborted" T T)
                  (RETURN))

         (* ;; "")

         (* ;; "If the caller says SAFE, he's guranteeing that the file will be there at least as long as we need it.  Otherwise, we take ownership of the information by copying it to a NODIRCORE. ")

              (CL:UNLESS SAFE
                  [if (\GETSTREAM FROMFILE 'INPUT T)
                      then (SETQ WASOPEN T)
                    else                                     (* ; 
                                                        "Wasn't open -- need to open it for input...")
                         (SETQ FROMFILE (OPENSTREAM FROMFILE 'INPUT NIL '((TYPE TEXT]

                  (* ;; "Create our holding file and copy the file-section into it.")

                  (SETQ NDCSTREAM (OPENSTREAM '{NODIRCORE} 'OUTPUT 'NEW))

                  (* ;; "Have to explicitly fill in 0 and EOFPTR, because if the file was open already, NILs would only copy from current fileptr to EOF.")

                  (* ;; 
            "Use COPYBYTES for formatted files, otherwise allow natural EOL conversion to take place")

                  (if (\TEDIT.GET.TRAILER FROMFILE)
                      then (COPYBYTES FROMFILE NDCSTREAM START END)
                    else (COPYCHARS FROMFILE NDCSTREAM START END))
                  (CL:UNLESS WASOPEN (CLOSEF FROMFILE))      (* ; 
                                                      "If the file didn't come to us open, close it.")
                  (CLOSEF NDCSTREAM)
                  (SETQ START 0)                             (* ; 
                                                             "But we now want everything we copied")
                  (SETQ END (GETFILEINFO NDCSTREAM 'LENGTH))
                  (SETQ FROMFILE NDCSTREAM))

         (* ;; "")

         (* ;; "FROMFILE is now a safe file or stream, possibly already open.  If it wasn't open before, we want to make sure it gets closed if/when this event gets undone.")

              (CL:UNLESS (\GETSTREAM FROMFILE 'INPUT T)
                  (SETQ FROMFILE (OPENSTREAM FROMFILE 'INPUT))
                  (\TEDIT.HISTORYADD TOOBJ (\TEDIT.HISTORY.EVENT TOOBJ :Closefile NIL NIL NIL NIL 
                                                  FROMFILE)))
              [SETQ FTSTREAM (OPENTEXTSTREAM FROMFILE NIL START END
                                    `(FONT ,(\TEDIT.GET.INSERT.CHARLOOKS TOOBJ TSEL)
                                           PARALOOKS
                                           ,(GETTOBJ TOOBJ DEFAULTPARALOOKS)
                                           PLAINTEXT
                                           ,PLAINTEXT]

         (* ;; "")

         (* ;; "FTSTREAM is now a text stream for the source. The COPYSEL is so that this doesn't smash the current FTSTREAM SEL, if the stream previously existed.")

              [SETQ FSEL (\TEDIT.COPYSEL (TEXTSEL (TEXTOBJ FTSTREAM]
                                                             (* ; "Select START to END")
              (\TEDIT.UPDATE.SEL FSEL 1 (TEXTLEN (TEXTOBJ FTSTREAM))
                     'LEFT)
              (\TEDIT.COPY FSEL TSEL FTSTREAM TSTREAM)
              (RETURN (FGETSEL FSEL DCH))))])

(TEDIT.RAW.INCLUDE
  [LAMBDA (TSTREAM INFILE START END SAFE)                    (* ; "Edited  1-May-2023 08:46 by rmk")
                                                             (* ; 
                                                        "Edited 27-May-93 16:36 by sybalsky:mv:envos")

    (* ;; "Inserts the INFILE characters betwen START and END into TSTREAM,  treating INFILE as a plain text file.  This is a documented entry, motivated by now-silly speed considerations.  But it really amounts to just calling TEDIT.INCLUDE with a (new) PLAINTEXT flag and let the OPENTEXTSTREAM plaintext reader do its thing.  I (RMK) added the SAFE argument here, consistent with the (undocumented) SAFE argument of TEDIT.INCLUDE.")

    (TEDIT.INCLUDE TSTREAM INFILE START END SAFE T])

(TEDIT.PUT
  [LAMBDA (TSTREAM FILE FORCENEW UNFORMATTED? FORMAT QUIET)  (* ; "Edited 11-Jul-2025 08:36 by rmk")
                                                             (* ; "Edited 25-Apr-2025 23:33 by rmk")
                                                             (* ; "Edited 22-Apr-2025 15:58 by rmk")
                                                             (* ; "Edited 14-Mar-2025 11:52 by rmk")
                                                             (* ; "Edited 22-Feb-2025 15:56 by rmk")
                                                             (* ; "Edited 23-Dec-2024 23:02 by rmk")
                                                             (* ; "Edited 11-Aug-2024 12:30 by rmk")
                                                             (* ; "Edited 29-Jun-2024 10:31 by rmk")
                                                             (* ; "Edited 26-Jun-2024 15:46 by rmk")
                                                             (* ; "Edited 29-Apr-2024 10:12 by rmk")
                                                             (* ; "Edited 31-Mar-2024 23:54 by rmk")
                                                             (* ; "Edited  7-Feb-2024 13:31 by rmk")
                                                             (* ; "Edited  4-Feb-2024 00:10 by rmk")
                                                             (* ; "Edited 22-Dec-2023 10:41 by rmk")
                                                             (* ; "Edited 19-Dec-2023 10:18 by rmk")
                                                             (* ; "Edited 21-Jun-99 19:02 by rmk:")
                                                             (* ; "Edited 19-Apr-93 13:04 by jds")

    (* ;; "If the guy was editing a file, make a new updated version;  else, ask for a file name")

    (* ;; "If FILE is specd, it's used;  else the user must give us one")

    (* ;; "Returns the destination stream open for input.")

    (SETQ TSTREAM (TEXTSTREAM TSTREAM))
    (if (EQ 'READONLY (\TEDIT.READONLY TSTREAM))
        then (TEDIT.PROMPTPRINT TSTREAM "Cannot save a read-only text-stream--aborted" T)
      else
      (RESETLST
          (PROG ((TEXTOBJ (fetch (TEXTSTREAM TEXTOBJ) of TSTREAM))
                 CHARSTREAM NEWPIECES PUTFN OLDEXTFORMAT NEWEXTFORMAT PUTSTRING)
                (CL:WHEN (AND (SETQ PUTFN (GETTEXTPROP TEXTOBJ 'PUTFN))
                              (EQ (APPLY* PUTFN TSTREAM (FULLNAME FILE)
                                         'BEFORE)
                                  'DON'T))

                    (* ;; "PUTFN BEFORE says it can't be saved, even though asked.  Let him know")

                    (TEDIT.PROMPTPRINT TEXTOBJ "This document cannot be saved" T T)
                    (RETURN NIL))
                (CL:UNLESS [OR (IGREATERP (TEXTLEN TEXTOBJ)
                                      0)
                               (EQ (CHARCODE Y)
                                   (CHCON1 (TEDIT.GETINPUT TEXTOBJ 
                                                  "Document is empty.  Save anyway? " "Yes"]
                       (RETURN NIL))
                (if (AND (STREAMP FILE)
                         (\GETSTREAM FILE 'OUTPUT T))
                    then (SETQ CHARSTREAM FILE)
                  else (CL:UNLESS UNFORMATTED?
                           (if (\TEDIT.FORMATTEDSTREAMP TEXTOBJ)
                               then [SETQ UNFORMATTED?
                                     (AND (GETTEXTPROP TEXTOBJ 'CLEARGET)
                                          (EQ 'N (U-CASE (NTHCHAR (TEDIT.GETINPUT TEXTOBJ 
                                                              "Convert plaintext to formatted file? "
                                                                         "No")
                                                                1]
                             else (SETQ UNFORMATTED? T)))
                       (SELECTQ FILE
                           (NIL (CL:UNLESS FORCENEW          (* ; "Forcenew for templates?")
                                    (CL:WHEN [AND (TEXTPROP TEXTOBJ 'TEMPLATE)
                                                  (EQ 'N (U-CASE (NTHCHAR (TEDIT.GETINPUT TEXTOBJ 
                                                                               "Overwrite template? "
                                                                                 "No")
                                                                        1]
                                        (SETQ FORCENEW 'DETEMPLATE)))
                                [SETQ FILE (\TEDIT.MAKEFILENAME (TEDIT.GETINPUT TEXTOBJ "Put to: "
                                                                       (CL:UNLESS FORCENEW
                                                                              (\TEDIT.LIKELY.FILENAME
                                                                               TEXTOBJ UNFORMATTED?])
                           (T (SETQ FILE (\TEDIT.LIKELY.FILENAME TEXTOBJ UNFORMATTED?)))
                           NIL)
                       (CL:UNLESS FILE                       (* ; "No file to put to.")
                           (TEDIT.PROMPTPRINT TEXTOBJ "No output file--aborted" T T)
                           (RETURN)))

           (* ;; "")

           (* ;; "Ready to save. IF the external format changes, we don't want to update the current textstream.  Unlesss we figure out what the new proper piecetypes should be (FATFILE2, UTF8...).")

                [SETQ OLDEXTFORMAT (AND (STREAMP (FGETTOBJ TEXTOBJ TXTFILE))
                                        (STREAMPROP (FGETTOBJ TEXTOBJ TXTFILE)
                                               'FORMAT]
                (SETQ NEWEXTFORMAT (OR FORMAT (GETTEXTPROP TEXTOBJ 'OUTPUT-FORMAT)
                                       OLDEXTFORMAT :DEFAULT))
                [RESETSAVE (\TEDIT.MARKACTIVE TEXTOBJ "Put")
                       '(PROGN (\TEDIT.MARKINACTIVE OLDVALUE]
                [RESETSAVE [SETQ CHARSTREAM (OPENSTREAM FILE 'OUTPUT 'NEW
                                                   `([TYPE ,(CL:IF UNFORMATTED?
                                                                'TEXT
                                                                'BINARY)]
                                                     (LINELENGTH T)
                                                     (FORMAT ,NEWEXTFORMAT]
                       '(AND RESETSTATE (DELFILE (CLOSEF? OLDVALUE]
                [RESETSAVE (\TEDIT.PUTRESET (CONS (THIS.PROCESS)
                                                  'DON'T]
                (CL:UNLESS QUIET
                    (SETQ PUTSTRING (CONCAT "Put to " (FULLNAME CHARSTREAM)
                                           "..."))
                    (TEDIT.PROMPTPRINT TEXTOBJ PUTSTRING T))

           (* ;; "")

           (* ;; "CHARSTREAM is open, we're ready to go.")

           (* ;; "We don't know how to decide that the user doesn't want to continue editing and therefore doesn't need the pieces to be updated to the new file.  The stream itself may be used in the future, even if right now there is no process or window")

                (SETQ CHARSTREAM (TEDIT.PUT.STREAM TSTREAM CHARSTREAM UNFORMATTED? NEWEXTFORMAT T))

           (* ;; "The file is written, nothing can be lost. CHARSTREAM isn't closed yet")

           (* ;; "")

           (* ;; 
       "TEDIT.PUT.FINISHEDFORMS is not documented.  Are we supposed to supply some defined specvars?")

                (for FORM in (GETTEXTPROP TEXTOBJ 'TEDIT.PUT.FINISHEDFORMS) do (EVAL FORM))
                (CL:WHEN PUTFN
                    (APPLY* PUTFN TSTREAM (FULLNAME CHARSTREAM)
                           'AFTER))

           (* ;; "")

           (* ;; "")

                (CL:UNLESS QUIET
                    (TEDIT.PROMPTPRINT TEXTOBJ (CONCAT PUTSTRING "done")
                           T))

           (* ;; "")

                (CL:WHEN (EQ FORCENEW 'DETEMPLATE)
                    (TEXTPROP TEXTOBJ 'TEMPLATE NIL))
                (RETURN CHARSTREAM)))])

(TEDIT.PUT.STREAM
  [LAMBDA (TSTREAM DESTSTREAM UNFORMATTED? EXTERNALFORMAT CONTINUE)
                                                             (* ; "Edited 30-May-2025 11:21 by rmk")
                                                             (* ; "Edited 20-Nov-2024 16:26 by rmk")
                                                             (* ; "Edited 22-Sep-2024 18:40 by rmk")
                                                             (* ; "Edited 14-May-2024 17:49 by rmk")
                                                             (* ; "Edited 29-Apr-2024 10:09 by rmk")
                                                             (* ; "Edited 19-Mar-2024 21:38 by rmk")
                                                             (* ; "Edited 17-Mar-2024 17:29 by rmk")
                                                             (* ; "Edited  7-Feb-2024 12:41 by rmk")
                                                             (* ; "Edited  4-Feb-2024 00:19 by rmk")
                                                             (* ; "")

    (* ;; " If UNFORMATTED?, the FORMATSTREAM portion of the Tedit stream is discarded, so only the plaintext character portion appears in DESTSTREAM. ")

    (* ;; "Saves the contents of TSTREAM on DESTSTREAM, starting at DESTSTREAM's current file position.  If DESTSTREAM is not a stream open for output, an attempt will be made to open it. Either way, if EXTERNALFORMAT is provided, the stream will be written with that format.  Otherwise, the current format of the stream (or the current default format, if a file) will be used.  ")

    (* ;; "If not CONTINUE and DESTSTREAM is opened here, it will be closed and its filename will be returned.  Otherwise, the DESTSTREAM will be left in its original mode and positioned after the last byte written.")
                                                             (* ; "")
    (RESETLST
        (LET ((TEXTOBJ (TEXTOBJ TSTREAM))
              NEWPIECES OPENEDHERE)
             (CL:UNLESS (\GETSTREAM DESTSTREAM 'OUTPUT T)
                 [RESETSAVE [SETQ DESTSTREAM (OPENSTREAM DESTSTREAM 'OUTPUT NIL '(LINELENGTH T]
                        '(AND RESETSTATE (DELFILE (CLOSEF? OLDVALUE]
                 (SETQ OPENEDHERE T))
             (CL:WHEN EXTERNALFORMAT
                 (STREAMPROP DESTSTREAM 'FORMAT EXTERNALFORMAT))
             (SETQ NEWPIECES (\TEDIT.PUT.PCTB TEXTOBJ DESTSTREAM UNFORMATTED? CONTINUE))
             (if CONTINUE
                 then (CLOSEF? DESTSTREAM)
                      (CL:UNLESS UNFORMATTED?                (* ; 
                                        "Make the directory date the same as the internal Tedit date")
                          (SETFILEINFO (FULLNAME DESTSTREAM)
                                 'ICREATIONDATE
                                 (TEDIT.FILEDATE DESTSTREAM T)))
                      (SETQ DESTSTREAM (OPENSTREAM DESTSTREAM 'INPUT))
                      (\TEDIT.INSERT.NEWPIECES DESTSTREAM TSTREAM NEWPIECES)
                      (SETFILEPTR DESTSTREAM 0)
                      (\PEEKBIN DESTSTREAM T)                (* ; 
                                              "Opening doesn't set up the buffers, you have to read.")
                      (CL:WHEN (FGETTOBJ TEXTOBJ TXTFILE)
                          (CLOSEF? (FGETTOBJ TEXTOBJ TXTFILE)))
                      (FSETTOBJ TEXTOBJ TXTFILE DESTSTREAM)
                      (FSETTOBJ TEXTOBJ \XDIRTY NIL)
                      (\TEDIT.UPDATE.TITLE TEXTOBJ DESTSTREAM)
                      (\TEDIT.HISTORYADD TEXTOBJ (\TEDIT.HISTORY.EVENT TEXTOBJ :Put))
                      (PUTTEXTPROP TEXTOBJ 'CLEARGET UNFORMATTED?)
                      DESTSTREAM
               elseif OPENEDHERE
                 then (OR (CLOSEF? DESTSTREAM)
                          (FULLNAME DESTSTREAM))
               else DESTSTREAM)))])
)
(DEFINEQ

(\TEDIT.GET.FOREIGN.FILE
  [LAMBDA (TEXT TSTREAM START END PROPS)                     (* ; "Edited  8-Feb-2025 20:20 by rmk")
                                                             (* ; "Edited  7-Feb-2025 08:10 by rmk")
                                                             (* ; "Edited 17-Mar-2024 00:21 by rmk")
                                                             (* ; "Edited 22-Oct-2023 20:40 by rmk")
                                                             (* ; "Edited 18-Sep-2023 16:40 by rmk")
                                                             (* ; "Edited 10-Aug-2023 17:26 by rmk")
                                                             (* ; "Edited  6-Sep-2022 12:18 by rmk")
                                                             (* ; "Edited 26-Aug-2022 08:43 by rmk")
                                                             (* ; "Edited 25-Jul-2022 21:21 by rmk")

    (* ;; "If TEXT is recognized as a file in a user format, convert it into a new text stream. It could be that the foreign file is coming from a TEDIT.GET on an existing stream. There may be a window attached to TSTREAM, and that's where the edit will eventually take place.  Its dimensions are available, e.g. for width and height, but it may not yet have been initialized for TEDIT (because the source text is being installed here). ")

    (* ;; "")

    (* ;; "The foreign function returns a textstream  FSTREAM.  If FSTREAM=TSTREAM then we assume that the foreign function filled in everything completely.  If it is a different stream, then we assume that its pieces are safe and its looks are good, we copy that information back into TSTREAM.")

    (* ;; "")

    (* ;; "Either way, the foreign function guarantees that file pieces, if any, are safe wrt buffer boundaries.")

    (LET (USERFILEFORMAT USERTEMP FSTREAM FTEXTOBJ (TTEXTOBJ (fetch (TEXTSTREAM TEXTOBJ) of TSTREAM))
                )

         (* ;; "Do the predicate and the converter agree that they can handle this?")

         (CL:WHEN (AND (SETQ USERFILEFORMAT (for FILETYPE in TEDIT.INPUT.FORMATS
                                               when (SETQ USERTEMP (APPLY* (CAR FILETYPE)
                                                                          TEXT TSTREAM PROPS))
                                               do (RETURN FILETYPE)))
                       (SETQ FSTREAM (APPLY* (CADR USERFILEFORMAT)
                                            TEXT TSTREAM PROPS USERTEMP START END))
                       (TEXTSTREAMP FSTREAM))                (* ; "Return NIL if we couldn't convert")
             (CL:UNLESS (EQ TSTREAM FSTREAM)
                 (SETQ FTEXTOBJ (TEXTOBJ FSTREAM))
                 (\TEDIT.INSERTPIECES (\TEDIT.FIRSTPIECE FTEXTOBJ)
                        NIL TTEXTOBJ)
                 (FSETTOBJ TTEXTOBJ SUFFIXPIECE (FGETTOBJ FTEXTOBJ SUFFIXPIECE))
                                                             (* ; "Last piece  have different looks")
                 (FSETTOBJ TTEXTOBJ TXTPAGEFRAMES (FGETTOBJ FTEXTOBJ TXTPAGEFRAMES))
                 (FSETTOBJ TTEXTOBJ DEFAULTPARALOOKS (FGETTOBJ FTEXTOBJ DEFAULTPARALOOKS))
                 (FSETTOBJ TTEXTOBJ DEFAULTCHARLOOKS (FGETTOBJ FTEXTOBJ DEFAULTCHARLOOKS)))
             TSTREAM)])

(\TEDIT.GET.UNFORMATTED.FILE
  [LAMBDA (STREAM TSTREAM START END PROPS)                   (* ; "Edited 10-Apr-2026 09:33 by rmk")
                                                             (* ; "Edited 28-Jul-2025 23:46 by rmk")
                                                             (* ; "Edited 24-Apr-2025 17:21 by rmk")
                                                             (* ; "Edited  8-Feb-2025 20:21 by rmk")
                                                             (* ; "Edited 17-Mar-2024 00:21 by rmk")
                                                             (* ; "Edited  5-Feb-2024 09:26 by rmk")
                                                             (* ; "Edited 21-Jan-2024 09:42 by rmk")
                                                             (* ; "Edited 29-Dec-2023 11:52 by rmk")
                                                             (* ; "Edited 27-Dec-2023 13:33 by rmk")
                                                             (* ; "Edited 22-Oct-2023 22:59 by rmk")
                                                             (* ; "Edited 12-Sep-2023 16:45 by rmk")
                                                             (* ; "Edited  3-Aug-2023 22:04 by rmk")
                                                             (* ; "Edited  3-May-2023 17:38 by rmk")
                                                             (* ; "Edited 26-Apr-2023 14:09 by rmk")
    (RESETLST
        [RESETSAVE NIL `(STREAMPROP ,STREAM ENDOFSTREAMOP ,(STREAMPROP STREAM 'ENDOFSTREAMOP
                                                                  (FUNCTION NILL]
        (\SETFILEPTR STREAM START)
        (LET ((TEXTOBJ (fetch (TEXTSTREAM TEXTOBJ) of TSTREAM))
              (FORMAT (STREAMPROP STREAM 'FORMAT))
              DEFAULTCHARLOOKS DEFAULTPARALOOKS PIECES)
             (PUTTEXTPROP TEXTOBJ 'CLEARGET T)
             (SETQ DEFAULTCHARLOOKS (GETTOBJ TEXTOBJ DEFAULTCHARLOOKS))
             (SETQ DEFAULTPARALOOKS (GETTOBJ TEXTOBJ DEFAULTPARALOOKS))
             (CL:WHEN (AND (EQ FORMAT :STRING)
                           (\IOMODEP STREAM 'OUTPUT T))
                 (SETQ STREAM (COPYFILE STREAM '{NODIRCORE})))
             (SETQ PIECES
              (SELECTQ FORMAT
                  ((:MCCS :XCCS)                             (* ; "XCCS is done later")
                       (\TEDIT.GET.UNFORMATTED.FILE.MCCS STREAM START END DEFAULTCHARLOOKS 
                              DEFAULTPARALOOKS))
                  (:UTF-8 (\TEDIT.GET.UNFORMATTED.FILE.UTF8 STREAM START END DEFAULTCHARLOOKS 
                                 DEFAULTPARALOOKS))
                  (:STRING (CL:WHEN (\IOMODEP STREAM 'OUTPUT T)

                               (* ;; 
            "Protect against somebody smashing the string. (Should also protect other files, but...)")

                               (SETQ STREAM (COPYFILE STREAM '{NODIRCORE})))

                           (* ;; "String format is known to be fat.  Eventually it should be sufficient to know the byesperchar of the piece to figure out the PFILEPOS byte pointers, and just use the generic \OUTCHAR to get the characters.")

                           (create PIECE
                                  PCONTENTS _ STREAM
                                  PFPOS _ START
                                  PLEN _ (FOLDLO (IDIFFERENCE END START)
                                                2)
                                  PCHARLOOKS _ DEFAULTCHARLOOKS
                                  PPARALAST _ NIL
                                  PPARALOOKS _ DEFAULTPARALOOKS
                                  PTYPE _ FATFILE2.PTYPE
                                  PBYTESPERCHAR _ 2))
                  (create PIECE
                         PCONTENTS _ STREAM
                         PFPOS _ START
                         PLEN _ (IDIFFERENCE END START)
                         PCHARLOOKS _ DEFAULTCHARLOOKS
                         PPARALAST _ NIL
                         PPARALOOKS _ DEFAULTPARALOOKS
                         PTYPE _ THINFILE.PTYPE
                         PBYTESPERCHAR _ 1)))
             (\TEDIT.INSERTPIECES PIECES NIL TEXTOBJ)))])

(\TEDIT.GET.FORMATTED.FILE
  [LAMBDA (TEXT TSTREAM START END PROPS)                     (* ; "Edited 25-Sep-2025 21:27 by rmk")
                                                             (* ; "Edited  9-Sep-2025 21:45 by rmk")
                                                             (* ; "Edited  7-Sep-2025 12:14 by rmk")
                                                             (* ; "Edited 28-Mar-2025 14:15 by rmk")
                                                             (* ; "Edited  7-Feb-2025 08:19 by rmk")
                                                             (* ; "Edited 28-Oct-2024 17:48 by rmk")
                                                             (* ; "Edited 21-Oct-2024 00:33 by rmk")
                                                             (* ; "Edited 29-Apr-2024 10:25 by rmk")
                                                             (* ; "Edited 17-Mar-2024 00:21 by rmk")
                                                             (* ; "Edited  5-Feb-2024 09:25 by rmk")
                                                             (* ; "Edited 21-Jan-2024 10:25 by rmk")
                                                             (* ; "Edited 18-Jan-2024 10:25 by rmk")
                                                             (* ; "Edited 27-Oct-2023 13:48 by rmk")
                                                             (* ; "Edited 11-Jun-99 14:37 by rmk:")
                                                             (* ; "Edited 19-Apr-93 13:46 by jds")

    (* ;; "TEXT is an open stream that knows its external format, TSTREAM is the textstream to be filled in.  If specified, START and END define the byte positions in TEXT to be included")

    (* ;; "Returns NIL if TSTREAM is not a formatted file, otherwise the ")

    (LET ((TEXTOBJ (FTEXTOBJ TSTREAM))
          (TRAILER (\TEDIT.GET.TRAILER TEXT END))
          PCCOUNT IDATE PROPS PC)
         (CL:WHEN TRAILER
             (SETTOBJ TEXTOBJ TXTPAGEFRAMES NIL)
             (FSETTOBJ TEXTOBJ DOCPROPS (fetch (\TEDIT.FILETRAILER PROPS) of TRAILER))
             (SETQ PCCOUNT (fetch (\TEDIT.FILETRAILER PCCOUNT) of TRAILER))
             (SELECTQ (fetch (\TEDIT.FILETRAILER VERSION) of TRAILER)
                 (3                                          (* ; "Version 3")
                    (\TEDIT.INSERTPIECES (\TEDIT.GET.PIECES3 TEXT TSTREAM PCCOUNT START END)
                           NIL TEXTOBJ))
                 (2                                          (* ; "Version 2;  obsoleted 5/22/85")
                    (\TEDIT.GET.PCTB2 TEXT TSTREAM PCCOUNT START END))
                 (1                                          (* ; 
                                                   "Version 1;  obsoleted at INTERMEZZO release 2/85")
                    (\TEDIT.GET.PCTB1 TEXT TSTREAM PCCOUNT START END))
                 (0                                          (* ; "VERSION 0")
                    (\TEDIT.GET.PCTB0 TEXT TSTREAM (CADR PCCOUNT)
                           PCCOUNT START END))
                 (\TEDIT.THELP "File format version incompatible with this version of TEdit."))
             (CL:WHEN (SETQ PC (\TEDIT.LASTPIECE TEXTOBJ))
                    (FSETPC PC PPARALAST T))
             (CL:UNLESS (EQ 'MCCS (GETMULTI (FGETTOBJ TEXTOBJ DOCPROPS)
                                         'CHARENCODING))
                    (\TEDIT.MCCS.TRANSLATE TSTREAM))
             TEXTOBJ)])

(\TEDIT.FORMATTEDSTREAMP
  [LAMBDA (TSTREAM)                                          (* ; "Edited  1-Aug-2025 14:50 by rmk")
                                                             (* ; "Edited  8-Feb-2025 20:21 by rmk")
                                                             (* ; "Edited 16-Mar-2024 10:03 by rmk")
                                                             (* ; "Edited 22-Sep-2023 20:17 by rmk")
                                                             (* ; "Edited 15-Sep-2023 00:09 by rmk")
                                                             (* ; "Edited 15-Aug-2023 17:35 by rmk")
                                                             (* ; "Edited 16-Sep-2022 21:00 by rmk")
                                                             (* ; "Edited  1-Sep-2022 08:54 by rmk")
                                                             (* ; "Edited 19-Apr-93 11:57 by jds")

    (* ;; 
   "Test to see if this stream's text would need a TEdit-format file (T) or is just plain text (NIL)")

    (LET ((TEXTOBJ (TEXTOBJ TSTREAM)))
         (for PC (FORMATLEVEL _ 0)
              (DEFAULTCLOOKS _ (FGETTOBJ TEXTOBJ DEFAULTCHARLOOKS))
              (DEFAULTPLOOKS _ (FGETTOBJ TEXTOBJ DEFAULTPARALOOKS))
              (TENTATIVE _ (GETTEXTPROP TEXTOBJ 'TEDIT.TENTATIVE)) inpieces (\TEDIT.FIRSTPIECE 
                                                                                   TEXTOBJ)
            do [COND
                  ((EQ OBJECT.PTYPE (PTYPE PC))              (* ; 
                                                             "OBJECTS require the special format")
                   (SETQ FORMATLEVEL 4)
                   (GO $$OUT))
                  ([OR (AND (PPARALAST PC)
                            (NEXTPIECE PC))
                       (NOT (\TEDIT.EQFMTSPEC DEFAULTPLOOKS (PPARALOOKS PC]
                                                             (* ; 
                                                   "A paragraph break not at the end, or a new plook")
                   (SETQ FORMATLEVEL (IMAX FORMATLEVEL 3)))
                  ((OR (NOT (\TEDIT.EQCLOOKS DEFAULTCLOOKS (PCHARLOOKS PC)))
                       (AND TENTATIVE (PNEW PC)))            (* ; "Change in font, size, etc.")
                   (SETQ FORMATLEVEL (IMAX FORMATLEVEL 2)))
                  ((MEMB (PTYPE PC)
                         (CONSTANT (LIST FATFILE2.PTYPE FATSTRING.PTYPE)))
                                                             (* ; "16-bit chars in the piece")
                   (SETQ FORMATLEVEL (IMAX FORMATLEVEL 1]
            finally 

                  (* ;; 
             "1 originally meant NSCHARS.  But that's not a %"look%", just an external format issue.")

                  (RETURN (SELECTQ FORMATLEVEL
                              (0 NIL)
                              (2 'CHARLOOKS)
                              (3 'PARALOOKS)
                              (4 'IMAGEOBJ)
                              NIL])

(\ARBIN
  [LAMBDA (STREAM)                                           (* ; "Edited 19-Dec-2023 10:13 by rmk")
                                                             (* jds "13-Nov-86 20:21")
                                                             (* ; 
                                     "Read an arbitrary object from a file, parse it, and return it.")
    (PROG ((LEN (\WIN STREAM))
           USERSTR)
          (COND
             ((NOT (ZEROP LEN))
              (SETQ USERSTR (OPENSTRINGSTREAM (\STRINGIN STREAM LEN)
                                   'INPUT))
              (RETURN (PROG1 (READ USERSTR *TEDIT-FILE-READTABLE*)
                             (CLOSEF? USERSTR])

(\ATMIN
  [LAMBDA (STREAM)                                           (* ; "Edited 19-Dec-2023 10:13 by rmk")
                                                             (* ; "Edited  1-Aug-2022 12:04 by rmk")
                                                             (* jds " 3-Apr-84 10:41")
    (PROG ((LEN (\WIN STREAM)))
          (RETURN (COND
                     ((ZEROP LEN)
                      NIL)
                     (T (PACK (for I from 1 to LEN collect (CHARACTER (BIN STREAM])

(\DWIN
  [LAMBDA (FILE)                                             (* ; "Edited  1-Aug-2022 12:04 by rmk")
                                                             (* jds " 3-JAN-83 16:08")
    (IPLUS (LLSH (BIN FILE)
                 24)
           (LLSH (BIN FILE)
                 16)
           (LLSH (BIN FILE)
                 8)
           (BIN FILE])

(\STRINGIN
  [LAMBDA (STREAM SETLEN)                                    (* ; "Edited 19-Dec-2023 10:13 by rmk")
                                                             (* ; "Edited 20-Apr-88 19:54 by jds")

         (* Read a string in length-contents form%: One word for the length, and one byte 
         per character contained. However, the length may be specified by the caller 
         instead of being read from the file.)

    (PROG ((LEN (OR SETLEN (\WIN STREAM)))
           STR)
          (SETQ STR (ALLOCSTRING LEN))
          [OR (ZEROP LEN)
              (for I from 1 to LEN do (RPLCHARCODE STR I (READCCODE STREAM]
          (RETURN STR])

(\TEDIT.GET.TRAILER
  [LAMBDA (STREAM LEN)                                       (* ; "Edited  9-Sep-2025 00:03 by rmk")
                                                             (* ; "Edited 26-Mar-2024 21:36 by rmk")
                                                             (* ; "Edited 18-Jan-2024 10:22 by rmk")
                                                             (* ; "Edited 16-Jan-2024 22:39 by rmk")
                                                             (* ; "Edited 15-Jan-2024 17:38 by rmk")
                                                             (* ; "Edited 13-Jan-2024 21:49 by rmk")

    (* ;; "For an open formatted stream, returns a list (PIECESTART TRAILERSIZE VERSION PCCOUNT IDATE PROPS) where PIECESTART is the byte position of the first piece.  Returns NIL if it is not a formatted stream. Either way, the file is left at position 0 FWIW ")

    (* ;; "If STREAM is the format-stream split of a complete Tedit file, then PIECESTART is the position in that larger file that this section was taken from, and 0 in STREAM corresponds to PIECESTART in that file.  TRAILERSIZE")

    (SETQ LEN (OR LEN (GETEOFPTR STREAM)))
    (CL:WHEN (IGREATERP LEN 8)
        (LET (PIECESTART TRAILERSIZE PCCOUNT VERSION IDATE PROPS)
             (SETFILEPTR STREAM (IDIFFERENCE LEN 8))         (* ; 
                                                           "Move to start of FILEPTR to descriptions")
             (SETQ PIECESTART (\DWIN STREAM))                (* ; 
                                                             "Read the file pos of the descriptions")
             (SETQ PCCOUNT (\WIN STREAM))
             (SETQ VERSION (IDIFFERENCE (\SMALLPIN STREAM)
                                  31415))
             (PROG1 (SELECTQ VERSION
                        (3                                   (* ; "Current version")
                           (CL:MULTIPLE-VALUE-SETQ (TRAILERSIZE IDATE PROPS)
                                  (\TEDIT.GET.PROPS3 STREAM LEN))
                           (create \TEDIT.FILETRAILER
                                  PIECESTART _ PIECESTART
                                  TRAILERSIZE _ TRAILERSIZE
                                  VERSION _ VERSION
                                  PCCOUNT _ PCCOUNT
                                  IDATE _ IDATE
                                  PROPS _ PROPS))
                        ((2 1 0) 
                             (create \TEDIT.FILETRAILER
                                    PIECESTART _ PIECESTART
                                    TRAILERSIZE _ 8
                                    VERSION _ VERSION
                                    PCCOUNT _ PCCOUNT))
                        NIL)
                    (SETFILEPTR STREAM 0))))])

(\TEDIT.CACHEFILE
  [LAMBDA (TEXT TEXTOBJ START END)                           (* ; "Edited 22-Sep-2023 20:15 by rmk")
                                                             (* ; "Edited 31-Aug-2023 15:35 by rmk")
                                                             (* ; "Edited 14-Jul-2022 08:44 by rmk")

    (* ;; "If TEXT is not a random-access file, we copy it into local storage (NODIRCORE)")

    (LET (CACHE)

         (* ;; "Sets the external format and its EOL.")

         [SETQ CACHE (OPENSTREAM '{NODIRCORE} 'BOTH 'NEW `((TYPE TEXT)
                                                           (:EXTERNAL-FORMAT ,(GETSTREAMPROP TEXT 
                                                                                     :EXTERNAL-FORMAT
                                                                                     ]
         (COND
            ((OR START END)
             (COPYBYTES TEXT CACHE (OR START 0)
                    (OR END -1)))
            (T (COPYBYTES TEXT CACHE)))                      (* ; "Copy the text there")

         (* ;; "COPYBYTES can only have start/end args of NIL if the file is not random access.  So it's impossible to grab out of the middle of a file on an NS server.  Sorry.")

         (CLOSEF TEXT)
         (CL:WHEN TEXTOBJ

             (* ;; 
             "REMEMBER THAT THIS TEXT WAS CACHED, SO THAT LATER PUTS DON'T INVALIDATE THE CACHE.")

             (PUTTEXTPROP TEXTOBJ 'CACHE T))
         CACHE])
)



(* ;; 
"Until CL:COMPILE-FILE and any others are updated, They should use the public TEDIT.FORMATTEDFILEP")


(MOVD? '\TEDIT.GET.TRAILER '\TEDIT.FORMATTEDP1)
(DEFINEQ

(\TEDIT.GET.PIECES3
  [LAMBDA (TEXT TSTREAM PCCOUNT CURTEXTBYTE# END)            (* ; "Edited 15-Apr-2026 12:06 by rmk")
                                                             (* ; "Edited  9-Apr-2026 13:45 by rmk")
                                                             (* ; "Edited 29-Jul-2025 09:30 by rmk")
                                                             (* ; "Edited 24-Apr-2025 17:20 by rmk")
                                                             (* ; "Edited  8-Feb-2025 20:21 by rmk")
                                                             (* ; "Edited 30-Aug-2024 15:44 by rmk")
                                                             (* ; "Edited 11-Jul-2024 13:20 by rmk")
                                                             (* ; "Edited 29-Apr-2024 10:37 by rmk")
                                                             (* ; "Edited  7-Apr-2024 17:20 by rmk")
                                                             (* ; "Edited 20-Mar-2024 10:59 by rmk")
                                                             (* ; "Edited 15-Mar-2024 14:37 by rmk")
                                                             (* ; "Edited 14-Jan-2024 00:22 by rmk")
                                                             (* ; "Edited 11-Jan-2024 12:37 by rmk")
                                                             (* ; "Edited 19-Dec-2023 10:13 by rmk")
                                                             (* ; "Edited  8-Dec-2023 22:49 by rmk")
                                                             (* ; "Edited  7-Nov-2023 13:10 by rmk")
                                                             (* ; "Edited 24-Sep-2023 22:00 by rmk")
                                                             (* ; "Edited  2-Sep-2023 11:12 by rmk")
                                                             (* ; "Edited 29-Aug-2023 00:18 by rmk")
    (SETFILEPTR TEXT (IDIFFERENCE (OR END (GETEOFPTR TEXT))
                            8))
    (SETFILEPTR TEXT (\DWIN TEXT))                           (* ; "Pieceinfo byte #")
    (for PCNO PC BYTELEN PREVPC FIRSTPC PARALOOKSMAP CHARLOOKSMAP DEFAULTCHARLOOKS OLDPARALOOKS
         (TEXTOBJ _ (TEXTOBJ! (fetch (TEXTSTREAM TEXTOBJ) of TSTREAM)))
         (ORIGBYTE# _ CURTEXTBYTE#) from 1 to PCCOUNT first (SETQ DEFAULTCHARLOOKS (FGETTOBJ TEXTOBJ
                                                                                          
                                                                                     DEFAULTCHARLOOKS
                                                                                          ))
                                                            (SETQ OLDPARALOOKS (FGETTOBJ TEXTOBJ 
                                                                                     DEFAULTPARALOOKS
                                                                                      ))
                                                            (SETQ FIRSTPC (CREATE PIECE)) 
                                                             (* ; "Throw away at the end")
                                                            (SETQ PREVPC FIRSTPC)
       do (SETQ PC NIL)                                      (* ; 
                        "This loop may not really read a piece, so we have to distinguish that case.")
          (SETQ BYTELEN (\DWIN TEXT))
          [SELECTC (\WIN TEXT)
              (\PieceDescriptorLOOKS                         (* ; 
                                  "New character looks.  Build a piece to describe those characters.")
                   (SETQ PC
                    (create PIECE
                           PCONTENTS _ TEXT
                           PFPOS _ CURTEXTBYTE#
                           PLEN _ BYTELEN
                           PPARALOOKS _ OLDPARALOOKS
                           PTYPE _ THINFILE.PTYPE
                           PBYTESPERCHAR _ 1
                           PREVPIECE _ PREVPC))
                   (\TEDIT.GET.CHARLOOKS.INDEX PC TEXT)      (* ; 
                                                             "Get its looks and character-pointers")
                   (add CURTEXTBYTE# BYTELEN))
              (\PieceDescriptorPARA                          (* ; 
                                                             "Reading a new set of paragraph looks.")
                   (CL:WHEN PREVPC (FSETPC PREVPC PPARALAST T))
                                                             (* ; 
                                                           "Mark the end of the preceding paragraph.")
                   (SETQ OLDPARALOOKS (\TEDIT.GET.PARALOOKS.INDEX TEXT))
                                                             (* ; 
                                                 "Get the new set of looks, for use by later pieces.")
                                                             (* ; 
                                              "Mark the document as containing paragraph formatting ")
                   (SETTOBJ TEXTOBJ FORMATTEDP T))
              (\PieceDescriptorOBJECT 
                                      (* ;; 
                             "BYTELEN is the number of bytes on the file, PLEN is always 1 character")

                   (SETQ PC
                    (create PIECE
                           PCONTENTS _ TEXT
                           PFPOS _ CURTEXTBYTE#
                           PLEN _ 1
                           PPARALOOKS _ OLDPARALOOKS
                           PTYPE _ OBJECT.PTYPE
                           PREVPIECE _ PREVPC))
                   (\TEDIT.GET.OBJECT TSTREAM PC TEXT CURTEXTBYTE#)
                   (add CURTEXTBYTE# BYTELEN)
                   (FSETPC PC PCHARLOOKS (if (ZEROP (BIN TEXT))
                                             then 

                                              (* ;; "No new looks;  steal them from the prior piece.  RMK: Goofy part of this format--we now always put out 0.")

                                                  (OR (AND PREVPC (PCHARLOOKS PREVPC))
                                                      DEFAULTCHARLOOKS)
                                           else 
                                                (* ;; 
                                      "There are new character looks for this object.  Read them in.")

                                                (\TEDIT.GET.SINGLE.CHARLOOKS TEXT TEXTOBJ))))
              (\PieceDescriptorCHARLOOKSLIST                 (* ; 
                                                 "Read the list of CHARLOOKSs used in this document.")
                   (add PCNO -1)                             (* ; 
                                                             "Lists don't count, in this format.")
                   (FSETTOBJ TEXTOBJ TXTCHARLOOKSLIST (\TEDIT.GET.CHARLOOKS.LIST TEXT TEXTOBJ))
                   [SETQ CHARLOOKSMAP (ARRAY (LENGTH (FGETTOBJ TEXTOBJ TXTCHARLOOKSLIST]
                                                             (* ; 
                                         "Build an array of the looks, so the reader can index them.")
                   (for J from 1 as CHARLOOKS in (FGETTOBJ TEXTOBJ TXTCHARLOOKSLIST)
                      do (SETA CHARLOOKSMAP J CHARLOOKS)))
              (\PieceDescriptorPARALOOKSLIST                 (* ; 
                                                 "Read the list of PARALOOKSs used in this document.")
                   (add PCNO -1)                             (* ; "Lists don't count in this format")
                   (FSETTOBJ TEXTOBJ TXTPARALOOKSLIST (\TEDIT.GET.PARALOOKS.LIST TEXT TEXTOBJ))
                   [SETQ PARALOOKSMAP (ARRAY (LENGTH (FGETTOBJ TEXTOBJ TXTPARALOOKSLIST]
                                                             (* ; 
                                         "Build an array of the looks, so the reader can index them.")
                   (for J from 1 as PARALOOKS in (FGETTOBJ TEXTOBJ TXTPARALOOKSLIST)
                      do (SETA PARALOOKSMAP J PARALOOKS)))
              (\PieceDescriptorPAGEFRAME                     (* ; 
                                                             "This is page layout info for the file")
                   (FSETTOBJ TEXTOBJ TXTPAGEFRAMES (\TEDIT.PARSE.PAGEFRAMES (READ TEXT 
                                                                               *TEDIT-FILE-READTABLE*
                                                                                  ))))
              (PROGN (TEDIT.PROMPTPRINT TEXTOBJ "WARNING: Unknown-type piece skipped." T)
                     (SETFILEPTR TEXT (IPLUS (GETFILEPTR TEXT)
                                             (\WIN TEXT]
          (CL:WHEN PC                                        (* ; 
                                               "Chain them together without putting them in the tree")
              (FSETPC PREVPC NEXTPIECE PC)
              (SETQ PREVPC PC)) finally (SETQ PC (NEXTPIECE FIRSTPC)) 
                                                             (* ; "Throw out the dummy FIRSTPC")
                                      (FSETPC PC PREVPIECE NIL) 

                                 (* ;; "We defer filling in the actual looks to avoid requiring the mapping arrays to come before the content pieces.  This makes it easier to prepend additional pieces without parsing the whole sequence, as Lafite wants to do.")

                                      [for P inpieces PC
                                         do (CL:WHEN (SMALLP (PCHARLOOKS P))
                                                (change (PCHARLOOKS P)
                                                       (ELT CHARLOOKSMAP DATUM)))
                                            (CL:WHEN (SMALLP (PPARALOOKS P))
                                                (change (PPARALOOKS P)
                                                       (CL:UNLESS (EQ DATUM 0)
                                                             (* ; " For the last piece?")
                                                           (ELT PARALOOKSMAP DATUM))))] 

                                      (* ;; "Produce MCCS codes for XCCS files, fix up later")

                                      (SELECTQ (STREAMPROP TEXT 'FORMAT)
                                          ((:MCCS :XCCS) 
                                               (\TEDIT.INTERPRET.MCCS.SHIFTS PC TEXT))
                                          NIL)
                                      (RETURN PC])

(\TEDIT.GET.PROPS3
  [LAMBDA (STREAM END)                                       (* ; "Edited  9-Sep-2025 21:49 by rmk")
                                                             (* ; "Edited  6-Dec-2023 16:55 by rmk")

    (* ;; "Returns the integer IDATE for slightly updated version 3 files, the directory idate if there is no date in the file.  At exit resets to starting position (assumes an error wouldn't matter).")

    (* ;; "Before the date there may also be a property list, unseen by pre MCCS code. This is headed by PROPS: followed by a pointer to the first byte of the property list, which can be READ.")

    (CL:WHEN (IGREATERP END (CONSTANT (IPLUS (NCHARS "DATE:")
                                             4 8)))
        (LET ((ORIGPTR (GETFILEPTR STREAM))
              [DATEPTR (IDIFFERENCE END (CONSTANT (IPLUS (NCHARS "DATE:")
                                                         4 8]
              (TRAILERSIZE 8)
              IDATE PROPSLEN PROPS)
             (SETFILEPTR STREAM DATEPTR)

             (* ;; 
     "DATE: is the marker for this extension to version 3 (could be removed if version is update).  ")

             (CL:WHEN (AND (EQ (CHARCODE D)
                               (BIN STREAM))
                           (EQ (CHARCODE A)
                               (BIN STREAM))
                           (EQ (CHARCODE T)
                               (BIN STREAM))
                           (EQ (CHARCODE E)
                               (BIN STREAM))
                           (EQ (CHARCODE %:)
                               (BIN STREAM)))
                 (SETQ IDATE (\DWIN STREAM))
                 (add TRAILERSIZE (CONSTANT (IPLUS (NCHARS "DATE:")
                                                   4)))
                 (CL:WHEN [IGREATERP END (IPLUS TRAILERSIZE (CONSTANT (IPLUS (NCHARS "PROPS:")
                                                                             4]
                     [SETFILEPTR STREAM (IDIFFERENCE DATEPTR (CONSTANT (IPLUS (NCHARS "PROPS:")
                                                                              4]
                     (CL:WHEN (AND (EQ (CHARCODE P)
                                       (BIN STREAM))
                                   (EQ (CHARCODE R)
                                       (BIN STREAM))
                                   (EQ (CHARCODE O)
                                       (BIN STREAM))
                                   (EQ (CHARCODE P)
                                       (BIN STREAM))
                                   (EQ (CHARCODE S)
                                       (BIN STREAM))
                                   (EQ (CHARCODE %:)
                                       (BIN STREAM)))
                         (SETQ PROPSLEN (\DWIN STREAM))
                         (SETFILEPTR STREAM (IDIFFERENCE DATEPTR PROPSLEN))
                         (SETQ PROPS (CAR (READ STREAM *TEDIT-FILE-READTABLE*)))
                         (add TRAILERSIZE PROPSLEN))))
             (SETFILEPTR STREAM ORIGPTR)
             (CL:VALUES TRAILERSIZE (OR IDATE (GETFILEINFO STREAM 'ICREATIONDATE))
                    PROPS)))])

(\TEDIT.MAKE.STRINGPIECE
  [LAMBDA (PC STRING)                                        (* ; "Edited 12-Apr-2026 21:30 by rmk")
                                                             (* ; "Edited 10-Apr-2026 09:33 by rmk")
                                                             (* ; "Edited 23-Jan-2024 14:32 by rmk")
                                                             (* ; "Edited 16-Jan-2024 11:15 by rmk")
                                                             (* ; "Edited 12-Jan-2024 16:34 by rmk")

    (* ;; "Makes a string piece modeled on PC containing STRING, and links it in the piece-chain after PC.  STRING can be a single charcode.")

    (SETQ STRING (CL:IF (CHARCODEP STRING)
                     (ALLOCSTRING 1 STRING)
                     (CONCAT STRING)))
    (LET (SPIECE)
         (SETQ SPIECE (if (fetch (STRINGP FATSTRINGP) of STRING)
                          then (create PIECE using PC PTYPE _ FATSTRING.PTYPE PCONTENTS _ STRING PLEN
                                                   _ (NCHARS STRING)
                                                   PBYTESPERCHAR _ 2 PREVPIECE _ PC)
                        else (create PIECE using PC PTYPE _ THINSTRING.PTYPE PCONTENTS _ STRING PLEN
                                                 _ (NCHARS STRING)
                                                 PBYTESPERCHAR _ 1 PREVPIECE _ PC)))
         (CL:WHEN (NEXTPIECE PC)
             (FSETPC (NEXTPIECE PC)
                    PREVPIECE SPIECE))
         (FSETPC PC NEXTPIECE SPIECE)
         SPIECE])
)
(DEFINEQ

(\TEDIT.GET.UNFORMATTED.FILE.MCCS
  [LAMBDA (STRM START END DEFAULTCHARLOOKS DEFAULTPARALOOKS) (* ; "Edited 12-Apr-2026 21:34 by rmk")
                                                             (* ; "Edited 10-Apr-2026 09:33 by rmk")

    (* ;; 
    "We build a chain of pieces  for the MCCS stringlets, some of which are subdivided at CR/LF.  ")

    (* ;; "We assume that caller has positioned the stream at the intended start byte and has set the ENDOFSTREAMOP to return NIL on EOF. ")

    (* ;; "This does not set PPARALAST on EOL pieces.  Maybe double EOL's?")

    (* ;; "If a shift at the EOF is ill-formed, it is ignored--no error.")

    (* ;; 
    " LF's after CR are discarded, LF's by themselves are converted to singleton EOLstring pieces.")

    (bind PREVPC PC CHAR TWOBYTE CHARLIST PLEN STARTPOS STRING (FIRSTPIECE _ (create PIECE))
          (CHARSET _ 0) first (SETQ PREVPC FIRSTPIECE)
       do (SETQ PLEN 0)
          (SETQ STARTPOS (GETFILEPTR STRM))
          [while (SETQ CHAR (BIN STRM)) until (EQ CHAR NSCHARSETSHIFT)
             do (CL:WHEN TWOBYTE
                    (SETQ CHARSET (LLSH CHAR 8))
                    (CL:UNLESS (SETQ CHAR (BIN STRM))        (* ; "Ill-formed at EOF, skip last byte")
                        (RETURN)))
                (SETQ CHAR (LOGOR CHARSET CHAR))
                (CL:UNLESS (OR TWOBYTE (EQ CHARSET 0))       (* ; "Collect characters for fatstring")
                    (push CHARLIST CHAR))
                (add PLEN 1) repeatuntil (MEMB CHAR (CHARCODE (CR LF] 

          (* ;; "Reached the end of the current (sub) run")

          (CL:UNLESS (EQ PLEN 0)                             (* ; "Make subrun's piece")
              (SELCHARQ CHAR
                   (CR                                       (* ; "Skip following LF")
                       (if TWOBYTE
                           then (CL:WHEN (EQ 0 (\PEEKCCODE STRM T))
                                    (BIN STRM)
                                    (CL:IF (EQ (CHARCODE LF)
                                               (\PEEKCCODE STRM T))
                                        (BIN STRM)
                                        (\BACKFILEPTR STRM)))
                         elseif (EQ (CHARCODE LF)
                                    (\PEEKCCODE STRM T))
                           then (BIN STRM)))
                   (LF                                       (* ; 
                                      "Prefix bcomes a separate piece, LF a singleton coerced to EOL")
                       (if (EQ PLEN 1)
                           then (SETQ CHARLIST (CHARCODE (EOL))) 
                                                             (* ; "Let it be fat below")
                         else (add PLEN -1)                  (* ; 
                                              "Back up to split the LF off into a separate EOL piece")
                              (\BACKFILEPTR STRM)
                              (CL:WHEN TWOBYTE (\BACKFILEPTR STRM))))
                   NIL)
              (SETQ PC
               (if CHARLIST
                   then (SETQ STRING (ALLOCSTRING (LENGTH CHARLIST)
                                            NIL NIL T))
                        (for C in CHARLIST as I from PLEN by -1 do (RPLCHARCODE STRING I C))
                        (SETQ CHARLIST NIL)
                        (create PIECE
                               PTYPE _ FATSTRING.PTYPE
                               PCONTENTS _ STRING
                               PLEN _ PLEN
                               PBYTESPERCHAR _ 2)
                 elseif TWOBYTE
                   then (create PIECE
                               PTYPE _ FATFILE2.PTYPE
                               PCONTENTS _ STRM
                               PFPOS _ STARTPOS
                               PLEN _ PLEN
                               PBYTESPERCHAR _ 2)
                 else (create PIECE
                             PTYPE _ THINFILE.PTYPE
                             PCONTENTS _ STRM
                             PFPOS _ STARTPOS
                             PLEN _ PLEN
                             PBYTESPERCHAR _ 1)))
              (FSETPC PC PCHARLOOKS DEFAULTCHARLOOKS)
              (FSETPC PC PPARALOOKS DEFAULTPARALOOKS)
              (FSETPC PC PREVPIECE PREVPC)
              (FSETPC PREVPC NEXTPIECE PC)
              (SETQ PREVPC PC)) 

          (* ;; "Switch to next run, end, or continue with next subrun")

          (SELECTC CHAR
              (NSCHARSETSHIFT                                (* ; "Switch to next run")
                   (SETQ CHARSET (BIN STRM))
                   (CL:UNLESS CHARSET                        (* ; "Ill-formed")
                       (RETURN (NEXTPIECE FIRSTPIECE)))
                   (SETQ TWOBYTE (CL:WHEN (EQ CHARSET \NORUNCODE)
                                     (SETQ CHARSET (BIN STRM))
                                     (CL:UNLESS CHARSET      (* ; "Ill-formed")
                                         (RETURN (NEXTPIECE FIRSTPIECE)))
                                     (CL:UNLESS (EQ CHARSET 0)
                                            (\MCCS.24BITENCODING.ERROR STRM))
                                     T))
                   (SETQ CHARSET (LLSH CHARSET 8)))
              (NIL                                           (* ; "End of file")
                   (RETURN (NEXTPIECE FIRSTPIECE)))
              NIL])

(\TEDIT.INTERPRET.MCCS.SHIFTS
  [LAMBDA (PIECES PFILE)                                     (* ; "Edited 29-Apr-2026 23:48 by rmk")
                                                             (* ; "Edited 24-Apr-2026 21:08 by rmk")
                                                             (* ; "Edited 10-Apr-2026 09:33 by rmk")
                                                             (* ; "Edited 21-Oct-2024 00:33 by rmk")
                                                             (* ; "Edited 14-May-2024 18:39 by rmk")
                                                             (* ; "Edited 21-Jan-2024 00:02 by rmk")
                                                             (* ; "Edited 19-Jan-2024 10:34 by rmk")
                                                             (* ; "Edited 12-Jan-2024 23:53 by rmk")
                                                             (* ; "Edited  6-Jan-2024 15:02 by rmk")
                                                             (* ; "Edited 19-Dec-2023 13:13 by rmk")

    (* ;; "This is called after by \TEDIT.GET.PIECES, after a GET, when the pieces are known all to reside in PFILE. PIECES is a chain of pieces read from a formatted MCCS (or XCCS) file but not yet inserted into the BTREE.  Each file piece has PFILE and PFPOS.  This function makes sure that no shift bytes are included in the pieces, by coercing the piece properties and bumping the PFPOS/PLEN to hide the shifts. This also coerces non-charset 0 one-byte pieces to fatstrings.")

    (* ;; "We run this before the pieces are inistalled in a stream, since this may change the character lengths.")

    (* ;; "This also has some EOL normalization.")

    (for PC BYTE EOLC inpieces PIECES when (EQ PFILE (PCONTENTS PC))
       do (\SETFILEPTR PFILE (PFPOS PC))
          (SETQ BYTE (BIN PFILE))
          (if (EQ NSCHARSETSHIFT BYTE)
              then (SELECTC (SETQ BYTE (BIN PFILE))
                       (0 (add (PFPOS PC)
                               2)
                          (add (PLEN PC)
                               -2))
                       (\NORUNCODE                           (* ; "Going for 2 byte characters")
                                   (CL:UNLESS (EQ 0 (BIN PFILE))
                                       (\TEDIT.THELP "MCCS CHARACTER NOT IN PLANE 0, FILEPOS = "
                                              (IDIFFERENCE (GETFILEPTR PFILE)
                                                     2)))
                                   (FSETPC PC PTYPE FATFILE2.PTYPE)
                                   (add (PFPOS PC)
                                        3)
                                   (change (PLEN PC)
                                          (FOLDLO (IDIFFERENCE DATUM 3)
                                                 2)))
                       (PROGN 
                              (* ;; "A run in a non-zero charset.  Convert it to FATSTRING so we don't have  to maintain code to interpret XCCS stringlet pieces.  After all, space efficiency is far worse for our ultimate goal of UTF-8 codes.")

                              (\TEDIT.RUN.TO.STRINGPIECE PC BYTE PFILE)))
            elseif (EQ FATFILE2.PTYPE (PTYPE PC))
              then                                           (* ; "This is the continuation of an MCCS 2-byte run that was broken up presumably for looks or paragraphs")
                   (change (PLEN PC)
                          (FOLDLO DATUM 2))
            else (FSETPC PC PBYTESPERCHAR 1)                 (* ; "A charset 0 1-byte run")
                 [\SETFILEPTR PFILE (SUB1 (IPLUS (PFPOS PC)
                                                 (PLEN PC]   (* ; 
                          "Position for the last byte for EOL processing.  Maybe only if PPARALAST ?")
                 (if (EQ (CHARCODE LF)
                         (SETQ BYTE (BIN PFILE)))
                     then 
                          (* ;; "First EOL approximation:  Convert trailing LF's to string-piece EOL's. This doesn't get LF's at the end of FATFILE2 or anywhere other than the end.")

                          [if (EQ (PLEN PC)
                                  1)
                              then (FSETPC PC PTYPE THINSTRING.PTYPE) 
                                                             (* ; "Convert to EOL string")
                                   (FSETPC PC PCONTENTS (ALLOCSTRING 1 (CHARCODE EOL)))
                            else (add (PLEN PC)
                                      -1)                    (* ; 
                                             "Shorten PC, add EOL string piece unless preceded by CR")
                                 (if (EQ (CHARCODE CR)
                                         (\BACKBIN PFILE))
                                     then (SETQ EOLC CRLF.EOLC)
                                   else (SETQ EOLC LF.EOLC)
                                        (SETQ PC (PROG1 (\TEDIT.MAKE.STRINGPIECE PC (CHARCODE EOL))
                                                        (FSETPC PC PPARALAST NIL]
                   else (CL:WHEN (EQ BYTE (CHARCODE CR))
                               (SETQ EOLC CR.EOLC))
                        (FSETPC PC PTYPE THINFILE.PTYPE)))
       finally (CL:WHEN EOLC
                   (replace (STREAM EOLCONVENTION) of PFILE with EOLC)))
    PIECES])

(\TEDIT.CONVERT.XCCSTOMCCS
  [LAMBDA (TSTREAM)                                          (* ; "Edited 24-Apr-2025 17:10 by rmk")

    (* ;; "Brute force way of converting a known-to-be MCCS stream into an XCCS stream")
                                                             (* ; 
                                               "Don't accumulate history during this transformation;")
    (RESETLST
        [RESETSAVE (TEXTPROP TSTREAM 'HISTORY 'OFF)
               `(PROGN (TEXTPROP ,TSTREAM 'HISTORY OLDVALUE]
        (for CHNO CHAR from 1 to (TEDIT.NCHARS TSTREAM) when (SMALLP (SETQ CHAR (TEDIT.NTHCHARCODE
                                                                                 TSTREAM CHNO)))
           unless (EQ CHAR (SETQ CHAR (MTOXCODE CHAR))) do (\TEDIT.RPLCHARCODE TSTREAM CHNO CHAR NIL
                                                                  T)))])

(\TEDIT.RUN.TO.STRINGPIECE
  [LAMBDA (PC CHARSET PFILE)                                 (* ; "Edited 10-Apr-2026 09:20 by rmk")
                                                             (* ; "Edited  7-Apr-2026 18:16 by rmk")
    (SETQ CHARSET (LLSH CHARSET 8))
    (LET ((STRING (ALLOCSTRING (PLEN PC)
                         NIL NIL T)))
         [for I from 1 to (PLEN PC) do (RPLCHARCODE STRING I (LOGOR CHARSET (BIN PFILE]
         (FSETPC PC PTYPE FATSTRING.PTYPE)
         (FSETPC PC PCONTENTS STRING)
         (FSETPC PC PBYTESPERCHAR 2)
         PC])
)



(* ; "MCCS")

(DEFINEQ

(\TEDIT.GET.UNFORMATTED.FILE.UTF8
  [LAMBDA (STRM START END DEFAULTCHARLOOKS DEFAULTPARALOOKS) (* ; "Edited 12-Apr-2026 21:46 by rmk")
                                                             (* ; "Edited 10-Apr-2026 09:24 by rmk")
                                                             (* ; "Edited 23-Oct-2025 08:48 by rmk")
                                                             (* ; "Edited 28-Jul-2025 23:45 by rmk")
                                                             (* ; "Edited 11-Mar-2024 23:55 by rmk")
                                                             (* ; "Edited  4-Feb-2024 10:12 by rmk")
                                                             (* ; "Edited  2-Feb-2024 11:24 by rmk")
                                                             (* ; "Edited 21-Jan-2024 09:41 by rmk")
                                                             (* ; "Edited 12-Jan-2024 13:17 by rmk")
                                                             (* ; "Edited 10-Jan-2024 10:32 by rmk")
                                                             (* ; "Edited  8-Jan-2024 12:08 by rmk")

    (* ;; "Break a UTF-8 file into pieces all of whose characters are of the same length. This is roughly the same logic of \TEDIT.GET.UNFORMATTED.FILE.XCCS.")

    (* ;; "We assume that caller has positioned the stream at the intended start byte and has set the ENDOFSTREAMOP to return NIL on EOF. ")

    (* ;; "CRBEFORE and the LF test are used to ensure that potential CR/LF's are normalized to EOL and appear at the end of their pieces, whether or not we decide to make them PPARALAST on input.  LF's after CR are discarded, LF's by themselves are converted to singleton EOLstring pieces.")

    (bind (NEXTFILEPOS _ START)
          (FIRSTPC _ (create PIECE
                            PCHARLOOKS _ DEFAULTCHARLOOKS
                            PPARALOOKS _ DEFAULTPARALOOKS))
          (NEXTCODESIZE _ 1)
          EOLC CHAR PREVPC PTYPE RUNLEN FILEPOS CRBEFORE CODESIZE PREVCRLF
       first (SELECTQ (READBOM STRM)
                 (:UTF-8 (add NEXTFILEPOS 3))
                 ((:UTF-16BE :UTF-16LE)                      (* ; 
                                        "Presumably we could create a single piece of the right type")
                      (ERROR "BOM :UTF-16 not yet implemented")
                      (add NEXTFILEPOS 2))
                 NIL)
             (SETQ CODESIZE NEXTCODESIZE)                    (* ; "Assume Ascii to start")
             (SETQ PREVPC FIRSTPC)                           (* ; "FIRSTPC is a throwaway")
       do (SETQ FILEPOS NEXTFILEPOS)                         (* ; "Start of next file piece")
          (do 
              (* ;; "We stop extending if the next character wouold be out of range, go below to wrap up the final piece. ")

              (CL:WHEN (IGEQ NEXTFILEPOS END)
                     (RETURN))
              (SETQ CHAR (\PEEKBIN STRM))                    (* ; 
                                                         "Keep CHAR for CR/LF checking, error if EOF")
                                                             (* ; "Error if  invalid header")
              (SETQ NEXTCODESIZE (NUTF8-BYTE1-BYTES CHAR))
              (CL:UNLESS (EQ CODESIZE NEXTCODESIZE)          (* ; "Header byte hasn't been read")

                  (* ;; "Don't want LF processing if we split because of size change. If next is a CR/LF still in size 1, we pick it up below")

                  (SETQ CHAR NIL)
                  (RETURN))
              (SETQ NEXTCODESIZE (UTF8.VALIDATE STRM))       (* ; "\Read/validate the trailing bytes")
              (add NEXTFILEPOS NEXTCODESIZE)
              (CL:WHEN (MEMB CHAR (CHARCODE (CR LF)))

                  (* ;; "Force a split now, after reading the CR/LF. But make sure a size change doesn't force an empty split in front of the next character.")

                  (SETQ NEXTCODESIZE (NUTF8-BYTE1-BYTES (OR (\PEEKBIN STRM)
                                                            0)))
                  (RETURN))) 

          (* ;; "")

          (* ;; "NEXTFILEPOS and file are positioned at beginning of next piece, possibly after CR and LF have been read.")

          (SETQ RUNLEN (IDIFFERENCE NEXTFILEPOS FILEPOS))
          (CL:WHEN (EQ CHAR (CHARCODE LF))                   (* ; "We never produce raw LF's")
              (add RUNLEN -1))
          (CL:WHEN (IGREATERP RUNLEN 0)
              (SETQ PTYPE (CL:IF (EQ CODESIZE 1)
                              THINFILE.PTYPE
                              UTF8.PTYPE))
              (SETQ PREVPC (FSETPC PREVPC NEXTPIECE
                                  (create PIECE
                                         PTYPE _ PTYPE
                                         PCONTENTS _ STRM
                                         PFPOS _ FILEPOS
                                         PLEN _ (IQUOTIENT RUNLEN CODESIZE)
                                         PCHARLOOKS _ DEFAULTCHARLOOKS
                                         PPARALOOKS _ DEFAULTPARALOOKS
                                         PBYTESPERCHAR _ CODESIZE
                                         PREVPIECE _ PREVPC))))
          (CL:WHEN (EQ CHAR (CHARCODE LF))
              [if CRBEFORE
                  then (SETQ EOLC CRLF.EOLC)
                else 
                     (* ;; "Linefeed not preceded by CR, replace by string piece")

                     (SETQ EOLC LF.EOLC)
                     (SETQ PREVPC (\TEDIT.MAKE.STRINGPIECE PREVPC (CHARCODE EOL])
          (CL:WHEN (IGEQ NEXTFILEPOS END)
              (CL:WHEN EOLC                                  (* ; 
                                                             "Record the last EOLC we encountered")
                  (replace (STREAM EOLCONVENTION) of STRM with EOLC))
              (RETURN (NEXTPIECE FIRSTPC)))
          (SETQ CODESIZE NEXTCODESIZE)
          (CL:WHEN (SETQ CRBEFORE (EQ CHAR (CHARCODE CR)))
                 (SETQ EOLC CR.EOLC])
)



(* ; "UTF-8")

(DEFINEQ

(\TEDIT.GET.CHARLOOKS.LIST
  [LAMBDA (FILE TEXTOBJ)                                     (* ; "Edited  7-Apr-2024 17:22 by rmk")
                                                             (* ; "Edited 16-Jan-2024 22:44 by rmk")
                                                             (* ; "Edited 19-Dec-2023 10:13 by rmk")
                                                             (* jds "28-Jan-85 17:50")
                                                             (* ; 
                                                         "Read the list of CHARLOOKSs from the file.")
    (for I from 1 to (\WIN FILE) collect (\TEDIT.GET.SINGLE.CHARLOOKS FILE TEXTOBJ])

(\TEDIT.GET.SINGLE.CHARLOOKS
  [LAMBDA (FILE TEXTOBJ)                                     (* ; "Edited 25-Sep-2025 18:30 by rmk")
                                                             (* ; "Edited  7-Sep-2025 11:04 by rmk")
                                                             (* ; "Edited  1-Aug-2025 13:43 by rmk")
                                                             (* ; "Edited 26-Jul-2025 11:14 by rmk")
                                                             (* ; "Edited 21-Jul-2025 23:41 by rmk")
                                                             (* ; "Edited 20-Jul-2025 13:14 by rmk")
                                                             (* ; "Edited 22-Apr-2025 15:20 by rmk")
                                                             (* ; "Edited  2-Jan-2025 11:08 by rmk")
                                                             (* ; "Edited 11-Dec-2024 22:59 by rmk")
                                                             (* ; "Edited  9-Dec-2024 20:11 by rmk")
                                                             (* ; "Edited 13-Aug-2024 08:49 by rmk")
                                                             (* ; "Edited 31-Jul-2024 00:04 by rmk")
                                                             (* ; "Edited  7-Apr-2024 17:21 by rmk")
                                                             (* ; "Edited 16-Jan-2024 22:46 by rmk")
                                                             (* ; "Edited 21-Dec-2023 23:54 by rmk")
                                                             (* ; "Edited 24-Aug-2023 15:05 by rmk")
                                                           (* ; "Edited 20-Feb-2022 12:42 by larry")
                                                             (* ; "Edited 30-May-91 20:25 by jds")

    (* ;; "Read one CHARLOOKS from FILE.  This gets and then sets the file pointer, based on the stored length.  But that won't work if the file is not random access.  Maybe that's not necessary?")

    (* ;; "TEXTOBJ only for printing in the local promptwindow, if necessary.")

    (PROG* ((LOOKS (create CHARLOOKS))
            (FILEPOS (GETFILEPTR FILE))
            (LOOKSLEN (\WIN FILE))
            FONT NAME SIZE SUPER PROPS STYLESTR BOLD ITALIC PROPS BITS)
           (SETQ NAME (\ARBIN FILE))                         (* ; "The font name")
           (SETQ SIZE (\WIN FILE))                           (* ; "Size of the type, in points")
           (SETQ SUPER (\SMALLPIN FILE))                     (* ; 
                                                         "Superscripting distance, could be negative")
           (FSETCLOOKS LOOKS CLSTYLE (OR (\ARBIN FILE)
                                         0))
           (SETQ PROPS (\ARBIN FILE))

     (* ;; "Complexity to deal with legacy files")

           [if (MEMB (CAR (LISTP PROPS))
                     '(CHARPROPS \TEDIT.CHARPROPS))
               then 
                    (* ;; 
                    "A true property list. Remove \TEDIT.CHARPROPS when few files are cleaned up")

                    (SETQ PROPS (CDR PROPS))
             else (SELECTQ [CAR (LISTP (CAR (LISTP PROPS]
                      (* ;; "\TEDIT.COLOR was used as an intermediate state to signal the alist, hopefully appears in few files.   And if neither, then this is just the original CLUSERINFO")
                      (\TEDIT.COLOR (RPLACA (CAR PROPS)
                                           'COLOR)
                                    (RPLACD (CAR PROPS)
                                           (CADR (CAR PROPS)))
                                    [LET ((X (ASSOC '\TEDIT.USERINFO PROPS)))
                                         (CL:WHEN X
                                             (RPLACA X 'USERINFO)
                                             (RPLACD X (CADR X)))])
                      (COLOR 
                             (* ;; "This may not be needed, even for correction.  ")

                             (CL:WHEN (LISTP (CDR (CAR PROPS)))
                                 (RPLACD (CAR PROPS)
                                        (CADR (CAR PROPS)))))
                      (PROGN 
                             (* ;; "Before props were generalized, just userinfo")

                             (SETQ PROPS (LIST (CONS 'USERINFO PROPS)
                                               `(COLOR . BLACK]
           (FSETCLOOKS LOOKS CLCOLOR (GETMULTI PROPS 'COLOR))
           (FSETCLOOKS LOOKS CLUSERINFO (GETMULTI PROPS 'USERINFO))
           (FSETCLOOKS LOOKS CLCHARENCODING (GETMULTI PROPS 'CHARENCODING))
           (SETQ BITS (\WIN FILE))                           (* ; "All the bits")
           [SETQ BOLD (NOT (ZEROP (LOGAND 512 BITS]
           [SETQ ITALIC (NOT (ZEROP (LOGAND 256 BITS]
           (with CHARLOOKS LOOKS [SETQ CLSELBEFORE (NOT (ZEROP (LOGAND 8192 BITS]
                 [SETQ CLUNBREAKABLE (NOT (ZEROP (LOGAND 4096 BITS]
                 [SETQ CLLEADER (NOT (ZEROP (LOGAND 2048 BITS]
                 [SETQ CLINVERTED (NOT (ZEROP (LOGAND 1024 BITS]
                 [SETQ CLULINE (NOT (ZEROP (LOGAND 128 BITS]
                 [SETQ CLOLINE (NOT (ZEROP (LOGAND 64 BITS]
                 [SETQ CLSTRIKE (NOT (ZEROP (LOGAND 32 BITS]
                 [SETQ CLSMALLCAP (NOT (ZEROP (LOGAND 16 BITS]
                 [SETQ CLPROTECTED (NOT (ZEROP (LOGAND 8 BITS]
                 [SETQ CLINVISIBLE (NOT (ZEROP (LOGAND 4 BITS]
                 [SETQ CLSELAFTER (NOT (ZEROP (LOGAND 2 BITS]
                 [SETQ CLCANCOPY (NOT (ZEROP (LOGAND 1 BITS]
                 (SETQ CLOFFSET SUPER))
           [if (LISTP NAME)
               then                                          (* ; 
                                                             "This was a font class.  Restore it.")
                    (SETQ FONT (FONTCLASS (pop NAME)
                                      NAME))                 (* ; 
                                            "But don't maintain original names, for equality testing")
                    (replace (FONTCLASS FONTCLASSNAME) of FONT with 'TEDIT-FONTCLASS)
                    (replace (FONTCLASS PRETTYFONT#) of FONT with 0)
             else (SETQ FONT (FONTCREATE NAME SIZE (PACK* (CL:IF BOLD
                                                              'B
                                                              'M)
                                                          (CL:IF ITALIC
                                                              'I
                                                              'R)
                                                          'R]
           (FSETCLOOKS LOOKS CLFONT FONT)
           (SETFILEPTR FILE (IPLUS FILEPOS LOOKSLEN))
           (RETURN LOOKS])

(\TEDIT.GET.CHARLOOKS
  [LAMBDA (PC FILE LOOKSARRAY)                               (* ; "Edited  1-Aug-2025 14:52 by rmk")
                                                             (* ; "Edited 28-Jul-2025 23:45 by rmk")
                                                             (* ; "Edited 13-Jan-2024 00:01 by rmk")
                                                             (* ; "Edited 19-Dec-2023 10:13 by rmk")
                                                             (* ; "Edited  3-Sep-2023 23:31 by rmk")
                                                             (* ; "Edited 28-Aug-2023 22:19 by rmk")
                                                             (* ; "Edited 26-Aug-2023 23:22 by rmk")
                                                             (* ; "Edited 30-May-91 21:43 by jds")

    (* ;; "Set the PCHARLOOKS for the current piece, PC, according to what the file says")

    (* ;; "We also ")

    (LET ((FLAGS (BIN FILE)))
         (FSETPC PC PCHARLOOKS (ELT LOOKSARRAY (\WIN FILE)))
         (CL:UNLESS (ZEROP (LOGAND FLAGS 1))
                (FSETPC PC PNEW T))
         (CL:UNLESS (ZEROP (LOGAND FLAGS 2))                 (* ; 
                                         "XCSS FAT. It may be a continuation of a previous fat piece")
             (FSETPC PC PLEN (IQUOTIENT (FGETPC PC PLEN)
                                    2))
             (FSETPC PC PTYPE FATFILE2.PTYPE)
             (FSETPC PC PBYTESPERCHAR 2))])

(\TEDIT.GET.PARALOOKS.INDEX
  [LAMBDA (STREAM PARAHASH)                                  (* ; "Edited 13-Jan-2024 13:06 by rmk")
                                                             (* ; "Edited 19-Dec-2023 10:13 by rmk")
                                                             (* ; "Edited 18-Dec-88 17:47 by jds")

    (* ;; "Reads the index of a paragraph format.  .")

    (* ;; "Index 0 indicates an end-of-file dummy, used to preserve the paralooks of EOF para break.")

    (\WIN STREAM])

(\TEDIT.GET.CHARLOOKS.INDEX
  [LAMBDA (PC FORMATSTREAM)                                  (* ; "Edited 24-Apr-2026 21:03 by rmk")
                                                             (* ; "Edited 28-Jul-2025 23:46 by rmk")
                                                             (* ; "Edited 14-Jan-2024 00:11 by rmk")
                                                             (* ; "Edited 19-Dec-2023 10:13 by rmk")
                                                             (* ; "Edited  3-Sep-2023 23:31 by rmk")
                                                             (* ; "Edited 28-Aug-2023 22:19 by rmk")
                                                             (* ; "Edited 26-Aug-2023 23:22 by rmk")
                                                             (* ; "Edited 30-May-91 21:43 by jds")

    (* ;; "Set the type, length, and charlooks-index for the current piece, PC")

    (LET ((FLAGS (BIN FORMATSTREAM)))
         (FSETPC PC PCHARLOOKS (\WIN FORMATSTREAM))
         (CL:UNLESS (ZEROP (LOGAND FLAGS 1))
                (FSETPC PC PNEW T))
         (CL:UNLESS (ZEROP (LOGAND FLAGS 2))

             (* ;; "MCSS FAT. It may have a 255 255 0 (NSHIFTBYTES=3) prefix or it may be  a continuation of a previous fat piece.  PLEN on the file is the runlength including the NSHIFTBYTES, so we can't fold from bytes to chars here: \TEDIT.INTERPRET.MCCS.SHIFTS.  Until then, this is goofy")

             (FSETPC PC PBYTESPERCHAR 2)
             (FSETPC PC PTYPE FATFILE2.PTYPE))])
)
(DEFINEQ

(\TEDIT.GET.PARALOOKS.LIST
  [LAMBDA (FILE TEXTOBJ)                                     (* ; "Edited 16-Jan-2024 22:47 by rmk")
                                                             (* ; "Edited 19-Dec-2023 10:13 by rmk")
                                                             (* jds "13-Jun-85 11:14")
                                                             (* ; 
                                                         "Read the list of CHARLOOKSs from the file.")
    (for I from 1 to (\WIN FILE) collect (\TEDIT.GET.SINGLE.PARALOOKS FILE TEXTOBJ])

(\TEDIT.GET.SINGLE.PARALOOKS
  [LAMBDA (FILE TEXTOBJ)                                     (* ; "Edited 19-Feb-2025 12:10 by rmk")
                                                             (* ; "Edited  8-Feb-2025 22:04 by rmk")
                                                             (* ; "Edited 22-Nov-2024 23:55 by rmk")
                                                             (* ; "Edited 23-Oct-2024 16:03 by rmk")
                                                             (* ; "Edited 21-Oct-2024 00:33 by rmk")
                                                             (* ; "Edited  5-Aug-2024 09:47 by rmk")
                                                             (* ; "Edited 29-Jul-2024 23:26 by rmk")
                                                             (* ; "Edited 28-Jul-2024 21:38 by rmk")
                                                             (* ; "Edited 16-Jan-2024 22:52 by rmk")
                                                             (* ; "Edited 19-Dec-2023 10:13 by rmk")
                                                             (* ; "Edited  3-Mar-2023 23:16 by rmk")
                                                             (* ; "Edited 11-Oct-2022 15:23 by rmk")
                                                             (* ; "Edited  1-Aug-2022 12:04 by rmk")
                                                             (* ; 
                                                        "Edited  2-Jul-93 21:31 by sybalskY:MV:ENVOS")
                                                             (* ; 
                           "Read a paragraph format spec from the FILE, and return it for later use.")
    (LET ((PARALOOKS (create PARALOOKS))
          (FILEPOS (GETFILEPTR FILE))
          (LOOKSLEN (\WIN FILE))
          TABFLG DEFTAB TABS)
         (FSETPLOOKS PARALOOKS 1STLEFTMAR (\SMALLPIN FILE))  (* ; 
                                                    "Left margin for the first line of the paragraph")
         (FSETPLOOKS PARALOOKS LEFTMAR (\SMALLPIN FILE))     (* ; 
                                                          "Left margin for the rest of the paragraph")
         (FSETPLOOKS PARALOOKS RIGHTMAR (\SMALLPIN FILE))    (* ; "Right margin for the paragraph")
         (FSETPLOOKS PARALOOKS LEADBEFORE (\SMALLPIN FILE))  (* ; "Leading before the paragraph")
         (FSETPLOOKS PARALOOKS LEADAFTER (\SMALLPIN FILE))   (* ; "Lead after the paragraph")
         (FSETPLOOKS PARALOOKS LINELEAD (\SMALLPIN FILE))    (* ; "inter-line leading")
         (SETQ TABFLG (BIN FILE))
         (FSETPLOOKS PARALOOKS QUAD (SELECTC (BIN FILE)
                                        (1 'LEFT)
                                        (2 'RIGHT)
                                        (3 'CENTERED)
                                        (4 'JUSTIFIED)
                                        (\TEDIT.THELP "UNRECOGNIZED QUAD BYTE")))
         (CL:UNLESS (ZEROP (LOGAND TABFLG 1))                (* ; "There are tabs to read")
             (SETQ DEFTAB (\SMALLPIN FILE))
             (CL:WHEN (ILEQ DEFTAB 1)                        (* ; 
                                                       "0/1 don't make sense, seemed to code default")
                 (SETQ DEFTAB DEFAULTTAB))
             (FSETPLOOKS PARALOOKS FMTDEFAULTTAB DEFTAB)
             [SETQ TABS (for TAB# from 1 to (BIN FILE) collect (create TAB
                                                                      TABX _ (\SMALLPIN FILE)
                                                                      TABKIND _
                                                                      (SELECTQ (BIN FILE)
                                                                          (0 'LEFT)
                                                                          (1 'RIGHT)
                                                                          (2 'CENTERED)
                                                                          (3 'DECIMAL)
                                                                          (4 'DOTTEDLEFT)
                                                                          (5 'DOTTEDRIGHT)
                                                                          (6 'DOTTEDCENTERED)
                                                                          (7 'DOTTEDDECIMAL)
                                                                          (\TEDIT.THELP]
             (FSETPLOOKS PARALOOKS FMTTABS TABS))
         (CL:UNLESS (FGETPLOOKS PARALOOKS FMTDEFAULTTAB)
                (FSETPLOOKS PARALOOKS FMTDEFAULTTAB DEFAULTTAB))
         (CL:UNLESS (ZEROP (LOGAND TABFLG 2))                (* ; 
                                                   "There are other paragraph parameters to be read.")
             (FSETPLOOKS PARALOOKS FMTSPECIALX (\SMALLPIN FILE))
                                                             (* ; 
                                                      "Special X location on page for this paragraph")
             (FSETPLOOKS PARALOOKS FMTSPECIALY (\SMALLPIN FILE))
             (FSETPLOOKS PARALOOKS FMTUSERINFO (\ARBIN FILE))
             (FSETPLOOKS PARALOOKS FMTPARATYPE (\ATMIN FILE))
             (FSETPLOOKS PARALOOKS FMTPARASUBTYPE (\ATMIN FILE))
             (FSETPLOOKS PARALOOKS FMTSTYLE (\ARBIN FILE))
             (FSETPLOOKS PARALOOKS FMTCHARSTYLES (\ARBIN FILE))
             (FSETPLOOKS PARALOOKS FMTNEWPAGEBEFORE (\ARBIN FILE))
             (FSETPLOOKS PARALOOKS FMTNEWPAGEAFTER (\ARBIN FILE))
             (FSETPLOOKS PARALOOKS FMTHEADINGKEEP (\ARBIN FILE))
             (FSETPLOOKS PARALOOKS FMTKEEP (\ARBIN FILE))
             (CL:WHEN (ILESSP (GETFILEPTR FILE)
                             (IPLUS FILEPOS LOOKSLEN))
                 (FSETPLOOKS PARALOOKS FMTBASETOBASE (\ARBIN FILE)))
             (CL:WHEN (ILESSP (GETFILEPTR FILE)
                             (IPLUS FILEPOS LOOKSLEN))
                 (FSETPLOOKS PARALOOKS FMTREVISED (\ARBIN FILE)))
             (CL:WHEN (ILESSP (GETFILEPTR FILE)
                             (IPLUS FILEPOS LOOKSLEN))
                 (FSETPLOOKS PARALOOKS FMTCOLUMN (\ARBIN FILE)))
             (CL:WHEN (ILESSP (GETFILEPTR FILE)
                             (IPLUS FILEPOS LOOKSLEN))
                 (FSETPLOOKS PARALOOKS FMTCHARSTYLES (\ARBIN FILE))))
         (CL:WHEN (ILESSP (GETFILEPTR FILE)
                         (IPLUS FILEPOS LOOKSLEN))           (* ; 
                               "There is more PARALOOKS info in this piece -- we probably lost data.")
             (TEDIT.PROMPTPRINT TEXTOBJ "WARNING: Newer file version; you lost PARALOOKS info" T)
             (SETFILEPTR FILE (IPLUS FILEPOS LOOKSLEN)))
         PARALOOKS])
)
(DEFINEQ

(\TEDIT.GET.OBJECT
  [LAMBDA (TSTREAM PIECE FILE CURTEXTBYTE# BYTELEN)          (* ; "Edited 15-Apr-2026 12:05 by rmk")
                                                             (* ; "Edited  1-Aug-2025 14:50 by rmk")
                                                             (* ; "Edited 28-Jul-2025 23:46 by rmk")
                                                             (* ; "Edited 31-Jul-2024 12:09 by rmk")
                                                             (* ; "Edited  5-Dec-2023 12:28 by rmk")
                                                             (* ; "Edited 26-Nov-2023 10:22 by rmk")
                                                             (* ; "Edited 21-Nov-2023 17:53 by rmk")
                                                             (* ; "Edited 25-Aug-2023 23:07 by rmk")
                                                             (* ; "Edited 12-Oct-2022 14:10 by rmk")
                                                            (* ; "Edited 12-Jun-90 17:50 by mitani")

    (* ;; "Get an object from FILE")

    (* ;; "TSTREAM =TEXTOBJ are used for the error message and (possibly for default charlooks)")

    (* ;; "CURFILEBYTE# = fileptr within the text section of the file where the object's text starts. On entry the file is positioned just before the object's GETFN in the looks section of the file. On exit, the fileptr points just after the GETFN in the looks section, after having been detoured to the text section for the getfn to read the object's data.")

    (LET ((TEXTOBJ (TEXTOBJ TSTREAM))
          FILEPTRSAVE GETFN OBJ)

         (* ;; "rrb 10-AUG-87 --- calculate the length of the image object's data.  This assumes that the file is currently pointed at the end of the data which is where the GETFN is written {I think}.")

         (* ;; "RMK:  Originally, BYTELEN was calculated here as (DIFFERENCE (GETFILEPTR FILE) CURTEXTBYTE#).  But this is garbage: (GETFILEPTR FILE) is in the looks section, CURTEXTBYTE# is in the text section. The caller knows the true value, now passes it in. ")

         (SETQ GETFN (\ATMIN FILE))                          (* ; 
                                                             "The GETFN for this kind of IMAGEOBJ")
         (SETQ FILEPTRSAVE (GETFILEPTR FILE))                (* ; 
                                             "Save our file location thru the building of the object")
         (SETFILEPTR FILE CURTEXTBYTE#)
         (SETQ OBJ (READIMAGEOBJ FILE GETFN NIL BYTELEN))
         (CL:WHEN (IMAGEOBJPROP OBJ 'UNKNOWNGETFN)           (* ; 
      "If the object has an unknown getfn property, then it's an encapsulated object.  Warn the user")
             (TEDIT.PROMPTPRINT TSTREAM (CONCAT (CL:IF (GETD (IMAGEOBJPROP OBJ 'UNKNOWNGETFN))
                                                    "Cannot read image object with GETFN "
                                                    "Image object with unknown GETFN ")
                                               (IMAGEOBJPROP OBJ 'UNKNOWNGETFN))
                    T))
         (SETFILEPTR FILE FILEPTRSAVE)
         (SETPC PIECE PCONTENTS OBJ)
         [FSETPC PIECE PCHARLOOKS (COND
                                     ((PREVPIECE PIECE)
                                      (PCHARLOOKS (PREVPIECE PIECE)))
                                     (T (OR (GETTOBJ TEXTOBJ DEFAULTCHARLOOKS)
                                            (\TEDIT.UNIQUIFY.CHARLOOKS (\TEDIT.CHARLOOKS.FROM.FONT
                                                                        DEFAULTFONT)
                                                   TEXTOBJ]
         (FSETPC PIECE PTYPE (CL:IF (IMAGEOBJPROP OBJ 'SUBSTREAM)
                                 SUBSTREAM.PTYPE
                                 OBJECT.PTYPE))
         OBJ])
)



(* ;; "Putting pageframe functions are on TEDIT-PAGE)")

(DEFINEQ

(\TEDIT.PUT.PCTB
  [LAMBDA (TEXTOBJ CHARSTREAM FORMATSTREAM CONTINUE KEEPSEPARATE)
                                                             (* ; "Edited 18-Apr-2026 14:56 by rmk")
                                                             (* ; "Edited  9-Apr-2026 23:19 by rmk")
                                                             (* ; "Edited  7-Apr-2026 12:31 by rmk")
                                                             (* ; "Edited 14-Feb-2026 10:32 by rmk")
                                                             (* ; "Edited  9-Sep-2025 21:32 by rmk")
                                                             (* ; "Edited 26-Apr-2025 00:11 by rmk")
                                                             (* ; "Edited 21-Oct-2024 00:33 by rmk")
                                                             (* ; "Edited 15-May-2024 17:03 by rmk")
                                                             (* ; "Edited 16-Mar-2024 12:40 by rmk")
                                                             (* ; "Edited 11-Mar-2024 00:33 by rmk")
                                                             (* ; "Edited 25-Jan-2024 00:00 by rmk")
                                                             (* ; "Edited 23-Jan-2024 13:43 by rmk")
                                                             (* ; "Edited 13-Jan-2024 19:57 by rmk")
                                                             (* ; "Edited 27-Sep-2023 23:50 by rmk")
                                                             (* ; "Edited  7-Sep-2023 23:43 by rmk")
                                                            (* ; "Edited 30-Apr-2021 14:46 by rmk:")
                                                             (* ; "Edited 19-May-99 21:58 by rmk:")
                                                             (* ; 
                                                        "Edited 27-May-93 16:00 by sybalsky:mv:envos")

    (* ;; "Put a representation of a piece table as plaintext on CHARSTREAM, preserving font changes and paragraph looks in a binary FORMATSTREAM. If FORMATSTREAM is not a stream, a stream is created here.   FORMATSTREAM=T if an unformatted stream is desired.  In that case, the format stream, whether created here or passed in, is not appended to the end of CHARSTREAM")

    (* ;; "")

    (* ;; "The characters and objects in the pieces are put out in the plaintext CHARSTREAM.  At the end of each sequence, when the byte positions are known, the hashed look-identifiers are put out to connect the looks back to the character sequences.")

    (* ;; "")

    (* ;; "If a sequence of pieces have equivalent piece properties (same character representation (fat, thin, utf-8 size) and looks) then the the characters of those pieces are concatenated and a single looks record is posted for the whole sequence.  For example, a sequence of THINFILE THINSTRING THINFILE pieces may be collapsed if they have the same font, new, etc. (but objects always have individual pieces).  Concatenation also stops at paralast sequences. ")

    (* ;; "")

    (* ;; "")

    (* ;; "If CONTINUE, return NEWPIECES for continued editing. NEWPIECES is the head of a  chain of new pieces that characterize the merged pieces on the file, to reestablish the correspondence between memory pieces and file pieces for continued editing. It is initialized to a throwaway dummy, and NEXTNEW slides down to link in subsequent new pieces.  If not CONTINUE, there will be no future editing in CHARSTREAM, no need to build new pieces.")

    (CL:WHEN (AND KEEPSEPARATE (NOT (STREAMP FORMATSTREAM)))
           (\TEDIT.THELP "FORMATSTREAM not provided with KEEPSEPARATE"))
    (CL:WHEN (EQ :UTF-8 (STREAMPROP CHARSTREAM 'FORMAT))
           (\TEDIT.PUT.UTF8.SPLITPIECES TEXTOBJ))
    (for PC PFILE NEXTNEW RUNLEN UNFORMATTED? (NSHIFTBYTES _ 0)
         (CURTEXTBYTE# _ 0)
         (OLDTEXTBYTE# _ 0)
         [UNFORMATTED? _ (PROG1 (EQ FORMATSTREAM T)
                             (CL:UNLESS (STREAMP FORMATSTREAM)
                                 [SETQ FORMATSTREAM (OPENSTREAM '{NODIRCORE} 'BOTH 'NEW
                                                           `((LINELENGTH ,MAX.SMALLP]))]
         (NEWPIECES _ (CL:WHEN CONTINUE (create PIECE)))
         (PCCOUNT _ 0)
         (EDITSTENTATIVE _ (GETTEXTPROP TEXTOBJ 'TEDIT.TENTATIVE))
         (LOOKSHASH _ (HASHARRAY 50))
         (PARAHASH _ (HASHARRAY 50))
         (*READTABLE* _ *TEDIT-FILE-READTABLE*)
         (*PRINT-BASE* _ 10)
         (EXTFORMAT _ (GETSTREAMPROP CHARSTREAM 'FORMAT))
         (EOLC _ (fetch (STREAM EOLCONVENTION) of CHARSTREAM)) inpieces (\TEDIT.FIRSTPIECE TEXTOBJ)
       first (SETQ NEXTNEW NEWPIECES) 

             (* ;; "All the layout and looks information goes into the LOOKSTREAM, CHARSTREAM is essentally plaintext.  Unless UNFORMATTED?, APPENDEDSTREAM is appended to the end of CHARSTREAM.")

             (* ;; "It seems that PCCOUNT isn't incremented for the PARA and CHARLOOKS lists, just for the page frame and the actual document pieces.")

             (CL:WHEN (FGETTOBJ TEXTOBJ TXTPAGEFRAMES)
                 (\TEDIT.PUT.PAGEFRAMES FORMATSTREAM (FGETTOBJ TEXTOBJ TXTPAGEFRAMES))
                 (add PCCOUNT 1))
             (\TEDIT.FLUSH.UNUSED.LOOKS TEXTOBJ)
             (\TEDIT.UNIQUIFY.ALL TEXTOBJ)                   (* ; 
                           "We can now use EQ tests on looks, and the lists may have been shortened.")
             (\TEDIT.PUT.PARALOOKS.LIST FORMATSTREAM PARAHASH (FGETTOBJ TEXTOBJ TXTPARALOOKSLIST))
             (\TEDIT.PUT.CHARLOOKS.LIST FORMATSTREAM LOOKSHASH (FGETTOBJ TEXTOBJ TXTCHARLOOKSLIST)) 

             (* ;; "The hash-tables map char and parlooks to unique id numbers")

             (* ;; "")

             (* ;; " We're ready to put the pieces on the output file.  ")

             (SETQ CURTEXTBYTE# (\GETFILEPTR CHARSTREAM))
             (SETQ OLDTEXTBYTE# CURTEXTBYTE#) 

             (* ;; "ZEROP should never happen, but...")

             (* ;; "PLEN is the number of characters in the piece, but they occupy different numbers of bytes depending on thin, fat, utf-8,XCCS...      ")

              unless (ZEROP (PLEN PC))
       do 
          (* ;; "PC starts a run of one or more pieces that can be collapsed together into a single file piece.  The paragraph looks are produced before the first piece of a new paragraph (first piece or previous piece was PPARALAST), then the piece(s)-characters, followed by the charlooks.  I.e., FORMATSTREAM describes the paragraph-start piece with its paragraph looks followed by its char looks.")

          (CL:WHEN (OR (NULL (PREVPIECE PC))
                       (PPARALAST (PREVPIECE PC)))
              (\TEDIT.PUT.PARALOOKS FORMATSTREAM PC PARAHASH)
              (add PCCOUNT 1))
          (CL:WHEN (MEMB EXTFORMAT '(:MCCS :XCCS))

              (* ;; "For MCCS, CHARSET will put out the char-shifting prefix bytes as needed. In format-version 3 all the file bytes belong to a piece, no skipping in the file.   TEDIT.GET calls \TEDIT.INTERPRET.MCCS.SHIFTS to shave those bytes.  NSHIFTBYTES is used here so the edit can continue.")

              (CHARSET CHARSTREAM (CL:IF (MEMB (PTYPE PC)
                                               FAT.PTYPES)
                                      T
                                      0))
              (SETQ NSHIFTBYTES (IDIFFERENCE (\GETFILEPTR CHARSTREAM)
                                       OLDTEXTBYTE#)))
          (do (\TEDIT.PUT.PCTB.PIECEDATA PC CHARSTREAM TEXTOBJ FORMATSTREAM OLDTEXTBYTE#)
              (CL:UNLESS (\TEDIT.PUT.PCTB.MERGEABLE PC (NEXTPIECE PC)
                                EDITSTENTATIVE EXTFORMAT TEXTOBJ)
                     (RETURN))
              (SETQ PC (NEXTPIECE PC))) 

          (* ;; "PC is the last piece written for a mergeable sequence. Finish off the corresponding file piece by writing PC's character looks into FORMATSTREAM. ")

          (SETQ CURTEXTBYTE# (\GETFILEPTR CHARSTREAM))
          (SETQ RUNLEN (IDIFFERENCE CURTEXTBYTE# OLDTEXTBYTE#))
          (CL:UNLESS (EQ OBJECT.PTYPE (PTYPE PC))            (* ; 
                                              "Objects get their charlooks from the preceding piece.")
              (\TEDIT.PUT.CHARLOOKS FORMATSTREAM RUNLEN PC EDITSTENTATIVE LOOKSHASH))
          (add PCCOUNT 1) 

          (* ;; "The output for the sequence of mergeable pieces is complete.")

          (CL:WHEN NEWPIECES

              (* ;; "Only for continued editing: make a new piece that describes those characters as they now reside on CHARSTREAM. ")

              (SETQ NEXTNEW (\TEDIT.PUT.PCTB.NEXTNEW NEXTNEW PC OLDTEXTBYTE# RUNLEN EXTFORMAT TEXTOBJ
                                   EOLC NSHIFTBYTES)))
          (SETQ OLDTEXTBYTE# CURTEXTBYTE#) finally 

                                 (* ;; "Finalize and append FORMATSTREAM unless unformatted or KEEPSEPARATE (for splitting). If KEEPSEPARATE, the caller must have provided the formatstream")

                                                 (CL:UNLESS UNFORMATTED?
                                                     (\TEDIT.PUT.TRAILER FORMATSTREAM (\GETFILEPTR
                                                                                       CHARSTREAM)
                                                            PCCOUNT 3 (FGETTOBJ TEXTOBJ DOCPROPS)))
                                                 (CL:UNLESS (OR UNFORMATTED? KEEPSEPARATE)
                                                     (COPYBYTES FORMATSTREAM CHARSTREAM 0
                                                            (GETEOFPTR FORMATSTREAM)))
                                                 (RETURN (CL:WHEN NEWPIECES

                                   (* ;; "Throw away the dummy head of the new piece chain (NEWPIECES is NIL if not continuing). The caller must install the new pieces.")

                                                             (NEXTPIECE NEWPIECES))])

(\TEDIT.PUT.PCTB.PIECEDATA
  [LAMBDA (PC CHARSTREAM TEXTOBJ FORMATSTREAM OLDTEXTBYTE#)  (* ; "Edited 18-Apr-2026 14:54 by rmk")
                                                             (* ; "Edited  9-Apr-2026 13:37 by rmk")
                                                             (* ; "Edited  7-Apr-2026 18:10 by rmk")
                                                             (* ; "Edited 21-Oct-2024 00:33 by rmk")
                                                             (* ; "Edited 15-May-2024 17:04 by rmk")

    (* ;; "Write the data defining PC on CHARSTREAM.")

    (* ;; "TEXTOBJ needed only to reopen any backing files that might currently be closed.")

    (* ;; "FORMATSTREAM is needed for objects.")

    (* ;; "OLDTEXTBYTE# needed to deal with XCCS shift before objects.")

    (LET (PFILE)
         (CL:WHEN (MEMB (PTYPE PC)
                        FILE.PTYPES)                         (* ; 
                                                             "Set up to read  characters from PFILE")
             (SETQ PFILE (PCONTENTS PC))
             (CL:UNLESS (\GETSTREAM PFILE 'INPUT T)          (* ; 
                                                             "Make sure this input file is open.")
                 (SETQ PFILE (\TEDIT.REOPEN.STREAM TEXTOBJ PFILE)))
                                                             (* ; 
                                                        "Presumably only happens once for each PFILE")
             (\SETFILEPTR PFILE (PFPOS PC)))
         (SELECTC (PTYPE PC)
             (THINFILE.PTYPE                                 (* ; 
                                                             "\OUTCHAR deals with external format")
                  (for I from 1 to (PLEN PC) do (\OUTCHAR CHARSTREAM (BIN PFILE))))
             (STRING.PTYPES                                  (* ; 
                                                          "Could split with infatstring/inthinstring")
                            (for CH instring (PCONTENTS PC) do (\OUTCHAR CHARSTREAM CH)))
             (FATFILE2.PTYPE 
                  (for I from 1 to (PLEN PC) do (\OUTCHAR CHARSTREAM (\WIN PFILE))))
             (UTF8.PTYPE (for I from 1 to (PLEN PC) do (\OUTCHAR CHARSTREAM (UTF8.INCCODEFN PFILE))))
             (OBJECT.PTYPE                                   (* ; "It's an object, use its PUTFN.")
                           (\TEDIT.PUT.OBJECT PC CHARSTREAM FORMATSTREAM OLDTEXTBYTE#)

                           (* ;; "0 indicates that nothing special needs to be done here to recover the looks of this piece.  \TEDIT.GET.PIECES3 says that the object-piece looks are taken from the previous piece (or default for first piece.  In earlier versions the value 1 indicated that the looks were not indexed and therefore had to be written explicitly here. This byte won't be needed in the next version of the format.")

                           (BOUT FORMATSTREAM 0))
             (\TEDIT.THELP "OTHER PTYPES"])

(\TEDIT.PUT.TRAILER
  [LAMBDA (FORMATSTREAM PIECESTART PCCOUNT VERSION PROPS)    (* ; "Edited  9-Sep-2025 17:55 by rmk")
                                                             (* ; "Edited 13-Jan-2024 10:13 by rmk")

    (* ;; "Finalize FORMATSTREAM.  We sneak in the date here--at the end of the looks, after the last look but before the final pointers, so that it doesn't interfere with anything.  TEDIT.FILEDATE pulls it out if it's there.")

    (LET ((PROPSPTR (GETFILEPTR FORMATSTREAM)))
         (CL:WHEN PROPS                                      (* ; "CONS to protect an atom or number")
             (PRIN2 (CONS PROPS)
                    FORMATSTREAM *TEDIT-FILE-READTABLE*)
             (PRIN1 "PROPS:" FORMATSTREAM)
             (\DWOUT FORMATSTREAM (IPLUS 4 (IDIFFERENCE (GETFILEPTR FORMATSTREAM)
                                                  PROPSPTR))))
         (PRIN1 "DATE:" FORMATSTREAM)
         (\DWOUT FORMATSTREAM (IDATE))
         (\DWOUT FORMATSTREAM PIECESTART)                    (* ; "Position of the first piece")
         (\WOUT FORMATSTREAM PCCOUNT)                        (* ; 
                                                          "Number of pieces followed by the password")
         (\WOUT FORMATSTREAM (IPLUS 31415 VERSION])

(\TEDIT.PUT.PCTB.MERGEABLE
  [LAMBDA (PREVPC PC EDITSTENTATIVE EXTFORMAT TEXTOBJ)       (* ; "Edited 12-Apr-2026 21:44 by rmk")
                                                             (* ; "Edited  7-Apr-2026 18:07 by rmk")
                                                             (* ; "Edited  1-Aug-2025 14:51 by rmk")
                                                             (* ; "Edited 25-Apr-2025 23:50 by rmk")
                                                             (* ; "Edited 24-Apr-2025 16:02 by rmk")
                                                             (* ; "Edited 14-May-2024 11:55 by rmk")
                                                             (* ; "Edited 12-May-2024 21:57 by rmk")
                                                             (* ; "Edited 23-Jan-2024 09:12 by rmk")
                                                             (* ; "Edited 12-Jan-2024 09:46 by rmk")
                                                             (* ; "Edited  5-Jan-2024 11:34 by rmk")
                                                             (* ; "Edited 30-Dec-2023 00:44 by rmk")
                                                             (* ; "Edited 22-Sep-2023 10:12 by rmk")
                                                             (* ; "Edited  6-Sep-2023 00:03 by rmk")
                                                             (* ; "Edited 24-Aug-2023 11:03 by rmk")
    (CL:WHEN PC
        [LET ((PREVTYPE (PTYPE PREVPC))
              (PCTYPE (PTYPE PC)))
             (CL:UNLESS [OR (EQ OBJECT.PTYPE PREVTYPE)
                            (EQ OBJECT.PTYPE PCTYPE)
                            (NEQ (PCHARLOOKS PREVPC)
                                 (PCHARLOOKS PC))
                            (NEQ (PPARALOOKS PREVPC)
                                 (PPARALOOKS PC))
                            (PPARALAST PREVPC)
                            (AND EDITSTENTATIVE (NEQ (PNEW PREVPC)
                                                     (PNEW PC]

                 (* ;; "PC cannot merge with PREVPC if PREVPC ends in EOL (even if not PPARALAST). (We assume here that EOL's of interest appear only in last-of-piece position.)  For some input piece types we can make the decision without bothering to look at their last character. If the destination EXTFORMAT is :UTF-8, the splitter has presumably arranged it so that EOL's only appear in thin string and file pieces.")

                 [AND (SELECTQ EXTFORMAT
                          ((:MCCS :XCCS) 
                                         (* ;; "All thin strings and files are mergeable, all fat pieces are mergeable, since they all go to FAT2.  ")

                               (EQ (THINPIECEP PREVPC)
                                   (THINPIECEP PC)))
                          (:UTF-8 

                                 (* ;; "UTF8 pieces with the same bytesperchar are mergeable.  We rely on \TEDIT.PUT.UTF8.SPLITPIECES to examine string pieces and split thin strings that include mixtures of Ascii and non-Ascii characters, and to split fat pieces that may contain Ascii character in 2-byte form. After splitting, all pieces with the same PBYTESPERCHAR can be merged.")

                                  (EQ (FGETPC PREVPC PBYTESPERCHAR)
                                      (FGETPC PC PBYTESPERCHAR)))
                          NIL)
                      (OR (EQ PREVTYPE UTF8.PTYPE)
                          [AND (EQ EXTFORMAT :UTF-8)
                               (NOT (MEMB PREVTYPE (CONSTANT (LIST THINFILE.PTYPE THINSTRING.PTYPE]
                          (NOT (MEMB (\TEDIT.PIECE.NTHCHARCODE PREVPC (SUB1 (PLEN PREVPC)))
                                     (CHARCODE (EOL LF])])])

(\TEDIT.PUT.UTF8.SPLITPIECES
  [LAMBDA (TEXTOBJ)                                          (* ; "Edited 12-Apr-2026 21:49 by rmk")
                                                             (* ; "Edited  9-Apr-2026 00:05 by rmk")
                                                             (* ; "Edited 19-Jan-2025 15:02 by rmk")
                                                             (* ; "Edited 17-Mar-2024 00:14 by rmk")
                                                             (* ; "Edited  3-Feb-2024 14:52 by rmk")
                                                             (* ; "Edited 11-Jan-2024 23:29 by rmk")
                                                             (* ; "Edited  5-Jan-2024 11:37 by rmk")
                                                             (* ; "Edited 30-Dec-2023 11:27 by rmk")

    (* ;; "We are putting to a UTF-8 format file. This function splits pieces if necessary to ensure that all the characters in a piece map to Unicode characters with the same-length UTF8 encoding. That length is stored in PUTF8BYTESPERCHAR.")

    (for PC inpieces (\TEDIT.FIRSTPIECE TEXTOBJ)
       do (SELECTC (PTYPE PC)
              (UTF8.PTYPE)
              (STRING.PTYPES (for CH BPC instring (PCONTENTS PC) as I from 1
                                do 

                                 (* ;; "If BPC changes, split off and mark the prefix piece with the previous value, go back to the main loop to continue on the residual suffix piece.")

                                   (if (EQ I 1)
                                       then (SETQ BPC (NUTF8-CODE-BYTES (MTOUCODE CH)))
                                            (FSETPC PC PBYTESPERCHAR BPC) 
                                                             (* ; 
                                                             "The first character defines the piece")
                                     elseif (EQ BPC (NUTF8-CODE-BYTES (MTOUCODE CH)))
                                     else (\TEDIT.SPLITPIECE PC (SUB1 I)
                                                 TEXTOBJ)
                                          (SETQ PC (PREVPIECE PC)) 
                                                             (* ; 
                                                             "Prefix piece always exists since I>1")
                                          (FSETPC PC PBYTESPERCHAR BPC) 
                                                             (* ; 
                                                          "Mark it, iteration continues on its next.")
                                          (RETURN))))
              (THINFILE.PTYPE 
                   (CL:UNLESS (EQ :UTF-8 (GETSTREAMPROP (PCONTENTS PC)
                                                'FORMAT))    (* ; "Could be above Ascii")
                       (for I BPC (PFILE _ (PCONTENTS PC)) from 1 to (PLEN PC)
                          first (\SETFILEPTR PFILE (PFPOS PC))
                          do (if (EQ I 1)
                                 then [SETQ BPC (NUTF8-CODE-BYTES (MTOUCODE (BIN PFILE]
                                      (FSETPC PC PBYTESPERCHAR BPC)
                               elseif [EQ BPC (NUTF8-CODE-BYTES (MTOUCODE (BIN PFILE]
                               else (\TEDIT.SPLITPIECE PC (SUB1 I)
                                           TEXTOBJ)
                                    (SETQ PC (PREVPIECE PC))
                                    (FSETPC PC PBYTESPERCHAR BPC)
                                    (RETURN)))))
              (FATFILE2.PTYPE                                (* ; "XCCS pieces")
                   (for I BPC CH (PFILE _ (PCONTENTS PC)) from 1 to (PLEN PC)
                      first (\SETFILEPTR PFILE (PFPOS PC))
                      do (SETQ CH (\WIN PFILE))
                         (if (EQ I 1)
                             then (SETQ BPC (NUTF8-CODE-BYTES (MTOUCODE CH)))
                                  (FSETPC PC PBYTESPERCHAR BPC)
                           elseif (EQ BPC (NUTF8-CODE-BYTES (MTOUCODE CH)))
                           else (\TEDIT.SPLITPIECE PC (SUB1 I)
                                       TEXTOBJ)
                                (SETQ PC (PREVPIECE PC))
                                (FSETPC PC PBYTESPERCHAR BPC)
                                (RETURN))))
              NIL])

(\TEDIT.PUT.MCCS.SPLITPIECES
  [LAMBDA (TEXTOBJ)                                          (* ; "Edited 15-Feb-2026 23:45 by rmk")

    (* ;; "We are putting to a :MCCS format file, and MCCS doesn't support single-byte runs of non-charset 0 characters. This function splits fat pieces into subpieces with only charset-0 characters or no charset-0 characters.  The former will be put out as THINFILE pieces, the latter as FATFILE2.")

    (for PC FIRST0 FIRSTNON0 inpieces (\TEDIT.FIRSTPIECE TEXTOBJ)
       when [AND (MEMB (PTYPE PC)
                       (CONSTANT (LIST FATSTRING.PTYPE FATFILE2.PTYPE UTF8.PTYPE)))
                 (SETQ FIRST0 (find I from 0 to (PLAST PC)
                                 suchthat (EQ 0 (\CHARSET (\TEDIT.PIECE.NTHCHARCODE PC I]
       do (if [SETQ FIRSTNON0 (find I from (ADD1 FIRST0) to (PLAST PC)
                                 suchthat (NEQ 0 (\CHARSET (\TEDIT.PIECE.NTHCHARCODE PC I]
              then 
                   (* ;; "xxx000yyy --> xxx 000yyy   or 000yyy --> 000 yyy")

                   (\TEDIT.SPLITPIECE PC (CL:IF (EQ FIRST0 0)
                                             FIRSTNON0
                                             FIRST0)
                          TEXTOBJ)                           (* ; "Iterate to the residual piece")
                   (SETQ PC (PREVPIECE PC))
            elseif (NEQ 0 FIRST0)
              then 
                   (* ;; "xxx000")

                   (\TEDIT.SPLITPIECE PC FIRST0 TEXTOBJ])

(\TEDIT.PUT.PCTB.NEXTNEW
  [LAMBDA (NEXTNEW PC OLDBYTE# RUNLEN EXTFORMAT TEXTOBJ EOLC NSHIFTBYTES)
                                                             (* ; "Edited 24-Apr-2026 20:45 by rmk")
                                                             (* ; "Edited 17-Apr-2026 23:55 by rmk")
                                                             (* ; "Edited 12-Apr-2026 21:47 by rmk")
                                                             (* ; "Edited  9-Apr-2026 13:20 by rmk")
                                                             (* ; "Edited  7-Apr-2026 18:12 by rmk")
                                                             (* ; "Edited 15-Feb-2026 15:09 by rmk")
                                                             (* ; "Edited 25-Apr-2025 08:48 by rmk")
                                                             (* ; "Edited 26-Mar-2025 09:27 by rmk")
                                                             (* ; "Edited 21-Oct-2024 00:26 by rmk")
                                                             (* ; "Edited 14-May-2024 18:54 by rmk")
                                                             (* ; "Edited 13-May-2024 08:27 by rmk")
                                                             (* ; "Edited 24-Jan-2024 23:29 by rmk")
                                                             (* ; "Edited 23-Jan-2024 15:24 by rmk")
                                                             (* ; "Edited 21-Jan-2024 10:34 by rmk")
                                                             (* ; "Edited 12-Jan-2024 16:24 by rmk")
                                                             (* ; "Edited  5-Jan-2024 17:46 by rmk")
                                                             (* ; "Edited 30-Dec-2023 21:56 by rmk")

    (* ;; "This updates the piece chain that is created for continued editing.")

    (* ;; "Note that the PCONTENTS (=  PFILE) field for these file pieces isn't filled in, that has to be done after CHARSTREAM is closed and reopened at the TEDIT.PUT level.  ")

    (* ;; "NSHIFTBYTES ignores any MCCS/XCCS charset shifts at the beginning of the new piece.")

    (SETQ RUNLEN (IDIFFERENCE RUNLEN NSHIFTBYTES))
    (FSETPC NEXTNEW NEXTPIECE (SETQ NEXTNEW (create PIECE
                                               using PC PFPOS _ (IPLUS NSHIFTBYTES OLDBYTE#)
                                                     PLEN _ RUNLEN PREVPIECE _ NEXTNEW PTREENODE _ 
                                                     NIL)))
    (SELECTQ EXTFORMAT
        (:UTF-8 (FSETPC NEXTNEW PTYPE (CL:IF (EQ 1 (FGETPC PC PBYTESPERCHAR))
                                          THINFILE.PTYPE
                                          UTF8.PTYPE)))
        ((:MCCS :XCCS)                                       (* ; 
                                         "String pieces can be merged with corresponding file pieces")
             (FSETPC NEXTNEW PTYPE (SELECTC (PTYPE PC)
                                       (THINSTRING.PTYPE 
                                            THINFILE.PTYPE)
                                       (FATSTRING.PTYPE 
                                            (FSETPC NEXTNEW PBYTESPERCHAR 2)
                                            FATFILE2.PTYPE)
                                       (PTYPE PC))))
        NIL)                                                 (* ; 
                                        "Accumulate PLEN across merged pieces. Objects are always 1.")
    [FSETPC NEXTNEW PLEN (CL:IF (EQ OBJECT.PTYPE (PTYPE NEXTNEW))
                             1
                             (IQUOTIENT RUNLEN (PBYTESPERCHAR NEXTNEW)))]
    (CL:UNLESS (EQ EOLC CR.EOLC)                             (* ; 
                                        "The file may have LF, but we want to restore EOL internally")
        (CL:WHEN [AND (EQ THINFILE.PTYPE (PTYPE NEXTNEW))
                      (EQ (CHARCODE EOL)
                          (\TEDIT.PIECE.NTHCHARCODE PC (PLAST PC]
            (if (EQ 1 (PLEN NEXTNEW))
                then (FSETPC NEXTNEW PTYPE THINSTRING.PTYPE)
                     (FSETPC NEXTNEW PCONTENTS (ALLOCSTRING 1 (CHARCODE EOL)))
              else (add (FGETPC NEXTNEW PLEN)
                        -1)                                  (* ; "We know it's thin, maybe paralast")
                   (SETQ NEXTNEW (\TEDIT.MAKE.STRINGPIECE NEXTNEW (CHARCODE EOL)))
                   (FSETPC (PREVPIECE NEXTNEW)
                          PPARALAST NIL))))
    NEXTNEW])

(\TEDIT.INSERT.NEWPIECES
  [LAMBDA (DESTSTREAM OLDSTREAM NEWPIECES)                   (* ; "Edited 10-Apr-2026 09:25 by rmk")
                                                             (* ; "Edited 14-May-2024 18:38 by rmk")
                                                             (* ; "Edited 29-Apr-2024 10:13 by rmk")
                                                             (* ; "Edited 20-Mar-2024 10:59 by rmk")
                                                             (* ; "Edited 17-Mar-2024 12:06 by rmk")
                                                             (* ; "Edited  5-Feb-2024 09:24 by rmk")
                                                             (* ; "Edited  3-Feb-2024 23:59 by rmk")
                                                             (* ; "Edited 21-Jan-2024 09:21 by rmk")
                                                             (* ; "Edited 12-Jan-2024 21:06 by rmk")
                                                             (* ; "Edited 18-Dec-2023 17:00 by rmk")
                                                             (* ; "Edited 11-Nov-2023 16:31 by rmk")
                                                             (* ; "Edited  8-Sep-2023 16:32 by rmk")

    (* ;; "This makes the pieces and BTREE of OLDSTREAM's TEXTOBJ consistent with the NEWPIECES chain and the new DESTSTREAM.  The character numbers of old and new pieces correspond, so editing can continue without updating panes, lines, or selections (which are all based on character numbers, not particular pieces).  This puts DESTSTREAM as the PFILE of each new file piece and then installs NEWPIECES in DESTSTREAM, replacing the BTREE and pieces already there.")

    (* ;; "The \SETFILEPTR translates OLDSTREAM's buffer parameters to the new file. ")

    (LET ((TEXTOBJ (TEXTOBJ! (fetch (TEXTSTREAM TEXTOBJ) of OLDSTREAM)))
          FILEPTR)
         (SETQ FILEPTR (\TEDIT.TEXTGETFILEPTR OLDSTREAM))    (* ; "Restore the editing parameters")
         (for PC inpieces NEWPIECES when (MEMB (PTYPE PC)
                                               FILE.PTYPES) do (FSETPC PC PCONTENTS DESTSTREAM))
                                                             (* ; "Non-object pieces are on OFILE")

         (* ;; "Here, finally, we toss the out-of-date pieces to install the new ones. For complete safety, the rest should be uninterruptable (although the file has just been saved, so nothing would really be lost)")

         (\TEDIT.MAKEPCTB TEXTOBJ)
         (\TEDIT.INSERTPIECES NEWPIECES NIL TEXTOBJ)         (* ; 
                                                             "Build the tree, then fix the stream")

         (* ;; "This guards agains the possiblity that a sequence of edits somehow got the positioning parameters cached in the stream out of step with the document.  This ensures that they are consistent after all the pieces have been written out.")

         (\TEDIT.TEXTSETFILEPTR OLDSTREAM (IMAX 0 (IMIN FILEPTR (FGETTOBJ TEXTOBJ TEXTLEN])

(\TEDIT.PUTRESET
  [LAMBDA (PROC&VALUE)                                       (* jds "15-May-85 16:38")
    (CONS (CAR PROC&VALUE)
          (PROCESSPROP (CAR PROC&VALUE)
                 'BEFOREEXIT
                 (CDR PROC&VALUE])

(\ARBOUT
  [LAMBDA (STREAM ITEM)                                      (* ; "Edited 19-Dec-2023 10:14 by rmk")
                                                             (* ; "Edited  7-Sep-2023 09:06 by rmk")
                                                             (* ; "Edited 20-Apr-88 19:55 by jds")

    (* ;; "Write an arbitrary MKSTRING-able thing in length-contents form.  SIZE is in characters, not bytes, which is OK because \STRINGIN uses READCCODE.")

    (LET [(SIZE (AND ITEM (NCHARS ITEM T *TEDIT-FILE-READTABLE*]
         (\WOUT STREAM (OR SIZE 0))
         (OR (NOT ITEM)
             (ZEROP SIZE)
             (PRIN2 ITEM STREAM *TEDIT-FILE-READTABLE*))
         NIL])

(\ATMOUT
  [LAMBDA (STREAM ATOM)                                      (* ; "Edited 19-Dec-2023 10:14 by rmk")
                                                             (* jds "30-Jan-85 14:46")
                                                             (* Write an atom's characters in 
                                                             length-contents form.)
    (\WOUT STREAM (COND
                     (ATOM (NCHARS ATOM))
                     (T 0)))
    (OR (NOT ATOM)
        (ZEROP (NCHARS ATOM))
        (for CH inatom ATOM do (\BOUT STREAM CH])

(\DWOUT
  [LAMBDA (FILE NUMBER)                                      (* jds " 3-JAN-83 15:30")
    (\BOUT FILE (LOGAND 255 (LRSH NUMBER 24)))
    (\BOUT FILE (LOGAND 255 (LRSH NUMBER 16)))
    (\BOUT FILE (LOGAND 255 (LRSH NUMBER 8)))
    (\BOUT FILE (LOGAND 255 NUMBER])

(\STRINGOUT
  [LAMBDA (STREAM STRING LEN)                                (* ; "Edited 19-Dec-2023 10:14 by rmk")
                                                             (* jds " 1-May-84 11:58")

         (* Write a string on a file in length-contents form;
         one word for the length, and one byte per character contained.)

    (SETQ LEN (OR LEN (NCHARS STRING)))
    (\WOUT STREAM LEN)
    (OR (ZEROP LEN)
        (for CH instring STRING as I from 1 to LEN do (\BOUT STREAM CH])
)
(DEFINEQ

(\TEDIT.PUT.CHARLOOKS.LIST
  [LAMBDA (LOOKSFILE LOOKSHASH LOOKSLIST LOOKSHASH)          (* ; "Edited 19-Dec-2023 10:14 by rmk")
                                                             (* ; "Edited 25-Aug-2023 11:39 by rmk")
                                                             (* ; "Edited 15-Aug-2023 23:08 by rmk")
                                                             (* jds " 5-Mar-85 15:58")
                                                             (* ; 
                                                   "Write the list of CHARLOOKSs into the font file.")

    (* ;; "Returns a hasharray that will map from a given CHARLOOKS to its index in the list.  Those position numbers are then written in the individual piece descriptions, and are used to reconstruct the piece looks when the file is read back in.  These descriptions are written in a 0-character pseudo-piece")

    (\DWOUT LOOKSFILE 0)                                     (* ; 
                                         "No characters, marked as containing the list of CHARLOOKSs")
    (\WOUT LOOKSFILE \PieceDescriptorCHARLOOKSLIST)
    (\WOUT LOOKSFILE (FLENGTH LOOKSLIST))                    (* ; "Number of charlooks to follow")
    (for I from 1 as LOOKS in LOOKSLIST do 
                                           (* ;; 
                                        "Write each charlooks, in the order they appear in the list.")

                                           (\TEDIT.PUT.SINGLE.CHARLOOKS LOOKSFILE LOOKS)
                                           (PUTHASH LOOKS I LOOKSHASH])

(\TEDIT.PUT.SINGLE.CHARLOOKS
  [LAMBDA (FORMATSTREAM LOOKS)                               (* ; "Edited 25-Sep-2025 18:31 by rmk")
                                                             (* ; "Edited  1-Aug-2025 13:42 by rmk")
                                                             (* ; "Edited 21-Jul-2025 23:32 by rmk")
                                                             (* ; "Edited 20-Jul-2025 13:17 by rmk")
                                                             (* ; "Edited 22-Apr-2025 14:50 by rmk")
                                                             (* ; "Edited  2-Jan-2025 10:43 by rmk")
                                                             (* ; "Edited 13-Aug-2024 08:47 by rmk")
                                                             (* ; "Edited 31-Jul-2024 00:05 by rmk")
                                                             (* ; "Edited 16-Jan-2024 23:07 by rmk")
                                                             (* ; "Edited 19-Dec-2023 10:14 by rmk")
                                                             (* ; "Edited 26-Aug-2023 11:29 by rmk")
                                                             (* ; "Edited 15-Aug-2023 23:17 by rmk")
                                                             (* ; "Edited 30-May-91 20:26 by jds")

    (* ;; "Put out a single CHARLOOKS description.")

    (LET ((FILEPOS (GETFILEPTR FORMATSTREAM))
          (FONT (FGETCLOOKS LOOKS CLFONT))
          LEN PROPS)
         (\WOUT FORMATSTREAM 0)                              (* ; 
                                                         "Reserve space for the length of this looks")
         [if (type? FONTCLASS FONT)
             then                                            (* ; 
                                         "For font classes, we need to save a list of device-FD sets")
                  (\ARBOUT FORMATSTREAM (FONTCLASSUNPARSE FONT))
           else                                              (* ; 
                                                            "For FONTDESCRIPTORs, do it the easy way")
                (\ATMOUT FORMATSTREAM (FONTPROP FONT 'FAMILY](* ; "The font family")
         (\WOUT FORMATSTREAM (OR (FONTPROP FONT 'SIZE)
                                 0))                         (* ; "Size of the type, in points")
         (\SMALLPOUT FORMATSTREAM (OR (FGETCLOOKS LOOKS CLOFFSET)
                                      0))                    (* ; "Super/subscripting distance")
         (if [AND (FGETCLOOKS LOOKS CLSTYLE)
                  (NOT (ZEROP (FGETCLOOKS LOOKS CLSTYLE]
             then (\ARBOUT FORMATSTREAM (FGETCLOOKS LOOKS CLSTYLE))
           else (\WOUT FORMATSTREAM 0))

         (* ;; "We create our own props structure here rather than using the CHARLOOKS CLPROPS directly.  That layout may change in the future.")

         (PUTMULTI PROPS 'COLOR (OR (FGETCLOOKS LOOKS CLCOLOR)
                                    'BLACK))
         (CL:WHEN (FGETCLOOKS LOOKS CLUSERINFO)
             (PUTMULTI PROPS 'USERINFO (FGETCLOOKS LOOKS CLUSERINFO)))
         (CL:WHEN (FGETCLOOKS LOOKS CLCHARENCODING)
             (PUTMULTI PROPS 'CHARENCODING (FGETCLOOKS LOOKS CLCHARENCODING)))
         (\ARBOUT FORMATSTREAM (CONS 'CHARPROPS PROPS))
         (\WOUT FORMATSTREAM (LOGOR (CL:IF (FGETCLOOKS LOOKS CLSELBEFORE)
                                        8192
                                        0)
                                    (CL:IF (FGETCLOOKS LOOKS CLUNBREAKABLE LOOKS)
                                        4096
                                        0)
                                    (CL:IF (FGETCLOOKS LOOKS CLLEADER)
                                        2048
                                        0)
                                    (CL:IF (FGETCLOOKS LOOKS CLINVERTED)
                                        1024
                                        0)
                                    (CL:IF (EQ 'BOLD (FONTPROP FONT 'WEIGHT))
                                        512
                                        0)
                                    (CL:IF (EQ 'ITALIC (FONTPROP FONT 'SLOPE))
                                        256
                                        0)
                                    (CL:IF (FGETCLOOKS LOOKS CLULINE)
                                        128
                                        0)
                                    (CL:IF (FGETCLOOKS LOOKS CLOLINE)
                                        64
                                        0)
                                    (CL:IF (FGETCLOOKS LOOKS CLSTRIKE)
                                        32
                                        0)
                                    (CL:IF (FGETCLOOKS LOOKS CLSMALLCAP)
                                        16
                                        0)
                                    (CL:IF (FGETCLOOKS LOOKS CLPROTECTED)
                                        8
                                        0)
                                    (CL:IF (FGETCLOOKS LOOKS CLINVISIBLE)
                                        4
                                        0)
                                    (CL:IF (FGETCLOOKS LOOKS CLSELAFTER)
                                        2
                                        0)
                                    (CL:IF (FGETCLOOKS LOOKS CLCANCOPY)
                                        1
                                        0)))

         (* ;; "Now go fill in the length field at the front of the LOOKS.  (ALL looks info should be written out BEFORE this comment.)")

         (SETQ LEN (IDIFFERENCE (GETFILEPTR FORMATSTREAM)
                          FILEPOS))                          (* ; "The length of this set of looks")
         (SETFILEPTR FORMATSTREAM FILEPOS)                   (* ; "Go write the length field")
         (\WOUT FORMATSTREAM LEN)                            (* ; "And back to the end of the file")
         (SETFILEPTR FORMATSTREAM -1])

(\TEDIT.PUT.CHARLOOKS
  [LAMBDA (FORMATSTREAM BYTELEN PC EDITSTENTATIVE LOOKSHARRAY)
                                                             (* ; "Edited  9-Apr-2026 23:24 by rmk")
                                                             (* ; "Edited  1-Aug-2025 14:51 by rmk")
                                                             (* ; "Edited 14-May-2024 10:24 by rmk")
                                                             (* ; "Edited 13-Jan-2024 16:35 by rmk")
                                                             (* ; "Edited 30-Dec-2023 16:25 by rmk")
                                                             (* ; "Edited 23-Aug-2023 22:27 by rmk")
                                                             (* ; "Edited 24-Jul-2023 17:21 by rmk")
                                                             (* ; "Edited  8-Sep-2022 22:54 by rmk")
                                                             (* ; "Edited 30-May-91 21:45 by jds")

    (* ;; "Put a description of PC's charlooks into FORMATSTREAM.  The looks apply to bytes OLDBYTE# thru CURBYTE#-1")

    (\DTEST PC 'PIECE)
    (\TEDIT.PUT.CHARLOOKS1 FORMATSTREAM BYTELEN (GETHASH (PCHARLOOKS PC)
                                                       LOOKSHARRAY)
           (AND EDITSTENTATIVE PC (PNEW PC))
           (EQ FATFILE2.PTYPE (PTYPE PC])

(\TEDIT.PUT.CHARLOOKS1
  [LAMBDA (FORMATSTREAM BYTELEN CHARLOOKSINDEX NEW FAT)      (* ; "Edited 13-Jan-2024 16:36 by rmk")

    (* ;; "Put out the actual bytes to represent a piece with the indicated properties.  This is split out as a separate helper function to hide Tedit innards from Lafite when it makes its dummy piece entries (LA.ADJUST.FORMATTING).")

    (\DWOUT FORMATSTREAM BYTELEN)                            (* ; "The length of this piece run")
    (\WOUT FORMATSTREAM \PieceDescriptorLOOKS)               (* ; 
                                                             "Mark this as setting the piece's looks")

    (* ;; "Flag for newness and fatness")

    (\BOUT FORMATSTREAM (LOGOR (CL:IF NEW
                                   1
                                   0)
                               (CL:IF FAT
                                   2
                                   0)))                      (* ; "The index into the list of fonts")
    (\WOUT FORMATSTREAM CHARLOOKSINDEX])

(\TEDIT.PUT.OBJECT
  [LAMBDA (PIECE CHARSTREAM FORMATSTREAM CURTEXTBYTE#)       (* ; "Edited 18-Apr-2026 14:52 by rmk")
                                                             (* ; "Edited 14-May-2024 12:09 by rmk")
                                                             (* ; "Edited 24-Jan-2024 23:35 by rmk")
                                                             (* ; "Edited 13-Jan-2024 12:20 by rmk")
                                                             (* ; "Edited 19-Dec-2023 10:14 by rmk")
                                                             (* ; "Edited 26-Aug-2023 15:13 by rmk")
                                                             (* ; "Edited 17-Jul-2023 16:39 by rmk")
                                                             (* ; "Edited  6-Aug-2022 10:02 by rmk")
                                                            (* ; "Edited 12-Jun-90 17:49 by mitani")

    (* ;; "Given a piece which describes an object, put the object out there. ")

    (LET ((OBJECT (PCONTENTS PIECE))
          (ORIGFILEPTR (GETFILEPTR FORMATSTREAM))
          BYTELEN)
         (\DWOUT FORMATSTREAM 0)                             (* ; 
                                            "Placeholder for byte-length of the object's description")
         (\WOUT FORMATSTREAM \PieceDescriptorOBJECT)         (* ; 
                                                             "Mark this as setting the piece's looks")
         (\ATMOUT FORMATSTREAM (IMAGEOBJPROP OBJECT 'GETFN)) (* ; 
                                                          "The FN to apply to reconstruct the object")
         (APPLY* (IMAGEOBJPROP OBJECT 'PUTFN)
                OBJECT CHARSTREAM)
         (SETQ BYTELEN (IDIFFERENCE (GETEOFPTR CHARSTREAM)
                              CURTEXTBYTE#))
         (SETFILEPTR FORMATSTREAM ORIGFILEPTR)               (* ; 
                          "Now go back and fill in the length of the text description of the object.")
         (\DWOUT FORMATSTREAM BYTELEN)
         (SETFILEPTR FORMATSTREAM -1)                        (* ; 
                                                             "Move back to the end of the looks file")
         (CL:WHEN (RANDACCESSP CHARSTREAM)                   (* ; "And the end of CHARSTREAM")
             (SETFILEPTR CHARSTREAM -1))
         BYTELEN])
)
(DEFINEQ

(\TEDIT.PUT.PARALOOKS.LIST
  [LAMBDA (LOOKSFILE PARAHASH PARALOOKS)                     (* ; "Edited 19-Dec-2023 10:14 by rmk")
                                                             (* ; "Edited 25-Aug-2023 11:39 by rmk")
                                                             (* ; "Edited 15-Aug-2023 23:25 by rmk")
                                                             (* ; "Edited  1-Sep-87 20:34 by jds")

    (* ;; "Write out the looks in a no-characters pseudo-piece, producing a hashtable of their arbitrary indexes for future reference.")

    (\DWOUT LOOKSFILE 0)
    (\WOUT LOOKSFILE \PieceDescriptorPARALOOKSLIST)
    (\WOUT LOOKSFILE (FLENGTH PARALOOKS))
    (for I from 1 as PL in PARALOOKS do (\TEDIT.PUT.SINGLE.PARALOOKS LOOKSFILE PL)
                                        (PUTHASH PL I PARAHASH])

(\TEDIT.PUT.SINGLE.PARALOOKS
  [LAMBDA (FONTFILE LOOKS)                                   (* ; "Edited 19-Feb-2025 12:11 by rmk")
                                                             (* ; "Edited 21-Oct-2024 00:33 by rmk")
                                                             (* ; "Edited 28-Jul-2024 21:29 by rmk")
                                                             (* ; "Edited 16-Jan-2024 23:00 by rmk")
                                                             (* ; "Edited 19-Dec-2023 10:14 by rmk")
                                                             (* ; "Edited 16-Aug-2023 22:11 by rmk")
                                                             (* ; "Edited  3-Mar-2023 23:25 by rmk")
                                                             (* ; 
                                                        "Edited  2-Jul-93 21:30 by sybalskY:MV:ENVOS")

    (* ;; "Put a description of LOOKS into FILE.")

    (LET ((FILEPOS (GETFILEPTR FONTFILE))
          DEFTAB TABS LEN)
         (\SMALLPOUT FONTFILE 0)                             (* ; 
                                                             "Reserve space to store the look length")
         (\SMALLPOUT FONTFILE (FGETPLOOKS LOOKS 1STLEFTMAR)) (* ; 
                                                    "Left margin for the first line of the paragraph")
         (\SMALLPOUT FONTFILE (FGETPLOOKS LOOKS LEFTMAR))    (* ; 
                                                          "Left margin for the rest of the paragraph")
         (\SMALLPOUT FONTFILE (FGETPLOOKS LOOKS RIGHTMAR))   (* ; "Right margin for the paragraph")
         (\SMALLPOUT FONTFILE (FGETPLOOKS LOOKS LEADBEFORE)) (* ; "Leading before the paragraph")
         (\SMALLPOUT FONTFILE (FGETPLOOKS LOOKS LEADAFTER))  (* ; "Lead after the paragraph")
         (\SMALLPOUT FONTFILE (FGETPLOOKS LOOKS LINELEAD))   (* ; "inter-line leading")
         (SETQ DEFTAB (FGETPLOOKS LOOKS FMTDEFAULTTAB))
         (SETQ TABS (FGETPLOOKS LOOKS FMTTABS))

         (* ;; "Indicate whether there are tab specs  or a default tab setting to save")

         (\BOUT FONTFILE (CL:IF (OR DEFTAB TABS)
                             3
                             2))
         (\BOUT FONTFILE (SELECTQ (FGETPLOOKS LOOKS QUAD)
                             (LEFT 1)
                             (RIGHT 2)
                             ((CENTER CENTERED) 
                                  3)
                             ((JUST JUSTIFIED) 
                                  4)
                             (\TEDIT.THELP)))
         (CL:WHEN (OR TABS DEFTAB)                           (* ; "There are tab specs to save.")
             (\SMALLPOUT FONTFILE (OR DEFTAB 0))
             (CL:WHEN (IGREATERP (LENGTH TABS)
                             255)
                    (\TEDIT.THELP "Paragraph has more than 255 TABs set--can't be saved."))
             (\BOUT FONTFILE (LENGTH TABS))
             [for TAB in TABS do (\SMALLPOUT FONTFILE (fetch (TAB TABX) of TAB)) 
                                                             (* ; "Setting and tab type.")
                                 (\BOUT FONTFILE (SELECTQ (fetch (TAB TABKIND) of TAB)
                                                     (LEFT 0)
                                                     (RIGHT 1)
                                                     (CENTERED 2)
                                                     (DECIMAL 3)
                                                     (DOTTEDLEFT 4)
                                                     (DOTTEDRIGHT 5)
                                                     (DOTTEDCENTERED 
                                                          6)
                                                     (DOTTEDDECIMAL 7)
                                                     (\TEDIT.THELP])
         (\SMALLPOUT FONTFILE (OR (FGETPLOOKS LOOKS FMTSPECIALX)
                                  0))
         (\SMALLPOUT FONTFILE (OR (FGETPLOOKS LOOKS FMTSPECIALY)
                                  0))
         (\ARBOUT FONTFILE (FGETPLOOKS LOOKS FMTUSERINFO))
         (\ATMOUT FONTFILE (FGETPLOOKS LOOKS FMTPARATYPE))
         (\ATMOUT FONTFILE (FGETPLOOKS LOOKS FMTPARASUBTYPE))
         (\ARBOUT FONTFILE (FGETPLOOKS LOOKS FMTSTYLE))
         (\ARBOUT FONTFILE (FGETPLOOKS LOOKS FMTCHARSTYLES))
         (\ARBOUT FONTFILE (FGETPLOOKS LOOKS FMTNEWPAGEBEFORE))
         (\ARBOUT FONTFILE (FGETPLOOKS LOOKS FMTNEWPAGEAFTER))
         (\ARBOUT FONTFILE (FGETPLOOKS LOOKS FMTHEADINGKEEP))
         (\ARBOUT FONTFILE (FGETPLOOKS LOOKS FMTKEEP))
         (\ARBOUT FONTFILE (FGETPLOOKS LOOKS FMTBASETOBASE))
         (\ARBOUT FONTFILE (FGETPLOOKS LOOKS FMTREVISED))
         (\ARBOUT FONTFILE (FGETPLOOKS LOOKS FMTCOLUMN))
         (\ARBOUT FONTFILE (FGETPLOOKS LOOKS FMTCHARSTYLES))

(* ;;; "Now go fill in the length field at the front of the LOOKS.  (ALL looks info should be written out BEFORE this comment.)")

         (SETQ LEN (IDIFFERENCE (GETFILEPTR FONTFILE)
                          FILEPOS))                          (* ; "The length of this set of looks")
         (SETFILEPTR FONTFILE FILEPOS)                       (* ; "Write the length field")
         (\SMALLPOUT FONTFILE LEN)                           (* ; "And back to the end of the file")
         (SETFILEPTR FONTFILE -1])

(\TEDIT.PUT.PARALOOKS
  [LAMBDA (LOOKSFILE PC PARAHASH)                            (* ; "Edited 14-May-2024 13:32 by rmk")
                                                             (* ; "Edited 19-Dec-2023 10:14 by rmk")
                                                             (* ; "Edited 25-Aug-2023 11:41 by rmk")
                                                             (* ; "Edited  3-Mar-2023 23:28 by rmk")
                                                             (* ; "Edited 30-May-91 21:44 by jds")

    (* ;; 
  "Put the identifier of PC's paralooks into LOOKSFILE.  This applies to characters CH1 thru CHLIM-1")

    (\DWOUT LOOKSFILE 0)                                     (* ; 
             "Place holder for number of characters in the piece -- really taken from the charlooks.")
    (\WOUT LOOKSFILE \PieceDescriptorPARA)                   (* ; 
                                                           "Identify this as a paragraph looks piece")
    (\WOUT LOOKSFILE (GETHASH (PPARALOOKS PC)
                            PARAHASH])
)
(DECLARE%: DOEVAL@COMPILE DONTCOPY

(GLOBALVARS TEDIT.INPUT.FORMATS *TEDIT-FILE-READTABLE*)
)
(DEFINEQ

(TEDITFROMLISPSOURCE
  [LAMBDA (SOURCEFILE TSTREAM PROPS USERTEMP START END)      (* ; "Edited  7-Feb-2026 17:02 by rmk")
                                                             (* ; "Edited  7-Apr-2025 23:13 by rmk")
                                                             (* ; "Edited  1-Apr-2025 12:54 by rmk")
                                                             (* ; "Edited 26-Mar-2025 10:02 by rmk")
                                                             (* ; "Edited 18-Feb-2025 23:34 by rmk")
                                                             (* ; "Edited 17-Nov-2024 10:03 by rmk")
                                                             (* ; "Edited 25-Dec-2023 12:28 by rmk")
                                                             (* ; "Edited  5-Dec-2023 23:46 by rmk")
                                                             (* ; "Edited 26-Oct-2023 11:22 by rmk")
                                                             (* ; "Edited 22-Oct-2023 22:55 by rmk")
                                                             (* ; "Edited 22-Sep-2023 09:07 by rmk")

    (* ;; "This is called because OPENTEXTSTREAM recognized that SOURCESTREAM is a LISPSOURCEP foreign-format file.  TSTREAM may have a partially instantiated attached window with region and prompt, etc., but may not yet have the properties of a text or process.")

    (* ;; 
    "TSTREAM and its window are available to provide default looks and region for the empty stream")

    (* ;; "USERTEMP is the reader environment returned by LISPSOURCEFILEP")

    (DECLARE (USEDFREE TEDIT.SOURCE.LINELENGTH))
    (CL:UNLESS TSTREAM
        (SETQ TSTREAM (OPENTEXTSTREAM)))

    (* ;; "Estimate 110 characters per line in the default font?")

    (PUTTEXTPROPS TSTREAM `(PARABREAKCHARS NIL OPENWIDTH ,(TIMES TEDIT.SOURCE.LINELENGTH
                                                                 (CHARWIDTH (CHARCODE SPACE)
                                                                        DEFAULTFONT))
                                  OPENHEIGHT
                                  ,(TIMES TEDIT.SOURCE.NLINES (FONTPROP DEFAULTFONT 'HEIGHT))
                                  BOUNDTABLE
                                  ,(TEDIT.ATOMBOUND.READTABLE (fetch (READER-ENVIRONMENT REREADTABLE)
                                                                 of USERTEMP))
                                  DEFAULTPUTEXTENSION ""))
    (COPY.TEXT.TO.IMAGE SOURCEFILE TSTREAM)
    TSTREAM])

(SHELLSCRIPTP
  [LAMBDA (FILE)                                             (* ; "Edited 15-Dec-2024 11:12 by rmk")

    (* ;; "True if FILE has extension .sh")

    (EQ 'sh (L-CASE (FILENAMEFIELD FILE 'EXTENSION])

(TEDITFROMSHELLSCRIPT
  [LAMBDA (SOURCEFILE TSTREAM PROPS USERTEMP START END)      (* ; "Edited 16-Dec-2024 11:25 by rmk")

    (* ;; "Use DEFAULTFONT for shell scripts")

    (STREAMPROP SOURCEFILE :EXTERNAL-FORMAT :UTF-8)
    [if (TEXTSTREAMP TSTREAM)
        then [TEDIT.LOOKS TSTREAM `(FONT ,(FONTCREATE DEFAULTFONT]
             (COPYCHARS SOURCEFILE TSTREAM START END)
      else (SETQ TSTREAM (OPENTEXTSTREAM SOURCEFILE NIL START END (APPEND 'FONT 'DEFAULTFONT PROPS]
    TSTREAM])
)

(RPAQ? TEDIT.SOURCE.LINELENGTH 110)

(RPAQ? TEDIT.SOURCE.NLINES 30)

(ADDTOVAR TEDIT.INPUT.FORMATS (LISPSOURCEFILEP TEDITFROMLISPSOURCE)
                              (SHELLSCRIPTP TEDITFROMSHELLSCRIPT))

(RPAQ? *TEDIT-FILE-READTABLE* (COPYREADTABLE \ORIGREADTABLE))
(DECLARE%: DONTCOPY
  (FILEMAP (NIL (5384 35643 (TEDIT.GET 5394 . 11804) (TEDIT.FORMATTEDFILEP 11806 . 13122) (
TEDIT.FILEDATE 13124 . 14433) (TEDIT.INCLUDE 14435 . 22464) (TEDIT.RAW.INCLUDE 22466 . 23274) (
TEDIT.PUT 23276 . 31632) (TEDIT.PUT.STREAM 31634 . 35641)) (35644 56951 (\TEDIT.GET.FOREIGN.FILE 35654
 . 39079) (\TEDIT.GET.UNFORMATTED.FILE 39081 . 43420) (\TEDIT.GET.FORMATTED.FILE 43422 . 47065) (
\TEDIT.FORMATTEDSTREAMP 47067 . 50198) (\ARBIN 50200 . 50920) (\ATMIN 50922 . 51459) (\DWIN 51461 . 
51840) (\STRINGIN 51842 . 52550) (\TEDIT.GET.TRAILER 52552 . 55420) (\TEDIT.CACHEFILE 55422 . 56949)) 
(57117 73044 (\TEDIT.GET.PIECES3 57127 . 68176) (\TEDIT.GET.PROPS3 68178 . 71400) (
\TEDIT.MAKE.STRINGPIECE 71402 . 73042)) (73045 85841 (\TEDIT.GET.UNFORMATTED.FILE.MCCS 73055 . 78706) 
(\TEDIT.INTERPRET.MCCS.SHIFTS 78708 . 84304) (\TEDIT.CONVERT.XCCSTOMCCS 84306 . 85238) (
\TEDIT.RUN.TO.STRINGPIECE 85240 . 85839)) (85863 92124 (\TEDIT.GET.UNFORMATTED.FILE.UTF8 85873 . 92122
)) (92147 103665 (\TEDIT.GET.CHARLOOKS.LIST 92157 . 92888) (\TEDIT.GET.SINGLE.CHARLOOKS 92890 . 99962)
 (\TEDIT.GET.CHARLOOKS 99964 . 101520) (\TEDIT.GET.PARALOOKS.INDEX 101522 . 102066) (
\TEDIT.GET.CHARLOOKS.INDEX 102068 . 103663)) (103666 111323 (\TEDIT.GET.PARALOOKS.LIST 103676 . 104298
) (\TEDIT.GET.SINGLE.PARALOOKS 104300 . 111321)) (111324 115266 (\TEDIT.GET.OBJECT 111334 . 115264)) (
115331 150489 (\TEDIT.PUT.PCTB 115341 . 125667) (\TEDIT.PUT.PCTB.PIECEDATA 125669 . 128826) (
\TEDIT.PUT.TRAILER 128828 . 130156) (\TEDIT.PUT.PCTB.MERGEABLE 130158 . 134019) (
\TEDIT.PUT.UTF8.SPLITPIECES 134021 . 138597) (\TEDIT.PUT.MCCS.SPLITPIECES 138599 . 140177) (
\TEDIT.PUT.PCTB.NEXTNEW 140179 . 144920) (\TEDIT.INSERT.NEWPIECES 144922 . 148087) (\TEDIT.PUTRESET 
148089 . 148331) (\ARBOUT 148333 . 149057) (\ATMOUT 149059 . 149664) (\DWOUT 149666 . 149945) (
\STRINGOUT 149947 . 150487)) (150490 163441 (\TEDIT.PUT.CHARLOOKS.LIST 150500 . 152172) (
\TEDIT.PUT.SINGLE.CHARLOOKS 152174 . 158454) (\TEDIT.PUT.CHARLOOKS 158456 . 159903) (
\TEDIT.PUT.CHARLOOKS1 159905 . 160956) (\TEDIT.PUT.OBJECT 160958 . 163439)) (163442 171081 (
\TEDIT.PUT.PARALOOKS.LIST 163452 . 164354) (\TEDIT.PUT.SINGLE.PARALOOKS 164356 . 169940) (
\TEDIT.PUT.PARALOOKS 169942 . 171079)) (171176 174581 (TEDITFROMLISPSOURCE 171186 . 173830) (
SHELLSCRIPTP 173832 . 174061) (TEDITFROMSHELLSCRIPT 174063 . 174579)))))
STOP
