|
|
DataMuseum.dkPresents historical artifacts from the history of: CP/M |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about CP/M Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - download
Length: 17408 (0x4400)
Types: TextFile
Names: »DATABASE.PAS«
└─⟦0d02879d3⟧ Bits:30004605 COMPAS Pascal version 3.03
└─⟦this⟧ »DATABASE.PAS«
└─⟦c96461903⟧ Bits:30002787 SW1602 COMPAS Pascal Version 3.07 Release 1.1
└─⟦this⟧ »DATABASE.PAS«
PROGRAM DATABASE; (*$A+,C-,R-,V-*)
(****************************************************************)
(* *)
(* C-FILE Version 1.00 *)
(* *)
(* DATABASE Example *)
(* *)
(* Copyright (C) 1984 by *)
(* Poly-Data microcenter ApS *)
(* *)
(****************************************************************)
(* DATABASE demonstrates how to use C-FILE to create and main- *)
(* tain a simple customer data base. DATABASE allows you to *)
(* add, find, view, edit, delete and list customers of a pre- *)
(* defined type (see the CUSTREC type definition below). *)
(* DATABASE maintains three files: A data file (CUST.DAT), a *)
(* customer code index (CUST.IXC) and a name index (CUST.IXN). *)
(* The customer code index does not allow duplicate keys, *)
(* whereas the name index does. When DATABASE is run for the *)
(* first time, it will automatically create an empty data base. *)
(* The main menu offers three functions: U)pdate, L)ist and *)
(* Q)uit. U)pdate is used to add, find, view, edit and delete *)
(* customers. L)ist is used to list customers, and Q)uit is *)
(* used to terminate the program. *)
(* On the U)pdate menu, The A)dd function is used to add new *)
(* customers. F)ind is used to locate a customer, either by *)
(* customer code or by last (and first) name. To search for a *)
(* specific customer code, simply enter it when the cursor *)
(* moves to the customer code field. If it is found, the custo- *)
(* mer's data is displayed and you may, if you wish, E)dit or *)
(* D)elete it. To search for a name, enter an empty customer *)
(* code. Then enter the last name and optionally the first *)
(* name. Note that if a first name is specified, the first 15 *)
(* characters of the last name must be entered in full. The *)
(* scan will locate the first customer of the specified name or *)
(* the first customer that follows the specified name if no *)
(* exact match occurs. You may then use N)ext and P)revious to *)
(* move forwards and backwards in alphabetical order. Once you *)
(* have located the wanted customer, enter Q)uit. You may then *)
(* E)dit or D)elete the record shown on the screen, or simply *)
(* leave it unchanged. *)
(* L)ist is used to list customers. Listings will show the cus- *)
(* tomer code, the name and the company. They may be output to *)
(* the P)rinter or to the S)creen, and they may be U)nsorted or *)
(* sorted by customer C)ode or N)ame. *)
(* For further comments, read through this source code. *)
LABEL STOP;
CONST
(* Data record size definition *)
CRSIZE = 342; (* Customer record size *)
(* C-FILE constants *)
MAXRSIZE = CRSIZE; (* Max record size *)
MAXKSIZE = 25; (* Max key size *)
NODESIZE = 16; (* Node size *)
NODEHALF = 8; (* Half node size *)
NBUFSIZE = 5; (* Node buffer size *)
MAXDEPTH = 5; (* Max index depth *)
(* Include C-FILE modules *)
(*$I DATMAN*)
(*$I NPFKEY*)
(*$I ADDKEY*)
(*$I DELKEY*)
TYPE
(* Various string types *)
STR5 = STRINGÆ5Å;
STR10 = STRINGÆ10Å;
STR15 = STRINGÆ15Å;
STR25 = STRINGÆ25Å;
STR80 = STRINGÆ80Å;
ANYSTR = STRINGÆ255Å;
(* Character set type *)
CHARSET = SET OF CHAR;
(* Customer record definition *)
CUSTREC = RECORD
STATUS: INTEGER; (* Status *)
CCODE: STRINGÆ15Å; (* Customer code *)
CDATE: STRINGÆ8Å; (* Entry date *)
FNAME: STRINGÆ15Å; (* First name *)
LNAME: STRINGÆ30Å; (* Last name *)
CMPNY: STRINGÆ40Å; (* Company *)
ADDR1: STRINGÆ40Å; (* Address 1 *)
ADDR2: STRINGÆ40Å; (* Address 2 *)
PHONE: STRINGÆ15Å; (* Phone number *)
PEXTN: STRINGÆ5Å; (* Extension *)
RMRK1: STRINGÆ40Å; (* Remarks 1 *)
RMRK2: STRINGÆ40Å; (* Remarks 2 *)
RMRK3: STRINGÆ40Å; (* Ramarks 3 *)
END;
VAR
(* Global variables *)
DATF: DATAFILE;
IDXC,IDXN: INDEXFILE;
CH: CHAR;
(* CAP converts a character to upper case *)
FUNCTION CAP(CH: CHAR): CHAR;
BEGIN
IF (CH>='a') AND (CH<='z') THEN
CAP:=CHR(ORD(CH)-32) ELSE CAP:=CH;
END;
(* CPAS converts a string to upper case *)
FUNCTION CAPS(S: STR80): STR80;
VAR
P: INTEGER;
BEGIN
FOR P:=1 TO LEN(S) DO SÆPÅ:=CAP(SÆPÅ);
CAPS:=S;
END;
(* CSTR returns a string with N characters of value C *)
FUNCTION CSTR(C: CHAR; N: INTEGER): STR80;
VAR
S: STRINGÆ80Å;
BEGIN
IF N<0 THEN N:=0; SÆ0Å:=CHR(N); FILL(SÆ1Å,N,C);
CSTR:=S;
END;
(* BEEP sounds the terminal's bell or beeper *)
PROCEDURE BEEP;
BEGIN
WRITE(^G);
END;
(* INPSTR is a generalized string input routine which supports *)
(* on-screen editing. S is any string variable. L is the maxi- *)
(* mum input length. X and Y are the display coordinates. TERM *)
(* is a set of valid terminator characters, and TC returns the *)
(* character that terminated the input. On entry, S is display- *)
(* ed at X,Y on the screen, padded to the right with undersco- *)
(* res. The string may then be edited using standard control *)
(* keys: ^S and ^D for left and right, ^A and ^F for beginning *)
(* and end of line, ^G to delete the character under the cur- *)
(* sor, ^H or DEL to backspace, and ^Y to delete to end of the *)
(* line. When a character is typed, it is inserted at the cur- *)
(* sor at the remainder of the line is moved to the right. When *)
(* a terminator character is typed, the trailing underscors are *)
(* removed, and INPSTR returns with the edited string in S and *)
(* the terminator character in TC. *)
PROCEDURE INPSTR(VAR S: ANYSTR; L,X,Y: INTEGER;
TERM: CHARSET; VAR TC: CHAR);
CONST
FC = '_';
VAR
P: INTEGER;
CH: CHAR;
BEGIN
GOTOXY(X,Y); WRITE(S,CSTR(FC,L-LEN(S))); P:=0;
REPEAT
GOTOXY(X+P,Y); READ(KBD,CH);
CASE CH OF
@32..@126:
IF P<L THEN
BEGIN
IF LEN(S)=L THEN DELETE(S,L,1);
P:=P+1; INSERT(CH,S,P);
WRITE(COPY(S,P,L));
END ELSE BEEP;
^S: IF P>0 THEN P:=P-1 ELSE BEEP;
^D: IF P<LEN(S) THEN P:=P+1 ELSE BEEP;
^A: P:=0;
^F: P:=LEN(S);
^G:
IF P<LEN(S) THEN
BEGIN
DELETE(S,P+1,1); WRITE(COPY(S,P+1,L),FC);
END;
^H,@127:
IF P>0 THEN
BEGIN
DELETE(S,P,1); WRITE(^H,COPY(S,P,L),FC); P:=P-1;
END ELSE BEEP;
^Y:
BEGIN
WRITE(CSTR(FC,LEN(S)-P)); DELETE(S,P+1,L);
END;
OTHERWISE
IF NOT(CH IN TERM) THEN BEEP;
END;
UNTIL CH IN TERM;
P:=LEN(S); GOTOXY(X+P,Y); WRITE('':L-P);
TC:=CH;
END;
(* SELECT display the prompt string PR on line 22, and waits *)
(* until the user enters a character which is contained in the *)
(* TERM character set. The character is then returned in TC. *)
PROCEDURE SELECT(PR: STR80; TERM: CHARSET; VAR TC: CHAR);
VAR
CH: CHAR;
BEGIN
GOTOXY(0,22); WRITE(PR,'? ',CLREOL);
REPEAT
READ(KBD,CH); TC:=CAP(CH);
IF NOT(TC IN TERM) THEN BEEP;
UNTIL TC IN TERM;
WRITE(CH);
END;
(* CLRFRAME clears the display frame, i.e. lines 3 to 20 *)
PROCEDURE CLRFRAME;
VAR
I: INTEGER;
BEGIN
FOR I:=3 TO 20 DO
BEGIN
GOTOXY(0,I); WRITE(CLREOL);
END;
END;
(* OUTFORM displays the entry form on the screen *)
PROCEDURE OUTFORM;
BEGIN
GOTOXY(6,4); WRITE('Code:');
GOTOXY(28,4); WRITE('Date:');
GOTOXY(0,6); WRITE('First Name:');
GOTOXY(28,6); WRITE('Last Name:');
GOTOXY(3,8); WRITE('Company:');
GOTOXY(1,9); WRITE('Address 1:');
GOTOXY(1,10); WRITE('Address 2:');
GOTOXY(5,12); WRITE('Phone:');
GOTOXY(28,12); WRITE('Extension:');
GOTOXY(1,14); WRITE('Remarks 1:');
GOTOXY(1,15); WRITE('Remarks 2:');
GOTOXY(1,16); WRITE('Remarks 3:');
END;
(* CLRFORM clears all fields in the entry form *)
PROCEDURE CLRFORM;
BEGIN
GOTOXY(12,4); WRITE('':15); GOTOXY(34,4); WRITE(CLREOL);
GOTOXY(12,6); WRITE('':15); GOTOXY(39,6); WRITE(CLREOL);
GOTOXY(12,8); WRITE(CLREOL); GOTOXY(12,9); WRITE(CLREOL);
GOTOXY(12,10); WRITE(CLREOL); GOTOXY(12,12); WRITE('':15);
GOTOXY(39,12); WRITE(CLREOL); GOTOXY(12,14); WRITE(CLREOL);
GOTOXY(12,15); WRITE(CLREOL); GOTOXY(12,16); WRITE(CLREOL);
END;
(* INPCUST is used for inputting customer records. The routine *)
(* assumes that the entry form is already displayed and filled *)
(* in with the default data contained in CUSTREC. Apart from *)
(* the control characters supported by INPSTR, the user may *)
(* type ^E to move to the previous field, RETURN, ^I or ^X to *)
(* move to the next field. INPCUST returns when the user types *)
(* ^Z or RETURN when the cursor is in the last field. *)
PROCEDURE INPCUST(VAR CUST: CUSTREC);
CONST
TERM: CHARSET = Æ^E,^I,^M,^X,^ZÅ;
VAR
L: INTEGER;
TC: CHAR;
BEGIN
L:=1;
WITH CUST DO
REPEAT
CASE L OF
1: INPSTR(CCODE,15,12,4,TERM,TC);
2: INPSTR(CDATE,8,34,4,TERM,TC);
3: INPSTR(FNAME,15,12,6,TERM,TC);
4: INPSTR(LNAME,30,39,6,TERM,TC);
5: INPSTR(CMPNY,40,12,8,TERM,TC);
6: INPSTR(ADDR1,30,12,9,TERM,TC);
7: INPSTR(ADDR2,30,12,10,TERM,TC);
8: INPSTR(PHONE,15,12,12,TERM,TC);
9: INPSTR(PEXTN,5,39,12,TERM,TC);
10: INPSTR(RMRK1,40,12,14,TERM,TC);
11: INPSTR(RMRK2,40,12,15,TERM,TC);
12: INPSTR(RMRK3,40,12,16,TERM,TC);
END;
IF (TC=^I) OR (TC=^M) OR (TC=^X) THEN
IF L=12 THEN L:=1 ELSE L:=L+1 ELSE
IF TC=^E THEN
IF L=1 THEN L:=12 ELSE L:=L-1;
UNTIL (TC=^M) AND (L=1) OR (TC=^Z);
END;
(* OUTCUST displays the customer data contained in CUST *)
PROCEDURE OUTCUST(VAR CUST: CUSTREC);
BEGIN
WITH CUST DO
BEGIN
GOTOXY(12,4); WRITE(CCODE,'':15-LEN(CCODE));
GOTOXY(34,4); WRITE(CDATE,CLREOL);
GOTOXY(12,6); WRITE(FNAME,'':15-LEN(FNAME));
GOTOXY(39,6); WRITE(LNAME,CLREOL);
GOTOXY(12,8); WRITE(CMPNY,CLREOL);
GOTOXY(12,9); WRITE(ADDR1,CLREOL);
GOTOXY(12,10); WRITE(ADDR2,CLREOL);
GOTOXY(12,12); WRITE(PHONE,'':15-LEN(PHONE));
GOTOXY(39,12); WRITE(PEXTN,CLREOL);
GOTOXY(12,14); WRITE(RMRK1,CLREOL);
GOTOXY(12,15); WRITE(RMRK2,CLREOL);
GOTOXY(12,16); WRITE(RMRK3,CLREOL);
END;
END;
(* KEYN converts a last name and a first name to a key string. *)
(* The key string consists of the first 15 characters of the *)
(* last name (padded with blanks if required) followed by the *)
(* first 10 characters of the first name. *)
FUNCTION KEYN(LN: STR15; FN: STR10): STR25;
CONST
BLANKS = ' ';
BEGIN
KEYN:=CAPS(LN)+COPY(BLANKS,1,15-LEN(LN))+CAPS(FN);
END;
(* UPDATE is used to update the data base *)
PROCEDURE UPDATE;
VAR
CH: CHAR;
(* ADD is used to add customers *)
PROCEDURE ADD;
VAR
D: INTEGER;
CC: STRINGÆ15Å;
KN: STRINGÆ25Å;
CUST: CUSTREC;
BEGIN
WITH CUST DO
BEGIN
FILL(CUST,SIZE(CUST),0);
REPEAT
INPCUST(CUST); CC:=CCODE; FINDKEY(IDXC,D,CC);
IF OK THEN
BEGIN
GOTOXY(5,18);
WRITE('ERROR: DUPLICATE CUSTOMER CODE'); BEEP;
END;
UNTIL NOT OK;
ADDREC(DATF,D,CUST); ADDKEY(IDXC,D,CCODE);
KN:=KEYN(LNAME,FNAME); ADDKEY(IDXN,D,KN);
GOTOXY(5,18); WRITE(CLREOL);
END;
END;
(* FIND is used to find, edit and delete customers *)
PROCEDURE FIND;
VAR
D,L,I: INTEGER;
CH,TC: CHAR;
CC,PC,FN: STRINGÆ15Å;
KN,PN: STRINGÆ25Å;
LN: STRINGÆ30Å;
CUST: CUSTREC;
BEGIN
IF USEDRECS(DATF)>0 THEN
BEGIN
CC:='';
REPEAT
INPSTR(CC,15,12,4,Æ^M,^ZÅ,TC);
IF CC<>'' THEN
BEGIN
FINDKEY(IDXC,D,CC);
IF OK THEN
BEGIN
GETREC(DATF,D,CUST); OUTCUST(CUST);
END ELSE
BEGIN
GOTOXY(5,18);
WRITE('ERROR: CUSTOMER CODE NOT FOUND'); BEEP;
END;
END;
UNTIL OK OR (CC='');
GOTOXY(5,18); WRITE(CLREOL);
IF CC='' THEN
BEGIN
L:=1; FN:=''; LN:='';
REPEAT
CASE L OF
1: INPSTR(FN,15,12,6,Æ^I,^M,^ZÅ,TC);
2: INPSTR(LN,30,39,6,Æ^I,^M,^ZÅ,TC);
END;
IF (TC=^I) OR (TC=^M) THEN L:=3-L;
UNTIL (TC=^M) AND (L=1) OR (TC=^Z);
KN:=KEYN(LN,FN); SEARCHKEY(IDXN,D,KN);
IF NOT OK THEN PREVKEY(IDXN,D,KN);
REPEAT
GETREC(DATF,D,CUST); OUTCUST(CUST);
SELECT('Find: N)ext, P)revious, Q)uit',Æ'N','P','Q'Å,CH);
CASE CH OF
'N': REPEAT NEXTKEY(IDXN,D,KN) UNTIL OK;
'P': REPEAT PREVKEY(IDXN,D,KN) UNTIL OK;
END;
UNTIL CH='Q';
END;
SELECT('Find: E)dit, D)elete, Q)uit',Æ'E','D','Q'Å,CH);
WITH CUST DO
CASE CH OF
'E':
BEGIN
PC:=CCODE; PN:=KEYN(LNAME,FNAME);
REPEAT
INPCUST(CUST);
IF CCODE=PC THEN OK:=FALSE ELSE
BEGIN
CC:=CCODE; FINDKEY(IDXC,I,CC); IF OK THEN BEEP;
END;
UNTIL NOT OK;
PUTREC(DATF,D,CUST);
IF CCODE<>PC THEN
BEGIN
DELETEKEY(IDXC,D,PC); ADDKEY(IDXC,D,CCODE);
END;
KN:=KEYN(LNAME,FNAME);
IF KN<>PN THEN
BEGIN
DELETEKEY(IDXN,D,PN); ADDKEY(IDXN,D,KN);
END;
END;
'D':
BEGIN
DELETEKEY(IDXC,D,CCODE);
KN:=KEYN(LNAME,FNAME); DELETEKEY(IDXN,D,KN);
DELETEREC(DATF,D);
END;
END;
END ELSE BEEP;
END;
BEGIN (*UPDATE*)
OUTFORM;
REPEAT
SELECT('Update: A)dd, F)ind, Q)uit',Æ'A','F','Q'Å,CH);
CASE CH OF
'A': ADD;
'F': FIND;
END;
IF CH<>'Q' THEN
BEGIN
GOTOXY(59,1); WRITE(USEDRECS(DATF):5); CLRFORM;
END;
UNTIL CH='Q';
END;
(* LIST is used to list customers *)
PROCEDURE LIST;
LABEL ESCAPE;
VAR
D,L,LD: INTEGER;
CH,CO,CS: CHAR;
CC: STRINGÆ15Å;
KN: STRINGÆ25Å;
NAME: STRINGÆ35Å;
CUST: CUSTREC;
BEGIN
SELECT('Output Device: P)rinter, S)creen',Æ'P','S'Å,CO);
SELECT('Sort By: C)ode, N)ame, U)nsorted',Æ'C','N','U'Å,CS);
GOTOXY(0,22); WRITE('Press <ESC> to abort',CLREOL);
CLEARKEY(IDXC);
CLEARKEY(IDXN);
D:=0; LD:=FILELEN(DATF)-1; L:=3;
REPEAT
IF KEYPRESS THEN
BEGIN
READ(KBD,CH); IF CH=@27 THEN GOTO ESCAPE;
END;
CASE CS OF
'C': NEXTKEY(IDXC,D,CC);
'N': NEXTKEY(IDXN,D,KN);
'U':
BEGIN
OK:=FALSE;
WHILE (D<LD) AND NOT OK DO
BEGIN
D:=D+1; GETREC(DATF,D,CUST);
OK:=CUST.STATUS=0;
END;
END;
END;
IF OK THEN WITH CUST DO
BEGIN
IF CS<>'U' THEN GETREC(DATF,D,CUST);
NAME:=LNAME;
IF FNAME<>'' THEN NAME:=NAME+', '+FNAME;
IF CO='P' THEN
BEGIN
WRITE(LST,CCODE,'':16-LEN(CCODE));
WRITE(LST,NAME,'':36-LEN(NAME));
WRITELN(LST,COPY(CMPNY,1,25));
END ELSE
BEGIN
IF L=21 THEN
BEGIN
GOTOXY(0,22);
WRITE('Press <RETURN> to continue or <ESC> to abort',CLREOL);
REPEAT READ(KBD,CH) UNTIL (CH=^M) OR (CH=@27);
IF CH=@27 THEN GOTO ESCAPE;
GOTOXY(0,22); WRITE('Press <ESC> to abort',CLREOL);
CLRFRAME; L:=3;
END;
GOTOXY(0,L); WRITE(CCODE);
GOTOXY(16,L); WRITE(NAME);
GOTOXY(52,L); WRITE(COPY(CMPNY,1,25));
L:=L+1;
END;
END;
UNTIL NOT OK;
IF CO='S' THEN
BEGIN
GOTOXY(0,22); WRITE('Press <RETURN>',CLREOL);
REPEAT READ(KBD,CH) UNTIL CH=^M;
END;
ESCAPE:
END;
(* Main program *)
BEGIN
WRITELN(CLRHOM,CSTR('-',79));
WRITELN('C-FILE Customer Data Base');
WRITELN(CSTR('-',79));
GOTOXY(0,21); WRITELN(CSTR('-',79));
WRITELN; WRITE(CSTR('-',79)); GOTOXY(0,3);
INITINDEX;
OPENFILE(DATF,'CUST.DAT',CRSIZE);
IF OK THEN OPENINDEX(IDXC,'CUST.IXC',15,0);
IF OK THEN OPENINDEX(IDXN,'CUST.IXN',25,1);
IF NOT OK THEN
BEGIN
SELECT('Data files missing. Create new files (Y/N)',Æ'Y','N'Å,CH);
IF CH='Y' THEN
BEGIN
MAKEFILE(DATF,'CUST.DAT',CRSIZE);
MAKEINDEX(IDXC,'CUST.IXC',15,0);
MAKEINDEX(IDXN,'CUST.IXN',25,1);
END ELSE
GOTO STOP;
END;
GOTOXY(59,1); WRITE(USEDRECS(DATF):5,' Records In Use');
REPEAT
SELECT('Select: U)pdate, L)ist, Q)uit',Æ'U','L','Q'Å,CH);
CASE CH OF
'U': UPDATE;
'L': LIST;
END;
IF CH<>'Q' THEN CLRFRAME;
UNTIL CH='Q';
CLOSEFILE(DATF);
CLOSEINDEX(IDXC);
CLOSEINDEX(IDXN);
STOP:
WRITE(CLRHOM);
END.
«eof»