C     This code is distributed under the terms and conditions of the
C     CCP4 licence agreement as `Part i)' software.  See the conditions
C     in the CCP4 manual for a copyright statement.
C
C_BEGIN_CCPLIB
C     These are supposedly-machine-independent low-level routines.
C     They're actually machine-dependent at least insofar as some
C     contain non-standard code, but they do compile with the compilers
C     tried on unix as well as VMS.
C
C     fixme: the bit-twiddling should be in library.c, not here.
C     amalgamate ccppsf and fdir/fext/froot.  also add tests of these
C     routines to testlib.
C
C     $Id: ccplib.f,v 1.88.2.1 2002/10/30 14:00:41 pjx Exp $
C
C      CCFILL    Set specified number of elements of byte array
C      CCPALC    Call subroutine with allocated memory
C      CCPALE    Call subroutine with allocated memory set from environment
C      CCPBYI    Copy array of unsigned (or signed) bytes into integer array
C      CCPBYT    Indicate whether byte handling is available
C      CCPCPI    Copy array of BYTE or INTEGER*2 elements into integer array
C      CCPDAT    Get calendar date
C      CCPDEX    Periodicity reduction of 1024 (for PROLSQ)
C      CCPDPN    more friendly CCPOPN
C      CCPE2I    read integer from logical name value
C      CCPERR    Report error or normal termination and stop
C      CCPEXS    test if file exists
C      CCPFYP    Set up environment & parse command line arguments
C      CCPGI2    Get unsigned integer*2 value from 0 to 65535 from N'th
C                unsigned integer*2 element of array.
C      CCPGTB    Get unsigned byte value from 0 to 255 from N'th byte of
C                array.
C      CCPI2I    Copy an array of INTEGER*2 elements into an integer array
C      CCPIBY    Copy array of integers into array of bytes.
C      CCPII2    Copy array of integers into array of INTEGER*2 elements.
C      CCPLWC    Convert a string to lower case
C      CCPMDE    If byte handling available return nos. of bytes for map
C                modes
C      CCPMVB    Move bytes from one non-character array to another if
C                byte handling is available
C      CCPMVI    Move words from one integer array to another
C      CCPMVR    Move words from one real array to another
C      CCPNUN    Return an unconnected i/o unit number
C      CCPONL    See if program is being run interactively
C      CCPPSF    Parse file name into components
C      CCPRCS    Like CCPVRS but use RCS-format date string
C      CSETNV    Associate logical name with file name
C      CCPPAG    Set paging parameters if available
C      CCPSI2    Set integer value from 0 to 65535 into the N'th
C                unsigned integer*2 element of an array.
C      CCPSTB    Set integer value from 0 to 255 into N'th byte of array.
C      CCPSUM    Sum the elements of an array
C      CCPTIM    Get CPU and Elapsed times
C      CCPTOI    Convert n'th byte or I*2 in a non-character array to an
C                integer
C      CCPUFL    Suppress underflow messages
C      CCPUPC    Convert a string to upper case
C      CCPVRS    Print program version number and date header
C      CCPZBI    Sets an array of bytes to zero
C      CCPZI     Set 'n' words of an integer array to zero using a simple loop
C      CCPZR     Set 'n' words of a real array to zero using a simple loop
C      FDIR      Returns the directory part of a file name
C      FEXTN     Returns the extension of a file name
C      FROOT     Returns the root of a file name
C      LITEND    determine endianness
C      LENSTR    length of string to last non-space
C      LUNSTI    Get logical unit number for input
C      LUNSTO    Get logical unit number for output
C      NBITST    Return the (unsigned) integer value held within a bit
C                field in a word
C      NOCRLF    write line suppressing cr/lf to standard output
C      QPRINT    write debug messages
C      STBITS    Set a bit field within a word to a given (unsigned)
C                integer value
C      CCPLIC    Check that license conditions have been agreed
C_END_CCPLIB
C
C
C
C_BEGIN_CCFILL
      SUBROUTINE CCFILL(ARR1,SCAL,NTIMES)
C     ===================================
C
C      CCFILL    Set NTIMES bytes array ARR1 to value SCAL
C
C Arguments:
C ==========
C
C        ARR1 (O)   BYTE ARRAY (*): WHERE BYTES ARE TO BE COPIED
C        SCAL (I)   BYTE: value to be copied into ARR1
C      NTIMES (I)   INTEGER: NUMBER OF BYTES TO BE COPIED
C_END_CCFILL
C
C     .. Scalar Arguments ..
      INTEGER NTIMES
      BYTE SCAL
C     ..
C     .. Array Arguments ..
      BYTE ARR1(*)
C     ..
C     .. Local Scalars ..
      INTEGER N
C     ..
      DO 10 N = 1,NTIMES
        ARR1(N) = SCAL
   10 CONTINUE
C
      END
C
C
C_BEGIN_CCPALC
      SUBROUTINE CCPALC(ROUTNE, N, TYPE, LENGTH)
C     ==========================================
C
C     Arrange to call subroutine ROUTNE with N array arguments each of
C     length LENGTH (i) and type indicated by TYPE (i): 'i' == integer,
C     'r' == real, 'd' == double precision, 'c' == complex, 'b' ==
C     "byte" (logical*1 or integer*1, unportable and deprecated) .  TYPE
C     elements may have either case.
C     Consider `call ccpalc (fred, 3, types, lens)' with types = (/'i',
C     'r', 'c'/)  and lens = (/1000, 2000, 3000/).  This effectively does
C        call fred (1000, arr1, 2000, arr2, 3000, arr3)
C     with
C        subroutine fred (n1, foo, n2, bar, n3, baz)
C        integer n1, n2, n3, foo (n1)
C        real bar (n2)
C        complex baz (n3)
C        ...
C     Obviously all communication with ROUTNE must be by COMMON (or,
C     possibly, extra ENTRYs).  The allocated memory is freed on return
C     from ROUTNE.  As a concession, it's initially filled with zeroed
C     bytes.

C
C Arguments:
C ==========
C
C      ROUTNE (I)   EXTERNAL: routine to call
C           N (I)   INTEGER: number of arguments to ROUTNE (<=12)
C        TYPE (I)   CHARACTER*1 (*): type of arguments to ROUTNE:
C                      'I': INTEGER; 'R': REAL; 'D': DOUBLE PRECISION;
C                      'C': COMPLEX; 'B': LOGICAL*1 or INTEGER*1
C      LENGTH (I)   INTEGER*(*): number of elements in each (array)
C                       argument of ROUTNE
C_END_CCPALC
C
C     .. Scalar Arguments ..
      INTEGER N
C     ..
C     .. Array Arguments ..
      CHARACTER TYPE (*)
      INTEGER LENGTH (*)
C     ..
      EXTERNAL ROUTNE, CCPAL1, CCPUPC
      INTEGER I, ITYPE (12)
      CHARACTER TTYPE (12)
C     ..
      IF (N.LT.1 .OR. N.GT.12)
     +     CALL CCPERR (1, 'CCPALC: bad number of arguments')
      DO 10 I=1,N
        TTYPE (I) = TYPE (I)
        CALL CCPUPC (TTYPE (I))
        ITYPE (I) = INDEX ('IRDCB', TTYPE (I))
        IF (ITYPE (I) .EQ. 0) CALL CCPERR (1, 'CCPALC: bad TYPE: '//
     +       TYPE (I))
        IF (LENGTH (I).LE.0) CALL CCPERR (1, 'CCPALC: length <=0')
 10   CONTINUE
      CALL CCPAL1 (ROUTNE, N, ITYPE, LENGTH)
      END
C
C
C_BEGIN_CCPALE
      SUBROUTINE CCPALE(ROUTNE, N, TYPE, LENGTH, LENDEF, PRINT)
C     =================================================
C
C     Arrange to call subroutine ROUTNE with N array arguments each of
C     length LENGTH (i) and type indicated by TYPE (i): 'i' == integer,
C     'r' == real, 'd' == double precision, 'c' == complex, 'b' == byte.
C     TYPE elements may have either case.  LENGTH points to an array of
C     environment variable (logical) names from which integer values are
C     read.  The lengths default to values from LENDEF.
C     This is a convenient interface to CCPALC to allow configuring of
C     the memory requirements on the command line where appropriate.
C     This may be useful if the memory requirements can't be determined
C     initially and it's necessary to guess.
C
C Arguments:
C ==========
C
C      ROUTNE (I)   EXTERNAL: routine to call
C           N (I)   INTEGER: number of arguments to ROUTNE (<=12)
C        TYPE (I)   CHARACTER*1 (*): type of arguments to ROUTNE:
C                      'I': INTEGER; 'R': REAL; 'D': DOUBLE PRECISION;
C                      'C': COMPLEX; 'B': LOGICAL*1 or INTEGER*1
C     LENGTH (I)   CHARACTER *(*): logical names representing the number
C                       of elements in each (array) argument of ROUTNE
C     LENDEF (I)   INTEGER (*): default lengths for the argument arrays
C     used if the appropriate LENGTH argument doesn't represent a
C     defined logical
C     PRINT  (I)   LOGICAL: whether or not to print the values of the
C     array lengths
C_END_CCPALE
C
C     .. Scalar Arguments ..
      INTEGER N
      LOGICAL PRINT
C     ..
C     .. Array Arguments ..
      CHARACTER TYPE (*),  LENGTH (*)*(*)
      INTEGER LENDEF (*)
C     ..
      EXTERNAL ROUTNE, CCPE2I, CCPALC, LUNSTO
      INTEGER I, LENG (12), CCPE2I, LUNSTO
C     ..
      DO 10 I=1,N
        LENG (I) = CCPE2I (LENGTH (I), LENDEF (I))
 10   CONTINUE
      IF (PRINT) THEN
        WRITE (LUNSTO(1),
     +     '(/'' Memory allocation (logical name, type, elements):'')')
        WRITE (LUNSTO(1), '(3X, A, 1X, A, 3X, I10)')
     +       (LENGTH (I), TYPE (I), LENG (I), I=1,N)
      ENDIF
      CALL CCPALC (ROUTNE, N, TYPE, LENG)
      END
C
C
C
C SUBROUTINE 'CCPBYI'
C ===================
C
C_BEGIN_CCPBYI
      SUBROUTINE CCPBYI(IA,IBYT,NB)
C     =============================
C
C COPY AN ARRAY OF UNSIGNED (OR SIGNED) BYTES INTO AN INTEGER ARRAY
C
C (MUST BE IMPLEMENTED IF CCPBYT FUNCTION RETURNS .TRUE.)
C [added for LAUE]
C
C Arguments:
C ==========
C
C      IA (O)   INTEGER ARRAY(*): TO RETURN INTEGER VALUES
C    IBYT (I)   BYTE ARRAY(*): DATA (MAY BE AN INTEGER ARRAY FOR EXAMPLE
C               WITH DATA PACKED INTO ADJACENT BYTES
C      NB (I)   INTEGER: IF >0, THE NUMBER OF UNSIGNED BYTES TO BE COPIED
C                        IF <0, -THE NUMBER OF SIGNED BYTES TO BE COPIED
C_END_CCPBYI
C
C SPECIFICATION STATEMENTS
C ------------------------
C
      INTEGER IA(*)
      BYTE IBYT(*)
      BYTE JBYT(4)
      EQUIVALENCE (JA,JBYT(1))
      LOGICAL CALLED, LITEND
      INTEGER IND
      EXTERNAL LITEND
      SAVE CALLED, IND
      DATA CALLED/.FALSE./
C
      IF (.NOT.CALLED) THEN
        CALLED=.TRUE.
        IF (LITEND(1)) THEN
          IND = 1
        ELSE
          IND = 4
        ENDIF
      ENDIF
C
C COPY DATA
C ---------
C
      NE = NB
      IF (NE.GT.0) THEN
         JA=0
         DO 10 I=1,NE
           JBYT(IND)=IBYT(I)
           IA(I)=JA
 10      CONTINUE
      ELSE
         NE = -NE
         DO 20 I=1,NE
         IA(I) = IBYT(I)
20       CONTINUE
      END IF
      END
C
C
C
C_BEGIN_CCPBYT
      LOGICAL FUNCTION CCPBYT(NBW)
C     ============================
C
C---- This function indicates whether byte handling is available or not.
C      if a value of .true. is returned then the subroutines ccpmde and
C      ccpmvb must be fully implemented.
C
C Arguments:
C ==========
C
C         NBW (O)   INTEGER: RETURNS THE NUMBER OF BYTES PER WORD OR A VALUE
C                   OF 1 IF NO BYTE HANDLING IS AVAILABLE.
C
C  RETURNS   CCPBYT  = .TRUE.  BYTE HANDLING AND ASSOCIATED CCPLIB
C                              ROUTINES AVAILABLE.
C                    = .FALSE. NO BYTE HANDLING AVAILABLE.
C_END_CCPBYT
C
C     .. Scalar Arguments ..
      INTEGER NBW
C     ..
      CCPBYT = .TRUE.
      NBW = 4
      END
C
C
C SUBROUTINE 'CCPCPI'
C ===================
C_BEGIN_CCPCPI
      SUBROUTINE CCPCPI(IA,IB,MINEL,MAXEL,ITYP)
C     =========================================
C
C Copy an array of BYTE or INTEGER*2 elements into an integer array
C
C (Must be implemented if ccpbyt function returns .TRUE.)
C [for LAUE]
C
C Arguments:
C ==========
C
C      IA (O)   INTEGER Array(*): to return values
C      IB (I)   INTEGER Array(*): holding data with data packed into adjacent
C                                 BYTE or INTEGER*2 elements
C   MINEL (I)   INTEGER: Minimum element to copy
C   MAXEL (I)   INTEGER: Maximum element to copy
C    ITYP (I)   INTEGER: Type =1 unsigned byte
C                             =2 signed byte
C                             =3 unsigned two byte integer
C                             =4 signed two byte integer
C
C               Note: if MINEL>MAXEL elements will be copied in reverse order
C_END_CCPCPI
C
C====== Specification statements
C
      INTEGER IA(*)
      BYTE IB(*)
      INTEGER*2 J2(2)
      BYTE JBYT(4)
      EQUIVALENCE (JA,J2(1),JBYT(1))
      LOGICAL CALLED, LITEND
      EXTERNAL LITEND
      INTEGER IND1, IND2, INDB
      SAVE CALLED, IND1, IND2, INDB
      DATA CALLED/.FALSE./
C
      IF (.NOT.CALLED) THEN
        IF (LITEND(1)) THEN
          IND1 = 1
          IND2 = 2
          INDB = 1
        ELSE
          IND1 = 3
          IND2 = 4
          INDB = 4
        ENDIF
        CALLED=.TRUE.
      ENDIF
C
C====== Copy data
C
      ISTEP = 1
      IF (MINEL.GT.MAXEL) ISTEP=-1
      IF (ITYP.EQ.1) THEN
         JA=0
         J=0
         DO 10 I=MINEL,MAXEL,ISTEP
            J=J+1
            JBYT(INDB)=IB(I)
            IA(J)=JA
10       CONTINUE
      ELSE IF (ITYP.EQ.2) THEN
         J=0
         DO 20 I=MINEL,MAXEL,ISTEP
            J=J+1
            IA(J)=IB(I)
20       CONTINUE
      ELSE IF (ITYP.EQ.3) THEN
         JA=0
         J=0
         DO 30 I=MINEL,MAXEL,ISTEP
            J=J+1
            JBYT(IND1)=IB(2*I-1)
            JBYT(IND2)=IB(2*I)
            IA(J)=JA
30       CONTINUE
      ELSE IF (ITYP.EQ.4) THEN
         J=0
         DO 40 I=MINEL,MAXEL,ISTEP
            J=J+1
            JBYT(1)=IB(2*I-1)
            JBYT(2)=IB(2*I)
            IA(J)=J2(1)
40       CONTINUE
      END IF
      RETURN
      END
C
C
C
C_BEGIN_CCPDAT
      SUBROUTINE CCPDAT(CALDAT)
C     =========================
C
C---- This subroutine returns the date if available
C
C Arguments:
C ==========
C
C      CALDAT (O)   CHARACTER*8: DATE AS DD/MM/YY
C                   (RETURNED AS A BLANK STRING IF NOT AVAILABLE)
C_END_CCPDAT
C
C SPECIFICATION STATEMENTS
C ------------------------
C
C---- Get date
C
C     .. Scalar Arguments ..
      CHARACTER CALDAT*8
C     ..
C     .. Local Scalars ..
      INTEGER ID,IM,IY
C     ..
C     .. External Subroutines ..
      EXTERNAL UIDATE
C     ..
C
      CALL UIDATE(IM,ID,IY)
      IY = MOD (IY, 100)
      WRITE (CALDAT,FMT=6000) ID,IM,IY
C
C---- Format statements
C
 6000 FORMAT (I2,'/',I2,'/',I2)
C
      IF (CALDAT(7:7) .EQ. ' ') CALDAT(7:7) = '0'
      END
C
C
C
C_BEGIN_CCPDEX
      SUBROUTINE CCPDEX(INDEX,N)
C     =========================
C
C---- This subroutine performs a periodicity reduction for a period
C     of 1024 for the elements of an array. written particularly for
C     'prolsq' to allow for use of the 'and' function on the cray or
C     'moveb' on the m28oh(iap).
C      These are much faster than the mod function used in
C      the standard fortran77 version.
C
C Arguments:
C ==========
C
C        INDEX (I/O) INTEGER ARRAY(*): NUMBERS FOR PERIODICITY REDUCTION
C           N (I)   INTEGER: NO. OF ELEMENTS IN INDEX
C
C EXAMPLE OF FUNCTIONS:
C
C FORTRAN77     INDEX(I)=MOD(INDEX(I),1024)+1
C CRAY-1S       INDEX(I)=AND(INDEX(I),1023)+1
C M280H(IAP)    CALL MOVEB(INDEX(I),1,0,1,22)
C               INDEX(I)=INDEX(I)+1
C_END_CCPDEX
C
C SPECIFICATION STATEMENTS AND CODE
C
C     .. Scalar Arguments ..
      INTEGER N
C     ..
C     .. Array Arguments ..
      INTEGER INDEX(N)
C     ..
C     .. Local Scalars ..
      INTEGER I
C     ..
C     .. Intrinsic Functions ..
      INTRINSIC MOD
C     ..
      DO 10 I = 1,N
        INDEX(I) = MOD(INDEX(I),1024) + 1
   10 CONTINUE
      END
C
C_BEGIN_CCPDPN
      SUBROUTINE CCPDPN(IUN,LOGNAM,STATUS,TYPE,LREC,IFAIL)
C     ====================================================
C
C---- Calls CCPOPN to open a file, but with mnemonic arguments
C
C Arguments:
C ==========
C
C         IUN (I)   INTEGER: UNIT NUMBER
C
C      LOGNAM (I)   CHARACTER*(*): LOGICAL FILE NAME
C
C      STATUS (I)   CHARACTER*(*): FILE STATUS FLAG:
C                                     'UNKNOWN'
C                                     'SCRATCH'
C                                     'OLD'
C                                     'NEW'
C                                     'READONLY'
C                                     'PRINTER'
C
C        TYPE (I)   CHARACTER*(*): FILE TYPE FLAG:
C                                  ='F', 'SEQUENTIAL' 'FORMATTED'
C                                  ='U', 'SEQUENTIAL' 'UNFORMATTED'
C                                  ='DF', 'DIRECT'     'FORMATTED'
C                                  ='DU', 'DIRECT'     'UNFORMATTED'
C     [STATUS and TYPE are case-insensitive]
C
C        LREC (I)   INTEGER: RECORD LENGTH FOR DIRECT ACCESS FILE (NO. OF
C                   CHARACTERS FOR A FORMATTED FILE OR WORDS FOR
C                   AN UNFORMATTED FILE). NOT RELEVANT FOR A SEQUENTIAL
C                   FILE
C
C       IFAIL (I/O) INTEGER: ON INPUT     =0, STOP ON OPEN FAILURE
C                                         =1, CONTINUE AFTER OPEN FAILURE
C                                             (only on file not found)
C                                         =-1, As 0, but silent on success
C                                             (equivalent to negative IUN)
C                            ON OUTPUT    UNCHANGED IF FILE OPEN OK
C                                         =-1, ERROR IN OPENING FILE
C_END_CCPDPN
C
C     .. Scalar Arguments ..
      INTEGER IFAIL,IUN,IUN1,LREC
      CHARACTER LOGNAM* (*),STATUS* (*),TYPE* (*)
C     ..
C     .. Local Scalars ..
      INTEGER ISTAT,ITYPE
      CHARACTER ERRSTR*80
C     ..
C     .. Local Arrays ..
      CHARACTER TYPES(4)*2,STATS(6)*8, STAT*8, TYP*2
C     ..
C     .. External Functions ..
      INTEGER CCPNUN,LENSTR
      EXTERNAL CCPNUN,LENSTR
C     ..
C     .. External Subroutines ..
      EXTERNAL CCPOPN
C     ..
C     .. Data statements ..
      DATA STATS/'UNKNOWN','SCRATCH','OLD','NEW','READONLY','PRINTER'/
      DATA TYPES/'F','U','DF','DU'/
C     ..
C
      IF (IUN .EQ. 0) IUN = CCPNUN()
      STAT = STATUS
      TYP = TYPE
      CALL CCPUPC(STAT)
      CALL CCPUPC(TYP)
      DO 10 ISTAT = 1,6
        IF (STAT.EQ.STATS(ISTAT)) GO TO 20
   10 CONTINUE
      ERRSTR = ' CCPDPN: illegal status : '
      ERRSTR(LENSTR(ERRSTR)+2:) = STATUS
      CALL CCPERR(1,ERRSTR)
C
   20 DO 30 ITYPE = 1,4
        IF (TYP.EQ.TYPES(ITYPE)) GO TO 40
   30 CONTINUE
      ERRSTR = ' CCPDPN: illegal type: '
      ERRSTR(LENSTR(ERRSTR)+2:) = TYPE
      CALL CCPERR(1,ERRSTR)
C
 40   CONTINUE
      IUN1 = IUN
C  If IFAIL lt 0 No open message from CCPOPN
      IF(IFAIL.LT.0 .AND. IUN.GT.0) THEN
        IUN1 = -IUN
        IFAIL = 0
      ENDIF
      CALL CCPOPN(IUN1,LOGNAM,ISTAT,ITYPE,LREC,IFAIL)
C
      END
C
C
C_BEGIN_CCPE2I
      INTEGER FUNCTION CCPE2I (NAME, DEFVAL)
C     ======================================
C
C     Return an integer extracted from environment variable NAME.  If
C     NAME isn't defined, use DEFVAL as the default.  If the value of
C     NAME isn't a representation of an integer, abort.
C
C     Arguments
C     =========
C
C     NAME (I)    CHARACTER *(*)
C     DEFVAL (I)  INTEGER
C_END_CCPE2I
      CHARACTER *(*) NAME
      CHARACTER BUFFER*80, EMESS*100
      INTEGER DEFVAL, LENSTR
      EXTERNAL UGTENV, LENSTR
      CALL UGTENV (NAME, BUFFER)
      IF (BUFFER.EQ.' ') THEN
        CCPE2I = DEFVAL
        RETURN
      ENDIF
      READ (BUFFER, '(BN,I80)', ERR=99) CCPE2I
      RETURN
 99   EMESS = ' Logical name '
      EMESS(LENSTR(EMESS)+2:) = NAME(1:LENSTR(NAME))
      IF(LENSTR(EMESS) .LE. 99) THEN
        EMESS(LENSTR(EMESS)+1:) =' should represent an integer and is: '
        IF(LENSTR(EMESS) .LE. 98)
     .           EMESS(LENSTR(EMESS)+2:) = BUFFER(1:LENSTR(BUFFER))
      ENDIF
      CALL CCPERR (1, EMESS)
      END
C
C
C     ===============================
C_BEGIN_CCPERR
      SUBROUTINE CCPERR(ISTAT,ERRSTR)
C     ===============================
C
C     Arguments:
C     ==========
C
C     ISTAT (I)   INTEGER: error/warning level
C
C         ISTAT=0  Normal termination and stop.
C         ISTAT=1  Fatal error and stop.
C         ISTAT=2  Report severe warning.
C         ISTAT=3  Report information.
C         ISTAT=4  Report from library
C            (ISTAT=-1 also report latest system error
C             and terminate)
C
C     ERRST (I)   CHARACTER*(*): message
C
C_END_CCPERR
C
C     .. Arguments ..
      INTEGER ISTAT
      CHARACTER ERRSTR*(*)
C     ..
C     .. Locals ..
      CHARACTER ERRBUF*256
C     ..
C     .. External Functions ..
      INTEGER LENSTR
      LOGICAL VAXVMS
      EXTERNAL LENSTR, VAXVMS
C     ..
C     .. External Routines ..
      EXTERNAL CCPPNM, QPRINT, CEXIT, GETELAPSED, UGERR
C     ..
C     .. Intrinsic Functions ..
      INTRINSIC ABS
C
C
      IF (ABS(ISTAT).LE.2) THEN
        call ccp4h_summary_beg()
      ENDIF
      IF (ISTAT.LT.0) THEN
        CALL UGERR(0,ERRBUF)
C     (avoid VMS `Message number 00000000')
        IF (ERRBUF .NE. ' ' .AND.
     +       ERRBUF.NE.'Message number 00000000') THEN
          CALL QPRINT(0,
     +         'Last system error message:')
          CALL QPRINT(0,ERRBUF)
        ENDIF
      ENDIF

C     construct appropriate ERRBUF
      IF (ABS(ISTAT).LE.1) THEN
C        report program name
         CALL CCPPNM (ERRBUF)
         IF (LENSTR(ERRBUF) .LT. 251) THEN
            ERRBUF (LENSTR(ERRBUF)+1:) = ': '
            ERRBUF (LENSTR(ERRBUF)+3:) = ERRSTR
         ENDIF
      ELSEIF (ISTAT.EQ.2) THEN
         ERRBUF = ' WARNING: '//ERRSTR
      ELSE
         ERRBUF = ERRSTR
      ENDIF

C     report messages and exit if appropriate
      IF (ABS(ISTAT).LE.1) THEN
        CALL QPRINT(0,ERRBUF)
        IF (VAXVMS()) THEN
          IF (ISTAT.EQ.0) THEN
            CALL GETELAPSED
C           success
            call ccp4h_pre_end()
            call ccp4h_summary_end()
            call ccp4h_html_close()
            CALL CEXIT(1)
          ELSE
C           FOR$_NOTFORSPE, "Not a FORTRAN-specific error"
            call ccp4h_pre_end()
            call ccp4h_summary_end()
            call ccp4h_html_close()
            CALL CEXIT(1605644)
          ENDIF
        ELSE
C         duplicate message to stderr, assumed to be connected to unit 0
          IF (ISTAT.EQ.1) WRITE (0,*) ERRBUF(1:LENSTR(ERRBUF))
          CALL GETELAPSED
          call ccp4h_pre_end()
          call ccp4h_summary_end()
          call ccp4h_html_close()
          CALL CEXIT(ISTAT)
        ENDIF
      ELSEIF (ISTAT.EQ.2) THEN
        CALL QPRINT(0,' ')
        CALL QPRINT(0,' $TEXT:Warning: $$ comment $$ ')
        CALL QPRINT(0,ERRBUF)
        CALL QPRINT(0,' $$')
        call ccp4h_summary_end()
      ELSE
        CALL QPRINT(0,ERRBUF)
      END IF
C
      RETURN
      END
C
C
C
C_BEGIN_CCPEXS
      LOGICAL FUNCTION CCPEXS(NAME)
C     =============================
C
C---- Tests if file assigned to logical name NAME exists
C
C Returns CCPEXS  .true.  if file exists
C                 .false. if file does not exist
C
C Arguments:
C ==========
C
C    NAME (I)   CHARACTER*(*): Logical name
C_END_CCPEXS
C
C     .. Scalar Arguments ..
      CHARACTER NAME* (*)
C     ..
C     .. Local Scalars ..
      CHARACTER*255 NAMFIL
C     ..
      NAMFIL = ' '
      CALL UGTENV(NAME,NAMFIL)
      IF (NAMFIL.EQ.' ') NAMFIL = NAME
C
      INQUIRE (FILE=NAMFIL,EXIST=CCPEXS)
C
      END
C
C
C     =================
C_BEGIN_CCPFYP
      SUBROUTINE CCPFYP
C     =================
C
C---- Used to set up the environment in which the program runs
C     and then parse the command line arguments.
C
C
C---- The logic for locating the "environ.def" and "default.def" files
C     is as follows:
C
C     If the file is defined on the command line then
C       If a directory is specified then
C         Use the filename as is.
C       Else if the SYS$LOGIN or HOME variable is defined then
C         Use "SYS$LOGIN:filename", "$HOME/filename" or "%HOME%\filename".
C       Else
C         Use the filename as is (in current directory).
C     Else
C       If the CINCL variable is defined then
C         Use "CINCL:filename", "$CINCL/filename" or "%CINCL%\filename".
C       Else if the SYS$LOGIN or HOME variable is defined then
C         Use "SYS$LOGIN:filename", "$HOME/filename" or "%HOME%\filename".
C       Else
C         Use the filename as is (in current directory).
C
C Arguments:  NONE
C ==========
C_END_CCPFYP
C
C==============================================================================
C
C     .. Parameters ..
      INTEGER ILIMIT,ISTRLN,IENV
      PARAMETER (ILIMIT=150,ISTRLN=200,IENV=20)
C     ..
C     .. Local Scalars ..
      INTEGER HELP,IARG,ICOUNT,IEND,IERR,II,ILOOP,ISKIP,
     +        ISTART,IUNIT,LOOP,RDENVF,RDLOGF,IHELP,LREC,IFAIL,
     +        IHTML,ISUMM
      LOGICAL DINIT,EINIT,VAX, MVS
      CHARACTER FILNAM* (ISTRLN), LINE* (ISTRLN),
     +     ENVFIL* (ISTRLN), LOGFIL* (ISTRLN), LOGNAM* (ISTRLN),
     +     TEMP* (ISTRLN), BKS*1, VERSION*20, PVERSION*20,
     +     PATCH_LEVEL*20, PROGNAME*20
C     ..
C     .. Local Arrays ..
      CHARACTER ENAME(ILIMIT)* (IENV),ETYPE(ILIMIT)* (5),
     +          EXTN(ILIMIT)* (4)
C     ..
C     .. External Functions ..
      INTEGER LENSTR
C     don't declare iargc
      LOGICAL VAXVMS, WINMVS
      CHARACTER FEXTN* (ISTRLN), FDIR*(ISTRLN), RTNBKS*1
      EXTERNAL LENSTR,VAXVMS,FEXTN,WINMVS, RTNBKS
C     ..
C     .. External Subroutines ..
      EXTERNAL CCPERR,CCPUPC,UGTARG,INITFYP,QPRINT,CSETNV,UGTENV
C     ..
C     .. Intrinsic Functions ..
      INTRINSIC ICHAR,INDEX
C     ..
C     .. Save statement ..
      SAVE
C     .. Data statements ..
      DATA ICOUNT/0/,IHELP/1/,DINIT/.TRUE./,EINIT/.TRUE./,
     +     RDLOGF/0/,RDENVF/0/,ILOOP/1/,IUNIT/31/,
     +     LOGFIL/'default.def'/,ENVFIL/'environ.def'/
C     ..
      VAX = VAXVMS()
      MVS = WINMVS()
      BKS = RTNBKS()
      IHTML = 0
      ISUMM = 0
C
      CALL INITFYP
C
C
      IARG = IARGC()
C
C---- Process command line option switches first
C
   10 CONTINUE
      ISKIP = 1
      IF (ILOOP.LE.IARG) THEN
        CALL UGTARG(ILOOP,LINE)
        CALL CCPUPC(LINE)
C
        IF (LINE(1:1).EQ.'-') THEN
          DO 20 II = 2,LENSTR(LINE)
            IF (LINE(II:II).EQ.'V' .OR. LINE(II:II).EQ.'H') THEN
              HELP = ILOOP + ISKIP
              IF (HELP.GT.IARG) THEN
                IHELP = 1
              ELSE
                CALL UGTARG(HELP,TEMP)
                IHELP = ICHAR(TEMP(1:1)) - ICHAR('0')
                IF (IHELP.LT.0 .OR. IHELP.GT.9) IHELP = 1
              END IF
              ISKIP = ISKIP + 1
            ELSE IF (LINE(II:II).EQ.'N') THEN
              IF (LENSTR(LINE).GE.6) THEN
                IF (LINE(II:II+5).EQ.'NOHTML') THEN
                  IHTML = -1
                  ILOOP = ILOOP + 1
                  GO TO 10
                ELSE IF (LINE(II:II+5).EQ.'NOSUMM') THEN
                  ISUMM = -1
                  ILOOP = ILOOP + 1
                  GO TO 10
                END IF
              END IF
              DINIT = .FALSE.
              EINIT = .FALSE.
            ELSE IF (LINE(II:II).EQ.'D') THEN
              RDLOGF = ILOOP + ISKIP
              IF (RDLOGF.GT.IARG) CALL CCPERR(1,'Use: -d filename')
              CALL UGTARG(RDLOGF,LOGFIL)
              DINIT = .TRUE.
              ISKIP = ISKIP + 1
            ELSE IF (LINE(II:II).EQ.'E') THEN
              RDENVF = ILOOP + ISKIP
              IF (RDENVF.GT.IARG) CALL CCPERR(1,'Use: -e filename')
              CALL UGTARG(RDENVF,ENVFIL)
              EINIT = .TRUE.
              ISKIP = ISKIP + 1
            ELSE IF (LINE(II:II).EQ.'I') THEN
              CALL CCP4_VERSION(VERSION)
              CALL CCP4_PATCH_LEVEL(PATCH_LEVEL)
              CALL CCP4_PROG_VERSION(PVERSION,1)
              CALL CCPPNM (PROGNAME)
              WRITE(TEMP,'(''CCP4 software suite: library version '',
     +                  A)') VERSION(1:LENSTR(VERSION))
              WRITE(6,'(A)') TEMP(1:LENSTR(TEMP))
              WRITE(TEMP,'(''CCP4 software suite: patch level     '',
     +                  A)') PATCH_LEVEL(1:LENSTR(PATCH_LEVEL))
              WRITE(6,'(A)') TEMP(1:LENSTR(TEMP))
              WRITE(TEMP,'(''Program:             '',
     +                  A)') PROGNAME(1:LENSTR(PROGNAME))
              IF (PVERSION.NE.' ') TEMP =
     +          TEMP(1:LENSTR(TEMP))//'; version '//PVERSION
              WRITE(6,'(A)') TEMP(1:LENSTR(TEMP))
              IF (VAXVMS()) THEN
                CALL CEXIT(1)
              ELSE
                CALL CEXIT(0)
              ENDIF
            ELSE
              CALL QPRINT(1,'Ignoring switch '//LINE(II:II))
            END IF
   20     CONTINUE
          ILOOP = ILOOP + ISKIP
          GO TO 10
        END IF
      END IF
C
C---- Initialise html and summary tag variables
C
      CALL CCP4H_INIT_LIB(IHTML,ISUMM)
C
C---- Set up debug level
C
      CALL QPRINT(IHELP,' ')
C
C---- Update argument list
C
      IF (EINIT) THEN
        II = -1
        IF (RDENVF.GT.0) THEN
          IF (FDIR(ENVFIL).NE.' ') II=0
        ELSE
          CALL UGTENV('CINCL',FILNAM)
          IF (FILNAM.NE.' ') THEN
            IF (VAX) THEN
              FILNAM = 'CINCL:'
              II = LENSTR(FILNAM)
            ELSEIF (MVS) THEN
                II = LENSTR(FILNAM)
              IF (FILNAM(II:II).NE.BKS) THEN
                II = II + 1
                IF (II.GT.ISTRLN) CALL CCPERR(1,
     +               'environ path name too long')
                FILNAM(II:II)=BKS
              ENDIF
            ELSE
              II = LENSTR(FILNAM)
              IF (FILNAM(II:II).NE.'/') THEN
                II = II + 1
                IF (II.GT.ISTRLN) CALL CCPERR(1,
     +               'environ path name too long')
                FILNAM(II:II)='/'
              ENDIF
            ENDIF
          ENDIF
        ENDIF
C
        IF (II.LT.0) THEN
          IF (VAX) THEN
            CALL UGTENV('SYS$LOGIN',FILNAM)
          ELSE
            CALL UGTENV('HOME',FILNAM)
          ENDIF
          IF (FILNAM.NE.' ') THEN
            IF (VAX) THEN
              FILNAM = 'SYS$LOGIN:'
              II = LENSTR(FILNAM)
            ELSEIF (MVS) THEN
              II = LENSTR(FILNAM)
              IF (FILNAM(II:II).NE.BKS) THEN
                II = II + 1
                IF (II.GT.ISTRLN) CALL CCPERR(1,
     +               'environ path name too long')
                FILNAM(II:II)=BKS
              ENDIF
            ELSE
              II = LENSTR(FILNAM)
              IF (FILNAM(II:II).NE.'/') THEN
                II = II + 1
                IF (II.GT.ISTRLN) CALL CCPERR(1,
     +               'environ path name too long')
                FILNAM(II:II)='/'
              ENDIF
            ENDIF
          ELSE
            II = 0
          ENDIF
        ENDIF
        FILNAM(II+1:) = ENVFIL
        IF (II.GT.ISTRLN) CALL CCPERR(1, 'environ path name too long')
        CALL QPRINT(2,'Opening file '//FILNAM)
        IFAIL=0
        IXUNIT = IUNIT
        IF (IHELP.LT.2) IXUNIT = -IUNIT
        CALL CCPDPN (IXUNIT,FILNAM,'READONLY','F',LREC,IFAIL)
   30   CONTINUE
        READ (UNIT=IUNIT,FMT=6000,END=40,ERR=80,IOSTAT=IERR) LINE
        TEMP = LINE
C       comments from `!' or `#' to end-of-line
        II = INDEX(LINE,'#')
        IF (II.NE.0) LINE(II:) = ' '
        II = INDEX(LINE,'!')
        IF (II.NE.0) LINE(II:) = ' '
        IF (LINE.NE.' ') THEN
          ICOUNT = ICOUNT + 1
          IF (ICOUNT.GT.ILIMIT) CALL CCPERR(1,
     +         'Too many logical names in environ file: '//TEMP)
          ISTART = INDEX(LINE,'=')
          IF (ISTART.EQ.0)
     +         CALL CCPERR(1,'Missing = in environ file: '//TEMP)
          ENAME(ICOUNT) = LINE(1:ISTART-1)
          EXTN(ICOUNT) = '.'//FEXTN(LINE)
          IF (EXTN(ICOUNT).EQ.'.')
     +         CALL CCPERR(1, 'Bad extension in environ file: '//TEMP)
          IEND = INDEX(LINE,EXTN(ICOUNT))
          ETYPE(ICOUNT) = LINE(ISTART+1:IEND-1)
        END IF
        GO TO 30
   40   CLOSE (UNIT=IUNIT)
      END IF
C
C---- Now get defaults file
C
      IF (DINIT) THEN
        II = -1
        IF (RDLOGF.GT.0) THEN
          IF (FDIR(LOGFIL).NE.' ') II=0
        ELSE
          CALL UGTENV('CINCL',FILNAM)
          IF (FILNAM.NE.' ') THEN
            IF (VAX) THEN
              FILNAM = 'CINCL:'
              II = LENSTR(FILNAM)
            ELSEIF (MVS) THEN
              II = LENSTR(FILNAM)
              IF (FILNAM(II:II).NE.BKS) THEN
                II = II + 1
                IF (II.GT.ISTRLN) CALL CCPERR(1,
     +               'default.def path name too long')
                FILNAM(II:II)=BKS
              ENDIF
            ELSE
              II = LENSTR(FILNAM)
              IF (FILNAM(II:II).NE.'/') THEN
                II = II + 1
                IF (II.GT.ISTRLN) CALL CCPERR(1,
     +               'default.def path name too long')
                FILNAM(II:II)='/'
              ENDIF
            ENDIF
          ENDIF
        ENDIF
C
        IF (II.LT.0) THEN
          IF (VAX) THEN
            CALL UGTENV('SYS$LOGIN',FILNAM)
          ELSE
            CALL UGTENV('HOME',FILNAM)
          ENDIF
          IF (FILNAM.NE.' ') THEN
            IF (VAX) THEN
              FILNAM = 'SYS$LOGIN:'
              II = LENSTR(FILNAM)
            ELSEIF (MVS) THEN
                II = LENSTR(FILNAM)
              IF (FILNAM(II:II).NE.BKS) THEN
                II = II + 1
                IF (II.GT.ISTRLN) CALL CCPERR(1,
     +               'default.def path name too long')
                FILNAM(II:II)=BKS
              ENDIF
            ELSE
              II = LENSTR(FILNAM)
              IF (FILNAM(II:II).NE.'/') THEN
                II = II + 1
                IF (II.GT.ISTRLN) CALL CCPERR(1,
     +               'default.def path name too long')
                FILNAM(II:II)='/'
              ENDIF
            ENDIF
          ELSE
            II = 0
          ENDIF
        ENDIF
        IF (II.GT.ISTRLN) CALL CCPERR(1,
     +       'default.def path name too long')
        FILNAM(II+1:) = LOGFIL
        CALL QPRINT(2,'Opening file '//FILNAM)
        IFAIL=0
        IXUNIT = IUNIT
        IF (IHELP.LT.2) IXUNIT = -IUNIT
        CALL CCPDPN (IXUNIT,FILNAM,'READONLY','F',LREC,IFAIL)
   50   CONTINUE
        READ (UNIT=IUNIT,FMT=6000,END=60,ERR=80,IOSTAT=IERR) LINE
        TEMP = LINE
C       comments from `!' or `#' to end-of-line
        II = INDEX(LINE,'#')
        IF (II.NE.0) LINE(II:) = ' '
        II = INDEX(LINE,'!')
        IF (II.NE.0) LINE(II:) = ' '
        IF (LINE.NE.' ') THEN
          II = INDEX(LINE,'=')
          IF (II.EQ.0)
     +         CALL CCPERR(1,'Missing = in defaults file: '//TEMP)
          LOGNAM = LINE(1:II-1)
          FILNAM = LINE(II+1:)
C
C---- here skip = .true. in csetnv means don't override existing logical
C         name (defined in environment)
C
          CALL CSETNV(LOGNAM,FILNAM,ENAME,ETYPE,EXTN,ICOUNT,.TRUE.)
        END IF
        GO TO 50
   60   CLOSE (UNIT=IUNIT)
      END IF
C
C---- Loop through command line arguments
C
      CALL QPRINT(2,'Processing Command Line Arguments')
      DO 70 LOOP = ILOOP,IARG,2
        CALL UGTARG(LOOP,LOGNAM)
         CALL CCPUPC(LOGNAM)
        CALL UGTARG(LOOP+1,FILNAM)
        IF (FILNAM.EQ.' ') CALL CCPERR(1,
     +       'Use: <logical name> <filename> ...')
C
C---- here skip = .false. in csetnv means override logical name
C       defined in the environment
C
        CALL CSETNV(LOGNAM,FILNAM,ENAME,ETYPE,EXTN,ICOUNT,.FALSE.)
   70 CONTINUE
      CALL QPRINT(2,'End of pre-processing stage')
      RETURN
C
 80   CALL CCPERR (-1,'Error reading environ or default file')
C
 6000 FORMAT (A)
      END
C
C
C SUBROUTINE 'CCPGI2'
C ===================
C
C_BEGIN_CCPGI2
      SUBROUTINE CCPGI2(IVAL,IA,N)
C     ============================
C
C GET AN UNSIGNED INTEGER*2 VALUE FROM 0 TO 65535 FROM THE N'TH unsigned
C INTEGER*2 ELEMENT OF AN INTEGER (OR OTHER) ARRAY.
C
C (MUST BE IMPLEMENTED IF CCPBYT FUNCTION RETURNS .TRUE.)
C [added for LAUE]
C
C Arguments:
C ==========
C
C    IVAL (O)   INTEGER: THE RETURNED VALUE FROM 0 TO 65535
C      IA (I/O) INTEGER*2 ARRAY(*): FROM WHICH THE UNSIGNED INTEGER*2 VALUE
C               IS TO BE RETRIEVED
C       N (I)   INTEGER: POSITION IN 'IA' WHERE THE UNSIGNED INTEGER*2 VALUE
C               IS TO BE RETRIEVED
C_END_CCPGI2
C
C SPECIFICATION STATEMENTS
C ------------------------
C
      INTEGER*2 IA(*)
      INTEGER*2 JBYT(2)
      EQUIVALENCE (JA,JBYT(1))
      LOGICAL CALLED, LITEND
      EXTERNAL LITEND
      INTEGER IND
      SAVE CALLED, IND
      DATA CALLED/.FALSE./
C
      IF (.NOT.CALLED) THEN
        IF (LITEND(1)) THEN
          IND = 1
        ELSE
          IND = 2
        ENDIF
        CALLED=.TRUE.
      ENDIF
C
C GET UNSIGNED INTEGER*2
C ----------------------
C
      JA=0
      JBYT(IND)=IA(N)
      IVAL=JA
      END
C
C
C SUBROUTINE 'CCPGTB'
C ===================
C
C_BEGIN_CCPGTB
      SUBROUTINE CCPGTB(IVAL,IA,N)
C     ============================
C
C GET AN UNSIGNED BYTE VALUE FROM 0 TO 255 FROM THE N'TH BYTE OF AN INTEGER
C (OR OTHER) ARRAY.
C
C (MUST BE IMPLEMENTED IF CCPBYT FUNCTION RETURNS .TRUE.)
C [for LAUE]
C
C Arguments:
C ==========
C
C    IVAL (O)   INTEGER: THE RETURNED VALUE FROM 0 TO 255
C      IA (I/O) BYTE ARRAY(*): FROM WHICH THE BYTE VALUE IS TO BE RETRIEVED
C       N (I)   INTEGER: THE POSITION IN 'IA' WHERE THE BYTE VALUE IS
C               TO BE RETRIEVED
C_END_CCPGTB
C
C SPECIFICATION STATEMENTS
C ------------------------
C
      BYTE IA(*)
      BYTE JBYT(4)
      EQUIVALENCE (JA,JBYT(1))
      LOGICAL CALLED, LITEND
      EXTERNAL LITEND
      INTEGER IND
      SAVE CALLED, IND
      DATA CALLED/.FALSE./
C
      IF (.NOT.CALLED) THEN
        IF (LITEND(1)) THEN
          IND = 1
        ELSE
          IND = 4
        ENDIF
        CALLED=.TRUE.
      ENDIF
C
C GET BYTE
C --------
C
      JA=0
      JBYT(IND)=IA(N)
      IVAL=JA
      END
C
C
C SUBROUTINE 'CCPI2I'
C ===================
C_BEGIN_CCPI2I
      SUBROUTINE CCPI2I(IA,I2,NE,SIGNED,SWAPB)
C     ========================================
C
C Copy an array of INTEGER*2 elements into an integer array
C
C (Must be implemented if ccpbyt function returns .TRUE.)
C [for LAUE]
C
C Arguments:
C ==========
C
C      IA (O)   INTEGER Array(*): to return values
C      I2 (I)   INTEGER*2 Array(*): holding data (may be an INTEGER array for
C               example with data packed into adjacent INTEGER*2 elements
C      NE (I)   INTEGER: The number of elements to be copied
C  SIGNED (I)   LOGICAL: =.TRUE.  Copy as signed integer*2 values
C                        =.FALSE. Copy as unsigned integer*2 values
C   SWAPB (I)   LOGICAL: =.TRUE.  Swap bytes in the integer*2 elements
C                        =.FALSE. Do not swap bytes
C_END_CCPI2I
C
C====== Specification statements
C
      LOGICAL SIGNED, SWAPB
      INTEGER IA(*)
      INTEGER*2 I2(*)
      INTEGER*2 J2(2)
      INTEGER*2 IEIGHT, I255
      PARAMETER (I255=255)
      EQUIVALENCE (JA,J2(1))
      LOGICAL CALLED, LITEND
      EXTERNAL LITEND
      INTEGER IND
      SAVE CALLED, IND
      DATA CALLED/.FALSE./
C
      IF (.NOT.CALLED) THEN
        IF (LITEND(1)) THEN
          IND = 1
        ELSE
          IND = 2
        ENDIF
        CALLED=.TRUE.
      ENDIF
C
C====== Swap bytes if required
C
      IEIGHT = 8
      IF (SWAPB) THEN
         DO 10 I = 1,NE
            I2(I) = IOR(IAND(ISHFT(I2(I),-IEIGHT),I255),
     +              ISHFT(I2(I),IEIGHT))
10       CONTINUE
      END IF
C
C====== Copy data
C
      IF (SIGNED) THEN
         DO 20 I=1,NE
            IA(I) = I2(I)
20       CONTINUE
      ELSE
         JA=0
         DO 30 I=1,NE
         J2(IND)=I2(I)
         IA(I)=JA
30       CONTINUE
      END IF
      END
C
C
C
C SUBROUTINE 'CCPIBY'
C ===================
C
C_BEGIN_CCPIBY
      SUBROUTINE CCPIBY(IBYT,IA,NB)
C     =============================
C
C COPY AN ARRAY OF INTEGERS INTO AN ARRAY OF UNSIGNED (OR UNSIGNED) BYTES.
C NOTE: NO OVERFLOW CHECKING IS DONE.
C
C (MUST BE IMPLEMENTED IF CCPBYT FUNCTION RETURNS .TRUE.)
C [for LAUE]
C
C Arguments:
C ==========
C
C    IBYT (O)   BYTE ARRAY(*) RETURNING DATA (MAY BE AN INTEGER ARRAY
C               FOR EXAMPLE WITH DATA PACKED INTO ADJACENT BYTES)
C      IA (I)   INTEGER ARRAY(*): Input values
C      NB (I)   INTEGER: IF >0, THE NUMBER OF ELEMENTS TO BE COPIED TO
C                        UNSIGNED BYTES
C                        IF <0, -THE NUMBER OF ELEMENTS TO BE COPIED TO
C                        SIGNED BYTES
C_END_CCPIBY
C
C SPECIFICATION STATEMENTS
C ------------------------
C
      INTEGER IA(*)
      BYTE IBYT(*)
      BYTE JBYT(4)
      EQUIVALENCE (JA,JBYT(1))
      LOGICAL CALLED, LITEND
      EXTERNAL LITEND
      INTEGER IND
      SAVE CALLED, IND
      DATA CALLED/.FALSE./
C
      IF (.NOT.CALLED) THEN
        IF (LITEND(1)) THEN
          IND = 1
        ELSE
          IND = 4
        ENDIF
        CALLED=.TRUE.
      ENDIF
C
C COPY DATA
C ---------
C
      NE = NB
      IF (NE.GT.0) THEN
         DO 10 I=1,NE
         JA=IA(I)
         IBYT(I)=JBYT(IND)
10       CONTINUE
      ELSE
         NE = -NE
         DO 20 I=1,NE
         IBYT(I) = IA(I)
20       CONTINUE
      END IF
      END
C
C
C
C SUBROUTINE 'CCPII2'
C ===================
C
C_BEGIN_CCPII2
      SUBROUTINE CCPII2(I2,IA,NE,SIGNED,SWAPB)
C     ========================================
C
C Copy an array of integers into an array of INTEGER*2 elements.
C NOTE: No overflow checking is done.
C
C (Must be implemented if ccpbyt function returns .TRUE.)
C [for LAUE]
C
C Arguments:
C ==========
C
C      I2 (O)   INTEGER*2 ARRAY(*): returning data (may be an INTEGER array for
C               example with data packed into adjacent INTEGER*2 elements)
C      IA (I)   INTEGER ARRAY(*): holding input values
C      NE (I)   INTEGER: The number of elements to be copied
C  SIGNED (I)   LOGICAL: =.TRUE.  Copy as signed integer*2 values
C                        =.FALSE. Copy as unsigned integer*2 values
C   SWAPB (I)   LOGICAL: =.TRUE.  Swap bytes in the integer*2 elements
C                        =.FALSE. Do not swap bytes
C_END_CCPII2
C
C====== Specification statements
C
      LOGICAL SIGNED, SWAPB
      INTEGER IA(*)
      INTEGER*2 I2(*)
      INTEGER*2 J2(2)
      INTEGER*2 IEIGHT, I255
      PARAMETER (I255=255)
      EQUIVALENCE (JA,J2(1))
      LOGICAL CALLED, LITEND
      EXTERNAL LITEND
      INTEGER IND
      SAVE CALLED, IND
      DATA CALLED/.FALSE./
C
      IF (.NOT.CALLED) THEN
        IF (LITEND(1)) THEN
          IND = 1
        ELSE
          IND = 2
        ENDIF
        CALLED=.TRUE.
      ENDIF
C
C====== Copy data
C
      IEIGHT = 8
      IF (SIGNED) THEN
         DO 10 I=1,NE
            I2(I) = IA(I)
10       CONTINUE
      ELSE
         DO 20 I=1,NE
            JA=IA(I)
            I2(I)=J2(IND)
20       CONTINUE
      ENDIF
C
C====== Swap bytes if required
C
      IF (SWAPB) THEN
         DO 30 I = 1,NE
            I2(I) = IOR(IAND(ISHFT(I2(I),-IEIGHT),I255),
     +              ISHFT(I2(I),IEIGHT))
30       CONTINUE
      END IF
      END
C
C
C
C_BEGIN_CCPLWC
      SUBROUTINE CCPLWC(STRING)
C     =========================
C
C---- convert a text string to lower case in situ
C
C Arguments:
C ==========
C
C  STRING (I/O) CHARACTER*(*): string to convert
C_END_CCPLWC
C
C     .. Scalar Arguments ..
      CHARACTER STRING* (*)
C     ..
C     .. Local Scalars ..
      INTEGER K,L,LL
      CHARACTER LC*26,UC*26
C     ..
C     .. Intrinsic Functions ..
      INTRINSIC INDEX,LEN
C     ..
C     .. Data statements ..
      DATA LC/'abcdefghijklmnopqrstuvwxyz'/
      DATA UC/'ABCDEFGHIJKLMNOPQRSTUVWXYZ'/
C     ..
C
      LL = LEN(STRING)
      IF (LL.GT.0) THEN
        DO 10 L = 1,LL
          K = INDEX(UC,STRING(L:L))
          IF (K.NE.0) STRING(L:L) = LC(K:K)
   10   CONTINUE
      END IF
C
      END
C
C
C
C_BEGIN_CCPMDE
      SUBROUTINE CCPMDE(MODE,NBYT)
C     ============================
C
C---- If byte handling is available (see ccpbyt) then this subroutine
C     returns the number of bytes per data item for the different modes
C     used, in particular, in the map handling subroutines.
C
C---- If byte handling is not available, then the number of words per
C     item is returned with zeros for the undefined items
C
C Arguments:
C ==========
C
C        MODE (I)   INTEGER:
C                   MODE = 0,   BYTES
C                        = 1,   SHORT (2 BYTE) INTEGERS
C                        = 2,   REAL/INTEGER (SINGLE WORD)
C                        = 3,   SHORT COMPLEX (2 * 2 BYTE INTEGERS)
C                        = 4,   COMPLEX (TWO WORDS)
C
C        NBYT (O)   INTEGER:
C                        > 0,   THE NUMBER OF BYTES FOR THE ITEM  IF
C                               CCPBYT RETURNS .TRUE. OR THE  NUMBER
C                               OF WORDS IF CCPBYT RETURNS .FALSE.
C                        = 0,   NO VALUE AVAILABLE FOR THIS MODE
C                        = -1,  INVALID MODE
C
C  TYPICAL VALUES:  1  2  4  4  8    IF BYTE HANDLING AVAILABLE WITH 4
C                                    BYTES/WORD
C                   0  0  1  0  2    IF BYTE HANDLING UNAVAILABLE
C_END_CCPMDE
C
C SPECIFICATION STATEMENTS
C ------------------------
C
C     .. Scalar Arguments ..
      INTEGER MODE,NBYT
C     ..
C     .. Local Arrays ..
      INTEGER MODES(0:4)
C     ..
C     .. Data statements ..
      DATA MODES/1,2,4,4,8/
C     ..
C
C---- Get number of bytes or words
C
      NBYT = -1
      IF (MODE.GE.0 .AND. MODE.LE.4) NBYT = MODES(MODE)
      END
C
C
C
C_BEGIN_CCPMVB
      SUBROUTINE CCPMVB(ARR1,I1,ARR2,I2,NTOMOV)
C     =========================================
C
C---- This subroutine moves bytes from one non-character array
C     to another. I must be implemented if ccpbyt returns .true.
C     but will otherwise be a dummy routine.
C
C Arguments:
C ==========
C
C        ARR1 (I/O) BYTE ARRAY(*): TO WHICH BYTES ARE TO BE COPIED
C          I1 (I)   INTEGER: THE START BYTE NUMBER IN ARR1 WHERE THE BYTES ARE
C                   TO BE COPIED
C        ARR2 (I)   BYTE ARRAY(*): FROM WHICH BYTES ARE TO BE COPIED
C          I2 (I)   THE START BYTE NUMBER IN ARR2 FROM WHICH THE BYTES
C                   ARE TO BE COPIED
C      NTOMOV (I)   INTEGER: THE NUMBER OF BYTES TO BE COPIED
C_END_CCPMVB
C
C     .. Scalar Arguments ..
      INTEGER I1,I2,NTOMOV
C     ..
C     .. Array Arguments ..
      BYTE ARR1(*),ARR2(*)
C     ..
C     .. Local Scalars ..
      INTEGER I,J,N
C     ..
      I = I1 - 1
      J = I2 - 1
      DO 10 N = 1,NTOMOV
        I = I + 1
        J = J + 1
        ARR1(I) = ARR2(J)
   10 CONTINUE
C
      END
C
C
C_BEGIN_CCPMVI
      SUBROUTINE CCPMVI (IARR1,IARR2,NUM)
C     =================================
C
C  This routine assigns the first NUM words of IARR2 to IARR1
C
C Arguments:
C ==========
C
C    IARR1 (O)   INTEGER ARRAY(*)
C    IARR2 (O)   INTEGER ARRAY(*)
C     NUM (I)   Number of words to copy
C_END_CCPMVI
C
C  Arguments
      INTEGER NUM
      REAL IARR1(*),IARR2(*)
C
      INTEGER J
C
      DO 10 J=1,NUM
   10 IARR1(J)=IARR2(J)
      END
C
C
C_BEGIN_CCPMVR
      SUBROUTINE CCPMVR (ARR1,ARR2,NUM)
C     =================================
C
C  This routine assigns the first NUM elements of ARR2 to ARR1
C
C Arguments:
C ==========
C
C    ARR1 (O)   REAL ARRAY(*)
C    ARR2 (O)   REAL ARRAY(*)
C     NUM (I)   Number of words to copy
C_END_CCPMVR
C
C  Arguments
      INTEGER NUM
      REAL ARR1(*),ARR2(*)
C
      INTEGER J
C
      DO 10 J=1,NUM
   10 ARR1(J)=ARR2(J)
      END
C
C_BEGIN_CCPNUN
      INTEGER FUNCTION CCPNUN ()
C     ==========================
C
C     Return (the next) unused (not connected) i/o unit number.
C     Use this to select an arbitrary unit for i/o to avoid clashes with
C     other code.  (The value returned will be the same until the unit in
C     question is opened or a lower-numbered one is closed.)
C
C_END_CCPNUN
      LOGICAL OD, EX
      EXTERNAL CCPERR
      INTEGER IOS
C     The `standard' unit 5 and 6 may or may not be reported as open,
C     normally depending on whether an appropriate read or write has
C     happened, so we'll start at 7.  Lower-numbered ones might be used
C     for other things such as standard error.  99 seems a reasonable
C     place to stop.
      DO 10 CCPNUN=7,99
        INQUIRE (UNIT=CCPNUN, OPENED=OD, IOSTAT=IOS, EXIST=EX)
        IF (EX .AND. (.NOT.OD) .AND. IOS.EQ.0) RETURN
 10   CONTINUE
      CALL CCPERR (1, 'CCPNUN: Can''t find an unused unit')
      END
C
C
C
C_BEGIN_CCPONL
      LOGICAL FUNCTION CCPONL(IDUM)
C     =============================
C
C---- This function determines whether a program is being run on-line
C     if this information is available
C
C Arguments:
C ==========
C
C        IDUM (D)   DUMMY
C
C RETURNS .TRUE.  IF PROGRAM IS BEING RUN ON-LINE
C RETURNS .FALSE. IF BATCH MODE OR STATUS UNKNOWN
C_END_CCPONL
C
C     .. Scalar Arguments ..
      INTEGER IDUM
C     ..
C     .. Local Scalars ..
      INTEGER IYES,ITERM
C     ..
C     .. External Functions ..
      EXTERNAL UISATT
C     ..
C
C      test for fortran unit=6 o/p
C
      IYES = 0
      ITERM = 6
      CALL UISATT(ITERM,IYES)
      CCPONL = IYES.EQ.1
      END
C
C
C
C SUBROUTINE 'CCPPSF'
C ===================
C
C_BEGIN_CCPPSF
      SUBROUTINE CCPPSF(FILNAM,PATH,NAME,TYPE,VERS)
C     =============================================
C
C PARSE FILE NAME INTO COMPONENTS
C
C NOTE: THE ROUTINE  CONTAINS MACHINE DEPENDENT CODE
C
C
C Arguments:
C ==========
C
C      FILNAM (I)   CHARACTER*(*): FILE NAME STRING (NO EMBEDDED BLANKS ASSUMED)
C
C        PATH (O)   CHARACTER*(*): STRING RETURNING PATH OR, FOR VAX VMS,
C                   THE PART OF THE FILE SPECIFICATION UP TO THE
C                   END OF THE DIRECTORY SPECIFICATION (BLANK IF NONE)
C                   (INCLUDES TERMINATING ] or : or /)
C
C        NAME (O)   CHARACTER*(*): STRING RETURNING NAME.  (BLANK IF NONE)
C
C        TYPE (O)   CHARACTER*(*): STRING RETURNING FILE TYPE/EXTENSION
C                   (BLANK IF NONE)
C
C        VERS (O)   CHARACTER*(*): STRING RETURNING THE VERSION.
C                   (BLANK IF NONE)
C
C AFTER REMOVAL OF THE PATH PART OF THE STRING, IF PRESENT, THE VERSION ON
C A VAX IS TAKEN AS ANY TEXT FOLLOWING A SEMICOLON IN THE STRING OR, IF NO
C SEMICOLON IS PRESENT, ANY TEXT FOLLOWING THE LAST DOT IN THE STRING
C PROVIDED THAT AT LEAST TWO DOTS ARE PRESENT. ON A UNIX SYSTEM THE VERSION
C WILL ALWAYS BE RETURNED AS A BLANK.
C
C AFTER THE REMOVAL OF THE PATH AND VERSION PARTS OF THE STRING THEN, IF
C THERE IS AT LEAST ONE DOT, THE NAME IS THE STRING UP TO THE LAST DOT
C REMAINING AND THE TYPE IS THE PART OF THE STRING AFTER THE DOT. IF
C NO DOT IS PRESENT THEN THE REMAINING STRING IS THE NAME AND THE TYPE
C IS BLANK.
C_END_CCPPSF
C
C SPECIFICATION STATEMENTS
C ------------------------
C
      CHARACTER*(*) FILNAM,PATH,NAME,TYPE,VERS
      EXTERNAL VAXVMS, WINMVS, RTNBKS
      LOGICAL VAXVMS, WINMVS, VMS, MVS
      CHARACTER RTNBKS*1, BKS*1
C
C INITIALISATIONS
C ---------------
C
      PATH=' '
      NAME=' '
      TYPE=' '
      VERS=' '
      LMAX=LENSTR(FILNAM)
      IF (LMAX.EQ.0) RETURN
      LMIN=0
      VMS = VAXVMS()
      MVS = WINMVS()
      BKS = RTNBKS()
10    LMIN=LMIN+1
      IF (FILNAM(LMIN:LMIN).EQ.' ') GO TO 10
C
C GET PATH
C --------
C
      IF (VMS) THEN
        DO 20 L=LMAX,LMIN,-1
          IF (FILNAM(L:L).EQ.':'.OR.FILNAM(L:L).EQ.']') GO TO 30
 20     CONTINUE
      ELSEIF (MVS) THEN
        DO 21 L=LMAX,LMIN,-1
          IF (FILNAM(L:L).EQ.BKS .OR. FILNAM(L:L).EQ.'/')GO TO 30
 21     CONTINUE
      ELSE
        DO 22 L=LMAX,LMIN,-1
          IF (FILNAM(L:L).EQ.'/')GO TO 30
 22     CONTINUE
      ENDIF
      GO TO 40
30    PATH=FILNAM(LMIN:L)
      LMIN=L+1
      IF (LMIN.GT.LMAX) RETURN
C
C GET VERSION IF PRESENT
C ----------------------
C
 40   CONTINUE
      IF (VMS) THEN
        LSC=INDEX(FILNAM(LMIN:LMAX),';')
        IF (LSC.GT.0) THEN
          LSC=LSC+LMIN-1
          IF (LSC.LT.LMAX) VERS=FILNAM(LSC+1:LMAX)
          LMAX=LSC-1
        ELSE
          LDOT=0
          NDOT=0
          DO 50 L=LMAX,LMIN,-1
            IF (FILNAM(L:L).EQ.'.') THEN
              NDOT=NDOT+1
              IF (LDOT.EQ.0) LDOT=L
            ENDIF
 50       CONTINUE
          IF (NDOT.GT.1) THEN
            IF (LDOT.LT.LMAX) VERS=FILNAM(LDOT+1:LMAX)
            LMAX=LDOT-1
          ENDIF
        ENDIF
      ENDIF
C
C GET NAME AND TYPE
C -----------------
C
      IF (LMAX.LT.LMIN) RETURN
      LDOT=0
      DO 60 L=LMAX,LMIN,-1
      IF (FILNAM(L:L).EQ.'.') THEN
         LDOT=L
         GO TO 70
      ENDIF
60    CONTINUE
70    IF (LDOT.EQ.0) THEN
         NAME=FILNAM(LMIN:LMAX)
         RETURN
      ELSE
         IF (LDOT.GT.LMIN) NAME=FILNAM(LMIN:LDOT-1)
         IF (LDOT.LT.LMAX) TYPE=FILNAM(LDOT+1:LMAX)
      ENDIF
      END
C
C
C
C_BEGIN_CCPRCS
      SUBROUTINE CCPRCS(ILP,PROG,RCSDAT)
C     ==================================
C
C     Interface to CCPVRS using RCS-format date e.g.,
C     '1992/09/14 18:47:13' or its form expanded with RCS
C     option `-kv' as for a CVS export, in which case it will have the
C     `1992/09/14 18:47:13' stripped.
C
C Arguments:
C ==========
C
C         ILP (I)   INTEGER: UNIT NUMBER FOR PRINTER OUTPUT
C        PROG (I)   CHARACTER*(*): VARIABLE HOLDING PROGRAM NAME (MAX
C                   OF 10 CHARACTERS)
C      RCSDAT (I)   CHARACTER*(*): VARIABLE HOLDING DATE IN RCS FORMAT
C_END_CCPRCS
C
      CHARACTER*(*) RCSDAT, PROG
      CHARACTER*8 DATE
      INTEGER ILP
      EXTERNAL CCPVRS
C
      IF (RCSDAT(:7) .EQ. '$Date: ') THEN
C       raw form (not exported)
        DATE = '  /  /'
        DATE(1:2) = RCSDAT(16:17)
        DATE(4:5) = RCSDAT(13:14)
        DATE(7:8) = RCSDAT(10:11)
      ELSE IF (LEN(RCSDAT).GE.10 .AND. (RCSDAT(:2).EQ.'19'
     $                             .OR. RCSDAT(:2).EQ.'20')) THEN
C       after export
        DATE = '  /  /'
        DATE(1:2) = RCSDAT(9:10)
        DATE(4:5) = RCSDAT(6:7)
        DATE(7:8) = RCSDAT(3:4)
      ELSE
C       fallback
        DATE = ' '
      ENDIF
      CALL CCPVRS(ILP,PROG,DATE)
      END
C
C
C
C_BEGIN_CSETNV
      SUBROUTINE CSETNV(LNAME,FILNAM,ENAME,ETYPE,EXTN,ICOUNT,LSKIP)
C     =============================================================
C
C     Associate `logical name' LNAME with value FILNAM.  It is passed
C     arrays of (name, type, extension) for ICOUNT number of name lines
C     read from environ.def.  Doesn't re-define existing name if LSKIP is true.
C
C Arguments:
C ==========
C
C   LNAME (I)   CHARACTER*(*): Logical name (environment variable).
C
C  FILNAM (I/O) CHARACTER*(*): File name, if extension is omitted it is appended
C
C   ENAME (I/O) CHARACTER(150)*20 ARRAY: containing list of environment
C               variables; if LNAME is not in list it is appended
C               (also to ETYPE & EXTN arrays).
C
C   ETYPE (I,O) CHARACTER(150)*5 ARRAY: containing list of in/out types.
C
C    EXTN (I/O) CHARACTER(150)*4 ARRAY: containing list of extensions.
C
C  ICOUNT (I/O) INTEGER: Length of arrays ENAME, ETYPE & EXTN.
C
C   LSKIP (I)   LOGICAL: If .TRUE. existing name not re-defined.
C
C_END_CSETNV
C
C     .. Parameters ..
      INTEGER ILIMIT,ISTRLN,IENV
      PARAMETER (ILIMIT=150,ISTRLN=200,IENV=20)
C     ..
C     .. Scalar Arguments ..
      INTEGER ICOUNT
      CHARACTER LNAME* (*),FILNAM* (*)
      LOGICAL LSKIP
C     ..
C     .. Array Arguments ..
      CHARACTER ENAME(ILIMIT)* (IENV),ETYPE(ILIMIT)* (5),
     +          EXTN(ILIMIT)* (4)
C     ..
C     .. Local Scalars ..
      INTEGER I,II,ISTAT,JJ,PROCID
      LOGICAL VAX,MVS,EXIST
      CHARACTER ERRSTR* (ISTRLN),LIBFIL* (ISTRLN),PROGNM* (ISTRLN),
     +          TMPNAM* (ISTRLN),LINE* (ISTRLN),SCRFIL* (ISTRLN),
     +          BKS*(1)
C     ..
C     .. External Functions ..
      INTEGER GETPID,LENSTR
      LOGICAL VAXVMS, WINMVS
      CHARACTER FDIR* (ISTRLN),FEXTN* (ISTRLN),FROOT* (ISTRLN), RTNBKS
      EXTERNAL GETPID,LENSTR,VAXVMS,FDIR,FEXTN,FROOT
C     ..
C     .. External Subroutines ..
      EXTERNAL CCPERR,UGTARG,QPRINT,UGTENV,USTENV
C     ..
C     .. Intrinsic Functions ..
      INTRINSIC INDEX
C     ..
      SAVE
      DATA PROGNM/' '/
C
C---- Check Logical Name does not already exist (unless processing
C     command line, in which case LSKIP will be true to override
C     environment)
C
      CALL UGTENV(LNAME,TMPNAM)
      IF (TMPNAM.NE.' ' .AND. LSKIP ) RETURN
      VAX = VAXVMS()
      MVS = WINMVS()
      BKS = RTNBKS()
C
C---- Get program name (argv[0]), but check if we have it already
C
      IF (PROGNM.EQ.' ') THEN
        CALL UGTARG(0,TMPNAM)
        PROGNM = FROOT(TMPNAM)
      ENDIF
C
C---- look through list for a match (possibly abbreviated) [is this
C     abbreviation possibility documented?]
C
      DO 10 JJ = 1,ICOUNT
        IF (ENAME(JJ).EQ.LNAME(1:LENSTR(ENAME(JJ)))) GO TO 20
   10 CONTINUE
C
C---- Unknown logical name add it to the list.
C
      TMPNAM = 'Non standard logical name '
      TMPNAM(27:) = LNAME
      CALL QPRINT(2,TMPNAM)
      ICOUNT = ICOUNT + 1
      IF (ICOUNT.GT.ILIMIT)
     +     CALL CCPERR(1,'Too many logical names')
      ENAME(ICOUNT) = LNAME
      ETYPE(ICOUNT) = 'undef'
      EXTN(ICOUNT) = FEXTN(FILNAM)
      JJ = ICOUNT
C
C---- Known logical name processing
C
   20 IF (FEXTN(FILNAM).EQ.' ') THEN
C
C---- Add extension
C
        IF (FILNAM.EQ.'/dev/null' .OR. FILNAM.EQ.'NL:') THEN
C          but not if FILNAM is /dev/null or NL:
          GOTO 333
        ELSE
          II = LENSTR(FILNAM) + 1
          FILNAM(II:) = EXTN(JJ)
        ENDIF
      ENDIF
      IF (FDIR(FILNAM).EQ.' ') THEN
CCC       this didn't agree with documentation:
CCC        IF (EXTN(JJ).EQ.'.lib' .OR. EXTN(JJ).EQ.'.prt' .OR.
CCC     +      EXTN(JJ).EQ.'.bes' .OR. EXTN(JJ).EQ.'.dic') THEN
        TMPNAM = FEXTN(FILNAM)
        IF (VAX) CALL CCPLWC(TMPNAM)
        IF (TMPNAM.EQ.'lib' .OR. TMPNAM.EQ.'prt' .OR.
     +      TMPNAM.EQ.'bes' .OR. TMPNAM.EQ.'dic') THEN
C         look for files without path but with standard extension in the
C         standard place
          CALL UGTENV('CLIBD',LIBFIL)
C         add the standard directory qualifier
          IF (VAX) THEN
C           fixme: should we insist that VMS defines CLIBD as well as un*x?
            IF (LIBFIL.NE.' ') THEN
              TMPNAM = 'CLIBD:'
              TMPNAM(7:) = FILNAM
            ELSE
              TMPNAM = FILNAM
            ENDIF
          ELSEIF (MVS) THEN
            IF (LIBFIL.EQ.' ') CALL CCPERR(1,'CLIBD not defined')
            II = LENSTR(LIBFIL)
            TMPNAM = LIBFIL(:II)//BKS
            II = II + 2
            TMPNAM(II:) = FILNAM
          ELSE
            IF (LIBFIL.EQ.' ') CALL CCPERR(1,'CLIBD not defined')
            II = LENSTR(LIBFIL)
            TMPNAM = LIBFIL(:II)//'/'
            II = II + 2
            TMPNAM(II:) = FILNAM
          END IF
          FILNAM = TMPNAM
        ELSE IF (EXTN(JJ).EQ.'.scr' .OR. FEXTN(FILNAM).EQ.'scr') THEN
C         scratch files in a special place
C         actually create <ccp4_scr>/<prognm>_.<pid>
          CALL UGTENV('CCP4_SCR',TMPNAM)
          IF (VAX) THEN
            IF (TMPNAM.EQ.' ') THEN
              TMPNAM = PROGNM
            ELSE
              TMPNAM = 'CCP4_SCR:' // PROGNM
            ENDIF
          ELSEIF (MVS) THEN
            IF (TMPNAM.EQ.' ') CALL CCPERR(1,'CCP4_SCR not defined')
            II = LENSTR(TMPNAM) + 1
            TMPNAM(II:) = BKS//PROGNM
          ELSE
            IF (TMPNAM.EQ.' ') CALL CCPERR(1,'CCP4_SCR not defined')
            II = LENSTR(TMPNAM) + 1
            TMPNAM(II:) = '/'//PROGNM
          END IF
          II = LENSTR(TMPNAM) + 1
          TMPNAM(II:II) = '_'
          II = II + 1
          I = INDEX(FILNAM,'.')
          TMPNAM(II:) = FILNAM(:I)
          IF (VAX) THEN
            WRITE (SCRFIL,'(Z8.8)') GETPID()
          ELSE
            PROCID = MOD(GETPID(),100000)
C     Windows98 does not return a pid so make a number up
            IF (PROCID.GT.99999 .OR. PROCID.LE.0) THEN
              CALL USTIME(PROCID)
              PROCID = MOD(PROCID,100000)
            ENDIF
            WRITE (SCRFIL,'(I5.5)') PROCID
          ENDIF
          FILNAM = TMPNAM(1:LENSTR(TMPNAM))//SCRFIL
        END IF
      END IF
333   CONTINUE
C
C---- Now test input files do exist (but not for defaults, to avoid
C     checking 40 or 50 files listed in default.def which the setup
C     should guarantee)
C
      IF (ETYPE(JJ).EQ.'in' .AND. .NOT.LSKIP) THEN
        INQUIRE(FILE=FILNAM,EXIST=EXIST)
        IF (.NOT.EXIST) THEN
          ERRSTR = 'Cannot find file '
          ERRSTR(18:) = FILNAM
          CALL CCPERR (-1,ERRSTR)
        END IF
      END IF
      II = LENSTR(LNAME) + 1
      LINE = LNAME
      LINE(II:II) = '='
      II = II + 1
      LINE(II:) = FILNAM
C     =======================================
      CALL USTENV(LINE(1:LENSTR(LINE)),ISTAT)
C     =======================================
      IF (ISTAT.NE.0) THEN
        IF (VAX) THEN
          ERRSTR = 'Cannot create environment variable '
        ELSE
          ERRSTR = 'Cannot create logical name '
        ENDIF
        ERRSTR(36:) = LNAME
        CALL CCPERR (-1,ERRSTR)
      END IF
      CALL QPRINT(3,LINE)
      END
C
C
C
C_BEGIN_CCPPAG
      SUBROUTINE CCPPAG(IUN,NCOL,NLIN)
C     ================================
C
C---- This subroutine returns the number of columns and lines
C     for a printer output page on a given fortran unit number
C     if the information is available
C
C Arguments:
C ==========
C
C         IUN (I)   INTEGER: FORTRAN UNIT NUMBER
C        NCOL (O)   INTEGER: NUMBER OF COLUMNS IN THE PAGE
C        NLIN (O)   INTEGER: NUMBER OF LINES IN THE PAGE
C
C Return 80,132 unless a terminal whence 0,80
C_END_CCPPAG
C
C     .. Scalar Arguments ..
      INTEGER IUN,NCOL,NLIN
C     ..
C     .. Local Scalars ..
      INTEGER IYES
C     ..
C     .. External Subroutines ..
      EXTERNAL UISATT
C     ..
      CALL UISATT(IUN,IYES)
      IF (IYES.EQ.1) THEN
        NLIN = 0
        NCOL = 80
      ELSE
        NLIN = 80
        NCOL = 132
      END IF
      END
C
C
C
C SUBROUTINE 'CCPSI2'
C ===================
C
C_BEGIN_CCPSI2
      SUBROUTINE CCPSI2(IVAL,IA,N)
C     ============================
C
C SET AN INTEGER VALUE FROM 0 TO 65535 INTO THE N'TH UNSIGNED INTEGER*2 ELEMENT
C OF AN INTEGER (OR OTHER) ARRAY.
C NOTE: NO OVERFLOW CHECKING IS DONE.
C
C (MUST BE IMPLEMENTED IF CCPBYT FUNCTION RETURNS .TRUE.)
C [for LAUE]
C
C Arguments:
C ==========
C
C    IVAL (I)   INTEGER: VALUE FROM 0 TO 65535
C
C      IA (I/O) INTEGER*2 ARRAY: WHERE THE UNSIGNED INTEGER*2 VALUE IS TO BE
C               INSERTED
C
C       N (I)   INTEGER: THE POSITION IN 'IA' WHERE THE UNSIGNED INTEGER*2
C               VALUE IS TO BE INSERTED
C_END_CCPSI2
C
C SPECIFICATION STATEMENTS
C ------------------------
C
      INTEGER*2 IA(*)
      INTEGER*2 JBYT(2)
      EQUIVALENCE (JA,JBYT(1))
      LOGICAL CALLED, LITEND
      EXTERNAL LITEND
      INTEGER IND
      SAVE CALLED, IND
      DATA CALLED/.FALSE./
C
      IF (.NOT.CALLED) THEN
        IF (LITEND(1)) THEN
          IND = 1
        ELSE
          IND = 2
        ENDIF
        CALLED=.TRUE.
      ENDIF
C
C SET UNSIGNED INTEGER*2
C ----------------------
C
      JA=IVAL
      IA(N)=JBYT(IND)
      END
C
C
C
C SUBROUTINE 'CCPSTB'
C ===================
C
C_BEGIN_CCPSTB
      SUBROUTINE CCPSTB(IVAL,IA,N)
C     ============================
C
C SET AN INTEGER VALUE FROM 0 TO 255 INTO THE N'TH BYTE OF AN INTEGER
C (OR OTHER) ARRAY.
C NOTE: NO OVERFLOW CHECKING IS DONE.
C
C (MUST BE IMPLEMENTED IF CCPBYT FUNCTION RETURNS .TRUE.)
C [for LAUE]
C
C Arguments:
C ==========
C
C    IVAL (I)   INTEGER: VALUE FROM 0 TO 255
C      IA (I/O) BYTE ARRAY(*): WHERE THE BYTE VALUE IS TO BE INSERTED
C       N (I)   INTEGER: THE POSITION IN 'IA' WHERE THE BYTE VALUE IS TO
C               BE INSERTED
C_END_CCPSTB
C
C SPECIFICATION STATEMENTS
C ------------------------
C
      BYTE IA(*)
      BYTE JBYT(4)
      EQUIVALENCE (JA,JBYT(1))
      EQUIVALENCE (JA,JBYT(1))
      LOGICAL CALLED, LITEND
      EXTERNAL LITEND
      INTEGER IND
      SAVE CALLED, IND
      DATA CALLED/.FALSE./
C
      IF (.NOT.CALLED) THEN
        IF (LITEND(1)) THEN
          IND = 1
        ELSE
          IND = 4
        ENDIF
        CALLED=.TRUE.
      ENDIF
C
C SET BYTE
C --------
C
      JA=IVAL
      IA(N)=JBYT(IND)
      END
C
C
C
C_BEGIN_CCPSUM
      REAL FUNCTION CCPSUM(A,N,L)
C     ===========================
C
C---- This function sums the elements of an array. (for the cray this
C     function will call the cray 'ssum' function)
C
C Arguments:
C ==========
C
C           A (I)   REAL ARRAY(N): ARRAY TO BE SUMMED
C           N (I)   INTEGER: NO. OF ELEMENTS IN THE ARRAY
C           L (I)   INTEGER: SUM EVERY L'TH ELEMENT
C
C  CCPSUM RETURNS THE SUM
C_END_CCPSUM
C
C SPECIFICATION STATEMENTS AND CODE
C
C     .. Scalar Arguments ..
      INTEGER L,N
C     ..
C     .. Array Arguments ..
      REAL A(N)
C     ..
C     .. Local Scalars ..
      INTEGER I
C     ..
      CCPSUM = 0.0
      DO 10 I = 1,N,L
        CCPSUM = A(I) + CCPSUM
   10 CONTINUE
      END
C
C
C
C_BEGIN_CCPTIM
      SUBROUTINE CCPTIM(IFLAG,CPU,ELAPS)
C     ==================================
C
C---- Return cpu time and elapsed time in seconds as
C     intervals from the initial call with iflag=0.  Note that there is
C     only one timer!
C
C Arguments:
C ==========
C
C       IFLAG (I/O) INTEGER: =0, initialise, =1, return times, =-1 dummy call
C                   returns -1 if time not available, in which case CPU
C                   and ELAPS are zero
C         CPU (O)   REAL: cpu time in seconds
C       ELAPS (O)   REAL: elapsed time in seconds
C_END_CCPTIM
C
C     .. Scalar Arguments ..
      REAL CPU,ELAPS
      INTEGER IFLAG
C     ..
C     .. Local Scalars ..
      INTEGER STIME, TIM0
C     ..
C     .. External Subroutines ..
      EXTERNAL UCPUTM,USTIME
C     ..
C     .. Save statement ..
      SAVE TIM0
C     ..
      IF (IFLAG.EQ.0) THEN
        ELAPS = 0.0
        CPU = 0.0
        CALL USTIME(TIM0)
        CALL UCPUTM(CPU)
      ELSE
        CALL USTIME(STIME)
        ELAPS = STIME - TIM0
        CPU = 1.0
        CALL UCPUTM(CPU)
      END IF
      END
C
C_BEGIN_CCPTOI
      SUBROUTINE CCPTOI(ARRAY,N,II,ITYP,IFAIL)
C     ========================================
C
C---- This subroutine converts the n'th byte or integer*2 element in a
C     non-character array to an integer value. it is used by the
C     map file handling routines and must be implemented if map modes
C     0,1,3 or 5 are to be used.
C
C Arguments:
C ==========
C
C       ARRAY (I)   REAL ARRAY(*): CONTAINING THE ELEMENTS TO BE CONVERTED
C
C           N (I)   INTEGER: THE NUMBER OF THE ELEMENT TO BE CONVERTED
C
C          II (O)   INTEGER: THE CALCULATED INTEGER VALUE (FOR BYTES THIS WILL
C                   BE IN THE RANGE 0-255)
C
C        ITYP (I)   INTEGER: THE CONVERSION TYPE =1, BYTE TO INTEGER
C                                                =2, INTEGER*2 TO INTEGER
C
C       IFAIL (I/O) INTEGER: ON INPUT   =0, STOP IF CONVERSION NOT AVAILABLE
C                                       =1, RETURN FROM SUBROUTINE ALWAYS
C                            ON OUTPUT  UNCHANGED IF CONVERSION CARRIED OUT
C                                       =-1 IF CONVERSION NOT AVAILABLE
C_END_CCPTOI
C
C     .. Scalar Arguments ..
      INTEGER IFAIL,II,ITYP,N
C     ..
C     .. Array Arguments ..
      REAL ARRAY(*)
C     ..
C     .. Local Scalars ..
      REAL RR
      INTEGER IA,NB,NIH,NW
C     ..
C     .. Local Arrays ..
      BYTE IBYT(4),JBYT(4)
      INTEGER*2 JHALF(2)
C     ..
C     .. Intrinsic Functions ..
      INTRINSIC MOD
C     ..
C     .. Equivalences ..
      EQUIVALENCE (IA,IBYT(1))
      EQUIVALENCE (RR,JHALF(1),JBYT(1))
C     ..
      LOGICAL CALLED, LITEND
      INTEGER IND
      EXTERNAL LITEND, CCPERR
      SAVE CALLED, IND
      DATA CALLED/.FALSE./
C
      IF (.NOT.CALLED) THEN
        CALLED=.TRUE.
        IF (LITEND(1)) THEN
          IND = 1
        ELSE
          IND = 4
        ENDIF
      ENDIF
C
      GO TO (10,20) ITYP
C
C---- Byte to integer value
C
   10 NW = (N-1)/4 + 1
      NB = MOD(N-1,4) + 1
      IA = 0
      RR = ARRAY(NW)
      IBYT(IND) = JBYT(NB)
      II = IA
      IF (II.LT.0 .OR. II.GT.255) THEN
        IF (IFAIL .EQ. 0) THEN
          CALL CCPERR(1,' *** Error in CCPTOI, bad conversion ***')
        ELSE
          IFAIL = -1
        ENDIF
      ENDIF
      RETURN
C
C---- Integer*2 to integer value
C
   20 NW = (N-1)/2 + 1
      NIH = MOD(N-1,2) + 1
      RR = ARRAY(NW)
      II = JHALF(NIH)
      IF (II.LT.0 .OR. II.GT.65535) THEN
        IF (IFAIL .EQ. 0) THEN
          CALL CCPERR(1,' *** Error in CCPTOI, bad conversion ***')
        ELSE
          IFAIL = -1
        ENDIF
      ENDIF
      END
C
C
C
C_BEGIN_CCPUFL
      SUBROUTINE CCPUFL
C     =================
C
C---- This subroutine is called to suppress underflow error messages
C     if required and if the facility is available.
C
C Arguments:  NONE
C ==========
C
C----  Not implemented.
C_END_CCPUFL
      END
C
C
C
C_BEGIN_CCPUPC
      SUBROUTINE CCPUPC(STRING)
C     =========================
C
C---- Convert a text string to upper case in situ
C
C Arguments:
C ==========
C
C      STRING (I/O) CHARACTER*(*): STRING TO BE CONVERTED
C_END_CCPUPC
C
C     .. Scalar Arguments ..
      CHARACTER STRING* (*)
C     ..
C     .. Local Scalars ..
      INTEGER K,L,LL
      CHARACTER LC*26,UC*26
C     ..
C     .. Intrinsic Functions ..
      INTRINSIC INDEX,LEN
C     ..
C     .. Data statements ..
      DATA LC/'abcdefghijklmnopqrstuvwxyz'/
      DATA UC/'ABCDEFGHIJKLMNOPQRSTUVWXYZ'/
C     ..
      LL = LEN(STRING)
      IF (LL.GT.0) THEN
        DO 10 L = 1,LL
          K = INDEX(LC,STRING(L:L))
          IF (K.NE.0) STRING(L:L) = UC(K:K)
   10   CONTINUE
      END IF
      END
C
C
C
C_BEGIN_CCP4_VERSION
      SUBROUTINE CCP4_VERSION(VERSION)
C     =================================
C
C---- Return current CCP4 version as string
C
C Arguments:
C ==========
C
C       VERSION (O)   CHARACTER*(*): current version of CCP4 suite
C_END_CCP4_VERSION
C
C     .. Scalar Arguments ..
      CHARACTER*(*) VERSION

      VERSION = '4.2'

      END
C
C_BEGIN_CCP4_PATCH_LEVEL
      SUBROUTINE CCP4_PATCH_LEVEL(PATCH_LEVEL)
C     =================================
C
C---- Return current CCP4 patch level as string
C
C Arguments:
C ==========
C
C      PATCH_LEVEL  (O)   CHARACTER*(*): current patch level of CCP4 suite
C_END_CCP4_PATCH_LEVEL
C
C     .. Scalar Arguments ..
      CHARACTER*(*) PATCH_LEVEL

      PATCH_LEVEL = '4.2.2'

      END
C
C
C
C_BEGIN_CCP4_PROG_VERSION
      SUBROUTINE CCP4_PROG_VERSION(VERSION,IFLAG)
C     ===========================================
C
C---- Set or return program version as quoted by calling
C     program. This is different from the library version
C     set in CCP4_VERSION
C
C Arguments:
C ==========
C
C       VERSION (I/O)   CHARACTER*(*): program version
C
C       IFLAG   (I)     INTEGER        = 0  use VERSION to set version string
C                                      = 1  retrieve VERSION
C_END_CCP4_PROG_VERSION
C
C     .. Scalar Arguments ..
      CHARACTER*(*) VERSION
      INTEGER IFLAG

      INTEGER LENGTH
      CHARACTER PVERSION*20
      SAVE PVERSION

      DATA PVERSION /' '/

      IF (IFLAG.EQ.0) THEN
        LENGTH = LENSTR(VERSION)
        IF (LENGTH.GT.20) LENGTH = 20
        PVERSION = VERSION(1:LENGTH)
      ELSEIF (IFLAG.EQ.1) THEN
        VERSION = PVERSION
      ENDIF

      END
C
C
C
C_BEGIN_CCPVRS
      SUBROUTINE CCPVRS(ILP,PROG,VDATE)
C     =================================
C
C---- Print program name and date of current version (also prints run
C     date if available)
C
C Arguments:
C ==========
C
C         ILP (I)   INTEGER: UNIT NUMBER FOR PRINTER OUTPUT
C        PROG (I)   CHARACTER*(*): VARIABLE HOLDING PROGRAM NAME (MAX
C                   OF 10 CHARACTERS)
C       VDATE (I)   CHARACTER*(*): VARIABLE HOLDING DATE OF THE CURRENT
C                   VERSION AS DD/MM/YY
C_END_CCPVRS
C     .. Scalar Arguments ..

      INTEGER ILP
      CHARACTER PROG* (*),VDATE* (*), PNM*(*)
C     ..
C     .. Local Scalars ..
      CHARACTER CTIME*8,DT2*8,DT*10,PR*20,UID*20,TMPPRG*100,VERSION*10,
     +          PVERSION*20
      SAVE PR
C     ..
C     .. External Functions ..
      INTEGER LENSTR
      CHARACTER FROOT*20
      EXTERNAL LENSTR, FROOT
C     ..
C     .. External Subroutines ..
      EXTERNAL CCPDAT,UGTUID,UTIME,ccp4h_summary_beg,ccp4h_summary_end
C     ..
      DATA PR /' '/
C
C---- Output heading
C
      call ccp4h_summary_beg()
      call ccp4h_pre_beg()
      PR = PROG
      DT = VDATE
      CALL CCPDAT(DT2)
      CALL UGTUID(UID)
      CALL UTIME(CTIME)
      CALL CCP4_VERSION(VERSION)
      CALL CCP4_PROG_VERSION(PVERSION,1)
      IF (PVERSION.EQ.' ') PVERSION = VERSION
      WRITE (ILP,FMT=6000) VERSION(1:3),PR,PVERSION(1:10),
     +  DT,UID(1:LENSTR(UID)),DT2,CTIME
 6000 FORMAT (/,/,/,/,
     + '1##########################################################',
     + '#####',/,
     + ' ##########################################################',
     + '#####',/,
     + ' ##########################################################',
     + '#####',/,
     + ' ### CCP4 ',A3,': ',A17,2X,'version ',A,': ',A8,'##',/,
     + ' ##########################################################',
     + '#####',/,
     + ' User: ',A,'  Run date: ',A8,'  Run time:',A,
     + /,/,/,
     + ' Please reference: Collaborative Computational Project,',
     + ' Number 4. 1994.',/,' "The CCP4 Suite: Programs for Protein',
     + ' Crystallography". Acta Cryst. D50, 760-763.',/,/,
     + ' as well as any specific reference in the program write-up.',
     + /,/)
      call ccp4h_summary_end()
C
      RETURN
C
      ENTRY CCPPNM (PNM)
C_BEGIN_CCPPNM
C     SUBROUTINE CCPPNM (PNM)
C     =======================
C
C     Returns the program name previously set by CCPVRS (/CCPRCS); if
C     that isn't set, use arg(0).
C
C     Argument:
C         PNM (O)   CHARACTER*(*)  Program name
C_END_CCPPNM
      IF (PR.EQ.' ') THEN
        CALL UGTARG(0,TMPPRG)
        PR = FROOT(TMPPRG)
      END IF
      PNM = PR
      END
C
C
C
C_BEGIN_CCPZBI
      SUBROUTINE CCPZBI (ARR1,NUM)
C     ============================
C
C  This routine zeros NUM bytes of the array ARR1
C
C Arguments:
C
C    ARR1 (O)   BYTE ARRAY(*): array to be zeroed
C     NUM (I)   INTEGER: Number of bytes
C_END_CCPZBI
C
C  Arguments ......
      INTEGER NUM
      BYTE ARR1(*)
C
      INTEGER J
C
      DO 10 J=1,NUM
   10 ARR1(J)=0
      END
C
C
C_BEGIN_CCPZI
      SUBROUTINE CCPZI (IARR1,NUM)
C     ===========================
C
C  This routine assigns zero to IARR1 using NUM words
C
C Arguments:
C
C    IARR1 (O)   INTEGER ARRAY(*): array to be zeroed
C     NUM (I)   INTEGER: Number of words
C_END_CCPZI
C
C  Arguments ..........
      INTEGER NUM, IARR1(*)
C
      INTEGER J
C
      DO 10 J=1,NUM
   10 IARR1(J)=0
      END
C
C
C_BEGIN_CCPZR
      SUBROUTINE CCPZR (ARR1,NUM)
C     ===========================
C
C  This routine assigns zero to ARR1 using NUM words
C
C Arguments:
C
C    ARR1 (O)   REAL ARRAY(*): array to be zeroed
C     NUM (I)   INTEGER: Number of words
C_END_CCPZR
C
C  Arguments ..........
      INTEGER NUM
      REAL ARR1(*)
C
      INTEGER J
C
      DO 10 J=1,NUM
   10 ARR1(J)=0.0
      END
C
C
C     ===================================
C_BEGIN_FDIR
      FUNCTION FDIR(FILNAM)
      CHARACTER*(*) FDIR
C     ===================================
C
C---- Returns the path (directory) of a file name or ' '
C
C Arguments:
C
C  FILNAM (I)   CHARACTER*(*): File name
C_END_FDIR
      CHARACTER FILNAM* (*)
      CHARACTER*1 NAME, TYPE, VERS
      EXTERNAL CCPPSF
C
      CALL CCPPSF(FILNAM, FDIR, NAME, TYPE, VERS)
      END
C
C
C     ====================================
C_BEGIN_FEXTN
      FUNCTION FEXTN(FILNAM)
      CHARACTER*(*) FEXTN
C     ====================================
C
C---- Returns the extension of a file name or ' '
C
C Arguments:
C
C  FILNAM (I)   CHARACTER*(*): File name
C_END_FEXTN
      CHARACTER FILNAM* (*)
      CHARACTER*1 PATH, NAME, VERS
      EXTERNAL CCPPSF
C
      CALL CCPPSF(FILNAM, PATH, NAME, FEXTN, VERS)
      END
C
C
C     ====================================
C_BEGIN_FROOT
      FUNCTION FROOT(FILNAM)
      CHARACTER*(*) FROOT
C     ====================================
C
C---- Returns a file name minus an extension.
C
C Arguments:
C
C  FILNAM (I)   CHARACTER*(*): File name
C_END_FROOT
C
      CHARACTER FILNAM* (*)
      CHARACTER*1 PATH, TYPE, VERS
      EXTERNAL CCPPSF
C
      CALL CCPPSF(FILNAM, PATH, FROOT, TYPE, VERS)
      END
C
C
C
C_BEGIN_LITEND
         LOGICAL FUNCTION LITEND(IDUM)
C        =============================
C
C---- Check endedness, Returns TRUE if little endian (VAX, FX2800,
C                                                   Ultrix)
C                              FALSE if big endian (IBM,IRIS,ESV)
C
C Arguments:
C ==========
C
C    IDUM (D)   DUMMY
C_END_LITEND
C
         INTEGER I, IDUM
         BYTE B(4)
         EQUIVALENCE (I,B(1))
C
C---- Initialise B
C
          DO 10 JDO=1,4
            B(JDO) = 0
 10       CONTINUE
C
          I = 1
C
          IF (B(1) .NE. 0) THEN
              LITEND = .TRUE.
          ELSE
              LITEND = .FALSE.
          END IF
C
        END
C
C
C======================================================================
C
C_BEGIN_LENSTR
      INTEGER FUNCTION LENSTR(STRING)
C     ===============================
C
C---- Returns significant string length excluding trailing spaces
C
C     NB: LENSTR removes trailing spaces, plus trailing "null"
C     characters (ascii code 0) and carriage-returns (ascii code 13).
C     Carriage-returns may be inserted by editing on non-unix
C     systems? (PJX)
C
C Arguments:
C ==========
C
C  STRING (I)   CHARACTER*(*): Input string
C_END_LENSTR
C
C     .. Scalar Arguments ..
      CHARACTER STRING* (*)
C     ..
C     .. Intrinsic Functions ..
      INTRINSIC LEN
C     ..
      LENSTR = LEN(STRING)
 10   CONTINUE
      IF (LENSTR.NE.0) THEN
        IF(STRING(LENSTR:LENSTR).EQ.' ' .OR.
     .       ICHAR(STRING(LENSTR:LENSTR)).EQ.0 .OR.
     .       ICHAR(STRING(LENSTR:LENSTR)).EQ.13) THEN
          LENSTR = LENSTR - 1
          GO TO 10
        END IF
      END IF
C
      END
C
C
C FUNCTION 'LUNSTI'
C =================
C
C_BEGIN_LUNSTI
      FUNCTION LUNSTI(IDUM)
C     =====================
C
C Returns the fortran standard input unit number
C
C Arguments:
C ==========
C
C       IDUM (D)   Dummy
C_END_LUNSTI
C
      LUNSTI = 5
      END
C
C
C FUNCTION 'LUNSTO'
C =================
C
C_BEGIN_LUNSTO
      FUNCTION LUNSTO(IDUM)
C     =====================
C
C Returns the fortran standard output unit number
C
C Arguments:
C ==========
C
C       IDUM (I)   Dummy argument
C_END_LUNSTO
C
      LUNSTO = 6
      END
C
C
C FUNCTION 'NBITST'
C =================
C
C_BEGIN_NBITST
      FUNCTION NBITST(IWORD,LSB,NBITS)
C     ================================
C
C Return the (unsigned) integer value held within a bit field in a word
C [for LAUE]
C
C Arguments:
C ==========
C
C      IWORD (I)    INTEGER: The word containing the bits to be examined
C
C        LSB (I)    INTEGER: The least significant bit offset for the bit field
C
C      NBITS (I)    INTEGER: The number of bits in the bit field (Must be less
C                   than the word length)
C_END_NBITST
C
C====== Get the bit value
C
      KMSK = 2**NBITS - 1
      NBITST = IAND(ISHFT(IWORD,-LSB),KMSK)
      END
C
C
C     =======================
C_BEGIN_NOCRLF
      SUBROUTINE NOCRLF(LINE)
C     =======================
C
C---- Output a line suppressing cr/lf.
C
C Arguments:
C ==========
C
C    LINE (I)   CHARACTER*(*): Line to output.
C_END_NOCRLF
C
      EXTERNAL LUNSTO,TTSEND
      INTEGER LUNSTO
      CHARACTER*(*) LINE
      CALL TTSEND(LUNSTO(1),LINE,0)
      END
C
C
C======================================================================
C
C_BEGIN_QPRINT
      SUBROUTINE QPRINT(IFLAG,MSG)
C     ============================
C
C     QPRINT - Conditionally print information.
C
C     Normally, MSG is printed iff IFLAG is not greater than the
C     `reference' level for messages.  This reference level is set to
C     the value of IFLAG on the first call (which won't print anything).
C     The first call is typically from CCPFYP.
C
C Usage:  CALL QPRINT   (IFLAG,MSG)
C         INTEGER       IFLAG
C         CHARACTER*(*) MSG
C
C Input:  IFLAG         debug level (0-9)
C         MSG           the output message itself
C
C Output: None.
C_END_QPRINT
C======================================================================
C
      INTEGER IFLAG, LEVEL
      CHARACTER MSG* (*)
      EXTERNAL LUNSTO, LENSTR
      INTEGER PFLAG, LUNSTO, LENSTR, LL, LX, LS
      SAVE PFLAG
      DATA PFLAG /-1/
C
C     external functions
C
      LOGICAL VAXVMS
      EXTERNAL VAXVMS
C
      IF (PFLAG.EQ.-1) THEN
        PFLAG = IFLAG
        RETURN
      END IF
C
C     way to retrieve current value of PFLAG
C
      IF (IFLAG.EQ.-999) THEN
        IFLAG = PFLAG
        RETURN
      END IF
C
      IF (IFLAG.LE.PFLAG) THEN
        LL = LENSTR (MSG)
        IF (VAXVMS() .AND. (LL.GE.132)) THEN
C         break lines longer than 132 characters for VMS
          LX = 1
          LS = 131
 10       CONTINUE
          WRITE (LUNSTO(1),'(1X, A)') MSG(LX:LS)
          IF (LS.EQ.LL) GOTO 20
          LX = LS  + 1
          LS = LS + 130
          IF (LS.GT.LL) LS = LL
          GO TO 10
        ELSE
          IF (LL.EQ.0) THEN
            WRITE(LUNSTO(1),'()')
          ELSE
            WRITE (LUNSTO(1),'(1X, A)') MSG(1:LL)
          END IF
        END IF
 20     CONTINUE
      END IF
      RETURN

      ENTRY QPRLVL (LEVEL)
C_BEGIN_QPRLVL
C     SUBROUTINE QPRLVL(LEVEL)
C
C     Returns the current `debug level' used by QPRINT in the integer
C     output variable LEVEL.
C_END_QPRLVL
      LEVEL = PFLAG
      END
C
C
C SUBROUTINE 'STBITS'
C ===================
C
C_BEGIN_STBITS
      SUBROUTINE STBITS (IWORD,LSB,NBITS,IVAL)
C     ========================================
C Set a bit field within a word to a given (unsigned) integer value
C [for LAUE]
C
C Arguments:
C ==========
C
C      IWORD (I/O)  INTEGER: The word in which the bits are to be set
C
C        LSB (I)    INTEGER: The least significant bit offset for the bit field
C
C      NBITS (I)    INTEGER: The number of bits in the bit field (must be less
C                   than the word length)
C
C       IVAL (I)    INTEGER: The unsigned integer value to be set in the bit
C                   field (The user should ensure that this value will
C                   fit within the requested bit field)
C_END_STBITS
C
C====== Set the bits
C
      KMSK = 2**NBITS - 1
      KVAL = IVAL
      KMSK = ISHFT(KMSK,LSB)
      KMSK = NOT(KMSK)
      KVAL = ISHFT(KVAL,LSB)
      IWORD = IOR(IAND(IWORD,KMSK),KVAL)
      END
C
C
C FUNCTION 'CCPLIC'
C ===================
C
C_BEGIN_CCPLIC
C
C Arguments:
C ==========
C
C      NAME (I)  CHARACTER: Which license to test for. Only CCP4 currently works.
C
C_END_CCPLIC
C
C======
C
      LOGICAL FUNCTION CCPLIC(NAME)

      CHARACTER NAME* (*)
C     The name defines the kind of license.
C     Theoretically one day more than one licenses could be added.
      PARAMETER (ISTRLN=200)
      LOGICAL VAXVMS, WINMVS, CCPEXS
      CHARACTER LINE* (ISTRLN)
      INTEGER SUE

      CALL CCPUPC(NAME)
      SUE = 0
      IF (.NOT.VAXVMS() .AND. .NOT.WINMVS() .AND. NAME.EQ.'CCP4') THEN
C first off check for global agreement
        CALL UGTENV (NAME, LINE)
        LINE =  LINE(1:LENSTR(LINE))//'/.agree2ccp4'
        IF (.NOT. CCPEXS(LINE(1:LENSTR(LINE)))) THEN
          SUE = 1
        ENDIF
C secondly check for local only if global is not there.
        IF (SUE.EQ.1) THEN
          CALL UGTENV ("HOME", LINE)
          LINE =  LINE(1:LENSTR(LINE))//'/.agree2ccp4'
          IF (.NOT. CCPEXS(LINE(1:LENSTR(LINE)))) THEN
            SUE = SUE + 1
          ENDIF
        ENDIF
      ENDIF
c if SUE is greater than 1 then fail.
      IF (SUE.GT.1) THEN
        CALL CCPERR (1,'Can''t find required license agreements.')
      ENDIF
      CCPLIC = .TRUE.
      END
