	SUBROUTINE VARSCN(LINE,IBGN,LEND,LSTCHR,ID1,ID2,IVALID)
C COPYRIGHT (C) 1983 GLENN EVERHART
C PERMISSION IS GIVEN TO ANYONE TO USE, DISTRIBUTE, OR COPY THIS
C PROGRAM FREELY BUT NOT TO SELL IT COMMERICALLY.
C VARSCN - SCAN COMMAND LINE FOR VARIABLE NAMES.
C
C	SCANS FOR VARIABLE NAMES OF FORM AAANNN WHERE AAA = LETTERS
C BETWEEN A AND Z UP TO NON-ALPHA, CORRESPONDING TO ROW, FOLLOWED BY
C NUMBERS IN THE 0-9 RANGE MAKING A DECIMAL COLUMN NUMBER.
C
C THE LETTERS ARE FORMED BY
C A-Z ALONE GIVE ROW 1-26, COL 1. % IS ROW 27,COL1
C A1-Z1 GIVE ROW 1-26, COL 2
C AA1-ZZ1 ARE ROW 27-52, COL 2
C
C In this version we also recognize cell names using an optional third
C dimension. Forms like B14#2 would be interpreted as cell B14 of sheet
C 2 (sheets start at 0). This is a display trick mainly, as cell offsets
C will be treated as simple 2D addresses as before. However, it will allow
C some greater automation of the notion of multiple areas. Each "page" is
C formed by adding constants KCDELT and KRDELT to the column and row
C of the base number, multiplied by the offset in sheets. These constants
C are initially zero, collapsing all "pages" on top of one another. This
C interpretation will occur provided K3DFG is 0 or positive. If it is 
C negative all 3D interpretation will be ignored, and even parsing of
C the cell names for trailing # characters will be disabled. (This will
C allow strict return to the older meanings.)
	IMPLICIT InTEgeR*4 (A-Z)
	INCLUDE 'VKLUGPRM.FTN'
C	PARAMETER RRW = 32
C	PARAMETER RCL = 32 ! REAL (PHYSICAL) ROWS AND COLUMNS OF SPREADSHEET
C	PARAMETER RRCL = 1024
C PARAMETER RRCL=RRW*RCL ! SIZE
	PARAMETER CUP=1,NEL=14
C NOTE COL 1 IS DUMMY. DISPLAY THE SHEET SIDEWAYS SO WE GET USUAL VISUAL
C ROWS, COLS., AND ACCUMULATORS A-Z,% JUST APPEAR AS A FICTITIOUS ROW 0
C ON DISPLAY, INSTEAD OF REAL COLUMN 1 HERE.
C	PARAMETER DRW = 8 ! DISPLAY MAX ROWS
C	PARAMETER DCL = 8 ! AND COLS
	DIMENSION LINE(LEND)
	LOGICAL*1 LINE
	Integer*4 klvl,k3dfg,kcdelt,krdelt,kshtf
	common/klvl/klvl,k3dfg,kcdelt,krdelt,kshtf
	InTEgeR*4 TYPE(RRWP,RCLP),VLEN(9)
	REAL*8 XVBLS(RRWP,RCLP),xac
	LOGICAL*1 AVBLS(20,27),VBLS(8,RRWP,RCLP)
	REAL*8 XAVB
	REAL*4 XAV2(2)
	LOGICAL*1 XAV1(8)
	EQUIVALENCE(XAVB,XAV2(1)),(XAVB,XAV1(1))
	EQUIVALENCE(XVBLS(1,1),VBLS(1,1,1))
	COMMON/V/TYPE,AVBLS,VBLS,VLEN
	DIMENSION NRDSP(DRW,DCL),NCDSP(DRW,DCL)
	COMMON/D2R/NRDSP,NCDSP
C NRDSP AND NCDSP ARE REAL ROW, COL OF DISPLAY ROW, COL CELLS. NOTE THAT
C NOT ALL DISPLAY CELLS ARE ALWAYS ACTUALLY SHOWN; ONLY THOSE THAT FIT
C ARE SHOWN; THE REST "EXIST" BUT DON'T APPEAR UNLESS ROWS ARE SMALL
C ENOUGH.
C
C THIS PROGRAM ALSO HANDLES CELL SPECS OF FORM
C P#+nnn#+nnn (or P#-nnn#-mmm) FOR Physical cells relative to our current
C physical cell on the sheet (clamped at boundaries), or of form
C D#+nnn#+mmm etc for Display cells relative to our current display
C location as held in the DROW,DCOL cells in commons.
	InTEgeR*4 idol1,idol2,idol3,idol4,idol5,idol6
	common/dollr/idol1,idol2,idol3,idol4,idol5,idol6
	InTEgeR*4 PROW,PCOL ! PHYSICAL ROW, COL BEING HANDLED.
	InTEgeR*4 DROW,DCOL,DCLV,DRWV
C -- FLAG DLFG AS 0 IF NO D# FORMS EVER SEEN, 1 IF SEEN.
	InTEgeR*4 DLFG
	COMMON/DLFG/DLFG
	InTEgeR*4 RSM,CSM,AFG,ASM,VCF,CH
	COMMON/DCTL/PROW,PCOL,DROW,DCOL,DRWV,DCLV
C DRWV,DCLV = # OF MAX ROWS, COLS ACTUALLY ON SCREEN NOW. DROW,DCOL
C ARE ACTUAL "CURSOR" LOCATION.
C
C ZERO OUR VARIABLES
	LPFG=0 ! FLAG WE GOT A LOGICAL/PHYSICAL # FORM AND TYPE
	AFG=0  ! FLAG WE SAW AN ALPHA
	ASM=0  ! SUM OF ALPHAS HASHCODED (ACCUMULATOR)
	NSM=0  ! ACCUMULATOR FOR NUMERICS
	NFG=0  ! FLAG WE SAW A NUMERIC
	RSM=0  ! AC FOR ROWS IN # FORMS
	CSM=0  ! AC FOR COLS IN # FORMS
	ISPC=0 ! COUNTER FOR NONSPACES SEEN (USED TO STOP ON TRAILING SPACES)
	ktpnd=0
	idol1=0
	idol2=0
	IF(LINE(IBGN).NE.'%')GOTO 2000
	ID1=27
	ID2=1
	IVALID=1
	LSTCHR=IBGN+1
C SPECIAL CASE FOR % = AC #27
	RETURN
2000	CONTINUE
	DO 1 N=IBGN,LEND
	VCF=0
	LSTCHR=N
	CH=LINE(N)
C IGNORE SPACES AND TABS IF LEADING
	IF(CH.GT.32)ISPC=ISPC+1
	IF(CH.GT.0.AND.CH.LE.32.AND.ISPC.EQ.0)GOTO 1
C SPECIAL CASE TRAILING DOLLAR SIGNS... SKIP AND FLAG
	IF(CH.NE.36)GOTO 3443
C 36 IS ASCII FOR $ SIGN
C SAW A DOLLAR SIGN
	IF(AFG.EQ.1.AND.NFG.EQ.0)IDOL1=1
	IF(AFG.EQ.1.AND.NFG.EQ.1)IDOL2=1
C LEAVES WITH IDOL1 FLAGGED AS 1 IF LETTER PART WAS FOLLOWED BY
C DOLLAR SIGN, AND IDOL2 FLAGGED IF NUMBER PART WAS FOLLOWED
C BY DOLLAR. IGNORES ALL OTHER DOLLAR SIGNS.
	GOTO 1
3443	CONTINUE
C GET CHARACTER VALUE IN.
C MUST BE UPPERCASE.
	IF(.NOT.(CH.GE.65.AND.CH.LT.91)) GOTO 100
C CH IS AN ALPHA, RANGE A-Z
	VCF=1 ! VALID CHAR SEEN
	AFG=1 !SAW THE ALPHA
C IF WE SEE A NUMERIC AND THEN SEE AN ALPHA IT'S NOT VALID.
	IF(NFG.NE.0)GOTO 103
	IF(ASM.LT.RRCL)ASM=(CH-64)+26*ASM
	IF(CH.EQ.80)LPFG=1 ! FLAG WE GOT PHYS. FORM MAYBE
	IF(CH.EQ.68)LPFG=2 ! FLAG WE GOT DISPLAY FORM MAYBE
100	CONTINUE
C EXPECT # FORMS TO HAVE # JUST AFTER 1ST ALPHA.
C 35 IS ASCII VALUE OF '#' CHAR.
	IF(CH.EQ.35)GOTO 1000
C NEXT TEST NUMERICS
	IF(.NOT.(CH.GE.48.AND.CH.LE.57))GOTO 101
C CH IS A NUMERIC, RANGE 0-9
	VCF=1 ! VALID CHAR SEEN
	NFG=1 ! FLAG WE SAW NUMERIC
	IF(AFG.NE.0)GOTO 102
103	CONTINUE
C INVALID ... NUMERIC AND NO PRIOR ALPHA. FLAG BAD NAME AND EXIT.
	IVALID=0
	RETURN
102	CONTINUE
	IF(NSM.LT.RRCL)NSM=(CH-48)+10*NSM ! CONVERT CHARS TO BINARY AS SEEN
101	CONTINUE
	IF(VCF.EQ.0)GOTO 2 !END ON ANY INVALID CHARACTER
1	CONTINUE
	GOTO 333
2	CONTINUE
333	CONTINUE
	IF(AFG.EQ.0)GOTO 103
	ID1=ASM
	ID2=1+NSM ! NOTE ID2=1 IF NO NUMERICS SEEN, MORE OTHERWISE.
	GOTO 1201
1000	CONTINUE
C HERE HANDLE CURRENT-REFERENCED FORMS USING # AS SPECIAL CHARACTER MEANING
C THE CURRENT POSITION. THESE TYPES OF REFERENCES MAY BE MOVED AROUND THE
C SHEET WHICH ACCOUNTS FOR THEIR USEFULNESS. SINCE THERE IS A DISPLAY
C AND PHYSICAL SHEET WHICH ARE MAPPED BY A MAPPING, ALLOW EITHER
C TO BE REFERENCED. THUS, COMPLEX CALCULATIONS MAY BE DONE BUT LARGELY
C HIDDEN. THE ACCUMULATORS MAY BE USED AS SCRATCH STORAGE FOR THIS
C SORT OF THING.
C SAW THE # SIGN, SO SEE IF THE + OR - N CAN BE DECODED.
C IF NO P OR D WAS SEEN HOWEVER WE HAVE AN INVALID NAME, SO FLAG IT.
	IF(LPFG.EQ.0)GOTO 103
C PASS THE # SIGN PRIOR TO GETTING THE NUMERIC.
	LSTCHR=LSTCHR+1
	iundr=0
	if(line(lstchr).eq.'_')iundr=1
	IF(LINE(LSTCHR).EQ.'$')IUNDR=2
	if(line(lstchr).ne.'%'.and.iundr.eq.0)goto 3900
c allow p#%ab form to mean use ac a and b to get offsets from "here"
C allow p#_ab to give absolute addresses instead of relative offsets
C ALLOW P#$V1V2 to act like p#_ab but with any variable name instead
C  of just accumulators.
	CSM=0
	RSM=0
C DEFAULT TO "THIS" CELL
	LSTCHR=LSTCHR+1
C PASS THE % SIGN
	if(iundr.lt.2)goto 3906
	kkk=lstchr
	kkkk=kkk+20
	call varsc2(line,kkk,kkkk,kend,kr1,kr2,kvld)
	if(kvld.eq.0)write(6,3907)kkk,(line(kkkv),kkkv=kkk,kkkk)
3907	format(' Invld vblnm c:',i4,'=',24a1)
	if(kvld.eq.0)goto 3906
	if(line(kend).eq.':')kend=kend+1
	kkk=kend
	kkkk=kkk+20
	call varsc2(line,kkk,kkkk,kend,kc1,kc2,kvld)
	if(kvld.eq.0)write(6,3907)kkk,(line(kkkv),kkkv=kkk,kkkk)
	if(kvld.eq.0)goto 3906
	if(line(kend).eq.':')kend=kend+1
	lstchr=kend
	call xvblgt(kr1,kr2,xac)
	rsm=xac
	call xvblgt(kc1,kc2,xac)
	csm=xac
	goto 3901
3906	continue
	RSM=LINE(LSTCHR)
	CSM=LINE(LSTCHR+1)
	LSTCHR=LSTCHR+2
C FIX UP ASCII OFFSETS, AND MEANWHILE REQUIRE UPPERCASE
C AND THAT THERE BE 2 AC'S NAMES AFTER THE %.
C THIS SHOULD BE HANDY FOR COMMAND FILES.
	RSM=RSM-64
	CSM=CSM-64
C NOW RSM, CSM ARE SUBSCRIPTS. PULL OUT VALUES FROM XVBLS
	IF(RSM.LE.0.OR.RSM.GT.27)GOTO 103
	IF(CSM.LE.0.OR.CSM.GT.27)GOTO 103
	DO 3902 IV=1,8
3902	XAV1(IV)=AVBLS(IV,RSM)
	RSM=XAVB
	DO 3903 IV=1,8
3903	XAV1(IV)=AVBLS(IV,CSM)
	CSM=XAVB
C LOADS THE 2 AC'S TO THE OFFSETS AND GOES ON...JUST NEEDS THE
C 2 LETTERS AFTER P#% OR D#%.
	goto 3901
3900	continue
	CALL GN(LSTCHR,LEND,NUM,LINE)
C GN GETS THE +- NN NUMBER AND RETURNS VALUE IN NUM.
C LSTCHR RETURNS AS NEXT CHAR NOT USED.
	RSM=NUM
C 35 IS ASCII FOR '#'
C allow any delimiter between numbers, though we must have # at start
C  to delimit valid relative coordinates.
C	IF(LINE(LSTCHR).NE.35) GOTO 103
C IF NO SECOND # SEEN, THE FORM IS INVALID SO SAY SO AND EXIT.
	LSTCHR=MIN0(LSTCHR+1,LEND)
CC BUMP PAST THE # IF WE SAW IT.
C now get the second numeric string and bump LSTCHR past it.
	NUM=0
	CALL GN(LSTCHR,LEND,NUM,LINE)
	CSM=NUM
C NOW HAVE THE NUMBERS ENCODED. NOTE THAT ## IS VALID.
3901	CONTINUE
	IF(LPFG.EQ.2) GOTO 1200
C IF HERE, LPFG=1 AND WE ARE ON PHYSICAL SHEET.
	If(Iundr.ne.0)goto 3905
	ID2=CSM+PCOL
	ID1=RSM+PROW
	goto 1201
3905	Continue
	ID2=CSM+1
	ID1=RSM
C P#_ab forms give ABSOLUTE addressing
1201	CONTINUE
C	IF(ID1.GT.RRW.OR.ID1.LE.0)GOTO 103
C	IF(ID2.GT.RCL.OR.ID2.LE.0)GOTO 103
C Add-in for 3d cells
	kshtf=0
	If(k3dfg.lt.0)goto 1202
C 37 is ascii %
	IF(LINE(LSTCHR).NE.37) GOTO 1202
C pass the trailing % character now
	LSTCHR=MIN0(LSTCHR+1,LEND)
C limited form of syntax: either a number is to be used
C or an accumulator.
	If(line(lstchr).gt.64) goto 1203
C a number.
	NUM=0
	CALL GN(LSTCHR,LEND,NUM,LINE)
	CSM=NUM
	Goto 1204
1203	Continue
C a (possible) accumulator
	csm=line(lstchr)
	LSTCHR=MIN0(LSTCHR+1,LEND)
	CSM=CSM-64
C Csm now is index to accumulator. Validity check it.
	IF(CSM.LE.0.OR.CSM.GT.27)GOTO 103
	DO 2902 IV=1,8
2902	XAV1(IV)=AVBLS(IV,csm)
C convert the accumulator value.
	CSM=XAVB
1204	Continue
C now fix up the col and row returned.
	id1=id1+(csm*kcdelt)
	id2=id2+(csm*krdelt)
	kshtf=csm
C allow our callers to see what (if any) "page" was flagged.
C note that zero and no page flagged are treated the same.
1202	Continue
C ALLOW "REFLECTED" COORDINATES FROM ALL OVER...
	IF(ID1.LE.0.OR.ID1.GT.RRCL)GOTO 103
	IF(ID2.LE.0.OR.ID2.GT.RRCL)GOTO 103
	IVALID=1
C ALL IS WELL
	RETURN
1200	CONTINUE
C DISPLAY COLUMN RELATIVE.
	DLFG=1
C DLFG=1 FLAGS WE'VE SEEN DISPLAY RELATIVE FORMS
	DRRW=DROW+RSM
	DRRW=MAX0(1,DRRW)
	DRRW=MIN0(DRW,DRRW)
	DCCL=DCOL+CSM
	DCCL=MAX0(1,DCCL)
	DCCL=MIN0(DCL,DCCL)
C CLAMP TO WITHIN LEGAL DIMENSIONS.
	ID1=NRDSP(DRRW,DCCL)
	ID2=NCDSP(DRRW,DCCL)
	GOTO 1201
	END
C
C GN - GET NUMBER
	SUBROUTINE GN(LAST,LEND,NUM,LINE)
	IMPLICIT InTEgeR*4(A-Z)
	PARAMETER CUP=1,NEL=14
	DIMENSION LINE(110)
	LOGICAL*1 LINE
	LOGICAL*1 NCH
	InTEgeR*4 CH,SFG
	NUM=0
	JSSF=0
	ISSF=0
	CH=0
	SFG=1
	NCH=0
	DO 1 N=LAST,LEND
	M=N
	NCH=LINE(N)
	CH=NCH
	IF(CH.EQ.0)GOTO 2
	IF(CH.EQ.45)SFG=-1
C SFG=SIGN FLAG
C 43 IS ASCII FOR +; 45 IS ASCII FOR - SIGN.
C IGNORE + SIGNS
	IF(CH.GT.32)ISSF=ISSF+1
	IF(ISSF.EQ.0.AND.CH.EQ.32)GOTO 1
C IGNORE SPACES TOO, PROVIDED THEY ARE LEADING SPACES.
C (OTHERS MAY BE DELIMITERS.)
	IF(CH.EQ.43.OR.CH.EQ.45)JSSF=JSSF+1
	IF(JSSF.GT.1.AND.(CH.EQ.43.OR.CH.EQ.45))GOTO 2
C IF WE HAVEN'T SEEN A +/- PROCESS IT HERE.
	IF(CH.EQ.43)GOTO 1
	IF(CH.EQ.45)GOTO 1
	IF(CH.LT.48.OR.CH.GT.57)GOTO 2
C TERMINATE ON ANY NON NUMERIC. SHOULD ALLOW TERMINATE ON SECOND #.
	IF(NUM.LT.3100)NUM=10*NUM+(CH-48)
1	CONTINUE
C NEXT LINE WAS MAX0...
2	LAST=MIN0(M,LEND)
	NUM=NUM*SFG
C ACCOUNTED FOR SIGN; NOW RETURN
	RETURN
	END
