summaryrefslogtreecommitdiff
path: root/v4.0/src/SELECT/ROUTINE2.ASM
blob: 2a8b7ad0c8d0a1ac3b22f4457f3e00ca502a1e30 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
;***************************************************************************
; Subroutines which are called by the macros in MACROS.INC.
; File: ROUTINE2.ASM
; Latest Change Date: August 04, 1987
;
; This is a stand alone module and is meant to be linked with the calling
; program.
;
;***************************************************************************
.ALPHA						 ;AN000;
;**********************************************************************
DATA	SEGMENT BYTE PUBLIC 'DATA'               ;AN000;

PATH_STRING		DW	  0		 ;AN000;
STRING_SIZE		DW	  0		 ;AN000;
PATH_PTR		DW	  0		 ;AN000;
PATH_SIZE		DW	  0		 ;AN000;
MAX_CHAR		DW	  0		 ;AN000;
CHAR_COUNT		DW	  0		 ;AN000;


SEARCH_FLAG		DB	  0		 ;AN000;

    PERIOD		EQU	  00000001B	 ;AN000;
    SLASH_FOUND 	EQU	  00000010B	 ;AN000;


    RESET_PERIOD	EQU	  11111110B	 ;AN000;
    RESET_SLASH_FOUND	EQU	  11111101B	 ;AN000;

INVALID_STRING		DB	  '"/\[]:|<>+=;, ' ;AN000;
			END_INVALID_STRING  EQU  $ ;AN000;
SIZE_INVALID_STR	EQU	  END_INVALID_STRING - INVALID_STRING ;AN000;

ZEROED_CHAR		DB	  0	      ;AN000;
			DB	  0	      ;AN000;

SEP_POSITION		DW	  0	      ;AN000;
NUM_PATHS		DW	  0	      ;AN000;


ERR_INVALID_DRV 	EQU	  1	      ;AN000;
ERR_NO_DRIVE		EQU	  2	      ;AN000;
ERR_DRIVE		EQU	  3	      ;AN000;
ERR_LEADING_SLASH	EQU	  4	      ;AN000;
ERR_NO_SLASH		EQU	  5	      ;AN000;
ERR_LAST_SLASH		EQU	  6	      ;AN000;
ERR_INVALID_CHAR	EQU	  7	      ;AN000;

OLD_ATTRIB		DW	  0	      ;AN000;
NEW_ATTRIB		DW	  0	      ;AN000;
WAY			DW	  0	      ;AN000;



PUBLIC	      CHK_W_PROTECT_FLAG	      ;AN000;
PUBLIC	      W_PROTECT_FLAG		      ;AN000;

W_P_FILENAME_A		DB	  'A:\',12 DUP(0), 0    ;AC000;JW
W_P_FILENAME_B		DB	  'B:\',12 DUP(0), 0    ;AN000;JW
CHK_W_PROTECT_FLAG	DB	  0			;AN000;
W_PROTECT_FLAG		DB	  0			;AN000;
DRIVE_FLAG		DB	  ?			;AN000;JW


NUM_FILES		DW	  0			;AN000;
LIST_TYPE		DW	  0			;AN000;
STR_PTR 		DW	  0			;AN000;
FILE_PTR		DW	  0			;AN000;



DATA	       ENDS				 ;AN000; DATA
;**********************************************************************
						 ;
	.XLIST					 ;AN000;
	INCLUDE STRUC.INC			 ;AN000;
	INCLUDE MACROS.INC			 ;AN000;
	INCLUDE VARSTRUC.INC			 ;AN000;
	INCLUDE EXT.INC 			 ;AN000;
	INCLUDE MAC_EQU.INC			 ;AN000;
	EXTRN	EXIT_DOS:FAR			 ;AN000;
	EXTRN	POS_ZERO:FAR			 ;AN000;
	EXTRN	HOOK_INT_24:FAR 		 ;AN000;
	EXTRN	RESTORE_INT_24:FAR		 ;AN000;
	EXTRN	GGET_STATUS:FAR 		 ;AN000;
	.LIST					 ;AN000;
						 ;
						 ;
;**********************************************************************
CODE_FAR    SEGMENT PARA PUBLIC 'CODE'           ;AN000; Segment for far routine
	ASSUME	CS:CODE_FAR,DS:DATA		 ;AN000;
						 ;
;********************************************************************************
; CHECK_DOS_PATH_ROUTINE: Check to see if the sepecified path for the DOS
;	      SET PATH command is valid.
;
; INPUT:
;    SI = Points to an ASCII-N string containing the path to check.  There sould
;	  be an extra byte following the string to facilitate changing the string
;	  into an ASCII-Z string.
;
; OUTPUT:
;    If CY = 0, the path is valid.
;    If CY = 1, The path is NOT valid:
;
;********************************************************************************
PUBLIC	 CHECK_DOS_PATH_ROUTINE 		 ;AN000;
CHECK_DOS_PATH_ROUTINE	PROC FAR		 ;AN000;
						 ;
    MOV  PATH_PTR, SI				 ;AN000; Get the pointer from the path
    MOV  AX, [SI]				 ;AN000; Get the lenth of the path string
    .IF < AX EQ 0 >				 ;AN000; If the length is zero then return that
	 JMP  NO_ERROR_DOS_PATH 		 ;AN000;    the path is valid.
    .ENDIF					 ;AN000;
    MOV  PATH_SIZE, AX				 ;AN000; Save the size of the string
    ADD  SI, 2					 ;AN000; Adjust path pointer for length word
						 ;
    .REPEAT					 ;AN000; Check all the path names in the string
	 MOV  AL, ';'                            ;AN000; separator between filenames
	 MOV  CX, PATH_PTR			 ;AN000; Get the pointer to the path
	 ADD  CX, 2				 ;AN000; Point to the start of the string
	 ADD  CX, PATH_SIZE			 ;AN000; Add the size of the path
	 SUB  CX, SI				 ;AN000; Subtract current pointer - Get length of string remaining
	 CALL ISOLATE_NEXT_PATH 		 ;AN000; Make the next path name into an ASCII-Z string
	 PUSH SEP_POSITION			 ;AN000; Save the position of the path seperator
	 PUSH WORD PTR ZEROED_CHAR		 ;AN000; Save the character that was made into a zero
	 MOV  CX, SEP_POSITION			 ;AN000;
	 SUB  CX, SI				 ;AN000; Get the length of the string
	 MOV  AX, 0101H 			 ;AN000;
	 CALL FAR PTR CHECK_VALID_PATH		 ;AN000; Check if it is a valid filename
	 POP  WORD PTR ZEROED_CHAR		 ;AN000;
	 POP  SEP_POSITION			 ;AN000;
	 CALL RESTORE_SEPARATOR 		 ;AN000; Restore the character between the path names
	 .IF < C >				 ;AN000; Was the file name not valid?
	      JMP  ERROR_DOS_PATH		 ;AN000; Exit the subroutine
	 .ENDIF 				 ;AN000;
	 MOV  SI, DI				 ;AN000; Get the pointer to the next path name
    .UNTIL < ZERO SI >				 ;AN000; If zero, all path names have been examined.
NO_ERROR_DOS_PATH:				 ;AN000;
    CLC 					 ;AN000;
    JMP  EXIT_DOS_PATH				 ;AN000;
ERROR_DOS_PATH: 				 ;AN000;
    STC 					 ;AN000;
EXIT_DOS_PATH:					 ;AN000;
    RET 					 ;AN000;
						 ;
CHECK_DOS_PATH_ROUTINE	ENDP			 ;AN000;
						 ;
						 ;
PUBLIC	 CHECK_VALID_PATH			 ;AN000;
;********************************************************************************
; CHECK_VALID_PATH: Check to see if the sepecified path is valid.
;
; INPUT:
;    SI = Points to an ASCII-Z string containing the path to check.  There sould
;	  be an extra byte following the string to facilitate changing the string
;	  into an ASCII-Z string.
;
;   CX = The size of the string containing the path.  The zero byte at the end
;	 of the string is NOT included in the length.
;
;    AL = 0: Drive letter cannot be specified.
;	= 1: Drive letter is optional and can be specified.
;	= 2: Drive letter must be specified.
;
;    AH = 0: First non-drive character cannot be a backslash ('\')
;	= 1: First non-drive character may be a backslash ('\')
;	= 2: First non-drive character must be a backslash ('\')
;
; OUTPUT:
;    If CY = 0, the path is valid.
;    If CY = 1, The path is NOT valid:
;	  AX = 1, The drive specified is invalid.
;	     = 2, There was no drive specified.
;	     = 3, There was a drive specified.
;	     = 4, There was a leading backslash
;	     = 5, The leading backslash was NOT present.
;	     = 6, There was a trailing backslash.
;
;********************************************************************************
CHECK_VALID_PATH   PROC FAR			 ;AN000;
						 ;
    PUSH DI					 ;AN000;
    MOV  STRING_SIZE, CX			 ;AN000; Save the size of the string
    MOV  PATH_STRING, SI			 ;AN000; Save the pointer to the string
    CALL CHECK_VALID_DRIVE			 ;AN000; See if there is a valid drive
    .IF < C >					 ;AN000; Is the drive specified invalid?
	 MOV  AX, ERR_INVALID_DRV		 ;AN000; Return this error code
	 JMP  EXIT_CHK_DRV			 ;AN000; Exit the subroutine
    .ENDIF					 ;AN000;
    .IF < BX EQ 0 >				 ;AN000; No drive sepecified?
	 .IF < AL EQ 2 >			 ;AN000; Must the drive be specified?
	      MOV  AX, ERR_NO_DRIVE		 ;AN000; Return this error code
	      JMP  EXIT_CHK_DRV 		 ;AN000; Exit the subroutine
	 .ENDIF 				 ;AN000;
    .ELSE					 ;AN000; Otherwise, the drive WAS specified.
	 .IF < AL EQ 0 >			 ;AN000; The drive cannot be specified
	      MOV  AX, ERR_DRIVE		 ;AN000; Return this error code
	      JMP  EXIT_CHK_DRV 		 ;AN000; Exit the subroutine
	 .ENDIF 				 ;AN000;
	 ADD  SI, 2				 ;AN000; Push pointer past the drive
    .ENDIF					 ;AN000;
    .IF < <BYTE PTR [SI]> EQ '\' >               ;AN000; Is the next byte a backslash?
	 .IF < AH EQ 0 >			 ;AN000; Is one permitted?
	      MOV  AX, ERR_LEADING_SLASH	 ;AN000; No! Return this error code
	      JMP  EXIT_CHK_DRV 		 ;AN000; Exit the subroutine
	 .ELSE					 ;AN000; Otherwise, one allowed.
	      INC  SI				 ;AN000; Push pointer past \
	 .ENDIF 				 ;AN000;
    .ELSE					 ;AN000; Otherwise, byte not a backslash
	 .IF < AH EQ 2 >			 ;AN000; Was one required?
	      MOV  AX, ERR_NO_SLASH		 ;AN000; If so, return this error code
	      JMP  EXIT_CHK_DRV 		 ;AN000; Exit from this subroutine
	 .ENDIF 				 ;AN000;
    .ENDIF					 ;AN000;
						 ;
    MOV  NUM_PATHS, 0				 ;AN000;
    .REPEAT					 ;AN000; Check all the path names in the string
	 MOV  AL, '\'                            ;AN000; Separator between filenames
	 MOV  CX, PATH_STRING			 ;AN000;
	 ADD  CX, STRING_SIZE			 ;AN000;
	 SUB  CX, SI				 ;AN000;
	 .IF < NUM_PATHS EQ 0 > AND		 ;AN000; If this is the first path checked...and
	 .IF < CX EQ 0 >			 ;AN000; If the length of the path is zero...
	      JMP  EXIT_NO_ERROR		 ;AN000; Exit with no error
	 .ENDIF 				 ;AN000;
	 CALL ISOLATE_NEXT_PATH 		 ;AN000; Make the next path name into an ASCII-Z string
	 CALL CHECK_VALID_FILENAME		 ;AN000; Check if it is a valid filename
	 CALL RESTORE_SEPARATOR 		 ;AN000; Restore the character between the path names
	 .IF < C >				 ;AN000; Was the file name not valid?
	      .IF < NUM_PATHS EQ 0 >		 ;AN000;
		   .LEAVE			 ;AN000;
	      .ELSE				 ;AN000;
		   MOV	AX, ERR_INVALID_CHAR	 ;AN000; If not, return this error code
		   JMP	EXIT_CHK_DRV		 ;AN000; Exit the subroutine
	      .ENDIF				 ;AN000;
	 .ENDIF 				 ;AN000;
	 MOV  SI, DI				 ;AN000; Get the pointer to the next path name
    .UNTIL < ZERO SI >				 ;AN000; If zero, all path names have been examined.
						 ;
    MOV  SI, PATH_STRING			 ;AN000; Get the pointer to the whole string
    ADD  SI, STRING_SIZE			 ;AN000; Add the string length
    DEC  SI					 ;AN000; Point to the last character in the string
    .IF < <BYTE PTR [SI]> EQ '\'>                ;AN000; Is the last character a \ ?
	 MOV  AX, ERR_LAST_SLASH		 ;AN000; If so, return this error code
	 JMP  EXIT_CHK_DRV			 ;AN000; Exit from the subroutine
    .ENDIF					 ;AN000;
EXIT_NO_ERROR:					 ;AN000;
    CLC 					 ;AN000; Indicate there were no errors
    JMP  EXIT_CHECK_PATH			 ;AN000;
						 ;
EXIT_CHK_DRV:					 ;AN000;
    STC 					 ;AN000; Indicate that there were errors
EXIT_CHECK_PATH:				 ;AN000;
    POP  DI					 ;AN000;
    RET 					 ;AN000;
						 ;
						 ;
CHECK_VALID_PATH   ENDP 			 ;AN000;
;********************************************************************************
; CHECK_VALID_DRIVE: Check to see if there is a drive specified on the path and
;	       if there is, is it valid.
;
; INPUT:
;    SI - Points to a string containing the path to search.
;
; OUTPUT:
;    If CY = 1, the drive is specified and is invalid
;    If CY = 0, The drive might be specified and is valid
;	  BX = 0: The drive is NOT specified.
;	     = 1: The drive IS specified.
;
;
;********************************************************************************
CHECK_VALID_DRIVE  PROC NEAR			 ;AN000;
						 ;
    PUSH AX					 ;AN000; Push all registers used
    .IF < <BYTE PTR [SI+1]> EQ ':' >             ;AN000; Is the second character in the string a ':'
	 MOV  AL, [SI]				 ;AN000; If so, get the first character
	 .IF < AL AE 'A' > AND                   ;AN000; Is it a capital letter?
	 .IF < AL BE 'Z' >                       ;AN000;
	      CLC				 ;AN000; If so, drive valid.
	      MOV  BX, 1			 ;AN000; Indicate the drive exists
	 .ELSEIF < AL AE 'a' > AND               ;AN000; Else, is the drive a lowercase letter?
	 .IF < AL BE 'z' >                       ;AN000;
	      CLC				 ;AN000; If so, the drive is valid
	      MOV  BX, 1			 ;AN000; Indicate that the drive exists
	 .ELSE					 ;AN000; Otherwise...
	      STC				 ;AN000; The drive is not valid
	 .ENDIF 				 ;AN000;
    .ELSE					 ;AN000;
	 CLC					 ;AN000; Indicate there were no errors
	 MOV  BX, 0				 ;AN000; The drive does not exist
    .ENDIF					 ;AN000;
    POP  AX					 ;AN000;
						 ;
    RET 					 ;AN000;
						 ;
CHECK_VALID_DRIVE  ENDP 			 ;AN000;
;********************************************************************************
; CHECK_VALID_FILENAME: Check to see if a filename is valid.
;
; INPUT:
;    SI - Points to an ASCII-Z string containing the filename to examine.
;
; OUTPUT:
;    If CY = 1, The filename is NOT valid.
;    If CY = 0, the filename IS valid.
;
;
;********************************************************************************
CHECK_VALID_FILENAME	PROC NEAR		 ;AN000;
						 ;
    INC  NUM_PATHS				 ;AN000;
    AND  SEARCH_FLAG, RESET_PERIOD		 ;AN000; Indicate no periods have been found yet
    MOV  MAX_CHAR, 8				 ;AN000; Up to 8 characters can be specified
    MOV  CHAR_COUNT, 0				 ;AN000; Number of character so far
    MOV  AL, [SI]				 ;AN000; Get the first character in the string
    .WHILE < AL NE 0 >				 ;AN000; Repeat untill we reach the string's end
	 INC  CHAR_COUNT			 ;AN000; Increment number of characters in path
	 MOV  BX, CHAR_COUNT			 ;AN000;
	 .IF < BX A MAX_CHAR > AND		 ;AN000;
	 .IF < AL NE '.' >                       ;AN000;
	      JMP  INVALID_CHAR 		 ;AN000;
	 .ENDIF 				 ;AN000;
	 .IF < AL B 20 >			 ;AN000; Is the character's code less than 20?
	      JMP  INVALID_CHAR 		 ;AN000; If so, it's invalid
	 .ELSE					 ;AN000; Otherwise...
	      CALL VALID_CHAR			 ;AN000; See if it's invalid
	      .IF < C > 			 ;AN000; If so,
		   JMP	INVALID_CHAR		 ;AN000; Exit the subroutine
	      .ENDIF				 ;AN000;
	 .ENDIF 				 ;AN000;
	 .IF < AL EQ '.' >                       ;AN000; Is the character a period?
	      .IF < BIT SEARCH_FLAG AND PERIOD > ;AN000; Is this the first one?
		   JMP	INVALID_CHAR		 ;AN000; If not, filename is invalid.
	      .ELSE				 ;AN000; Otherwise...
		   OR	SEARCH_FLAG, PERIOD	 ;AN000; Indicate that ONE has been found
		   .IF < CHAR_COUNT EQ 1 >	 ;AN000; Were there any characters before the period
			JMP  INVALID_CHAR	 ;AN000; If not, this is an invalid path
		   .ENDIF			 ;AN000;
		   MOV	MAX_CHAR, 3		 ;AN000; Allow three characters after the period
		   MOV	CHAR_COUNT, 0		 ;AN000; No characters yet
	      .ENDIF				 ;AN000;
	 .ENDIF 				 ;AN000;
	 INC  SI				 ;AN000; Point to next character
	 MOV  AL, [SI]				 ;AN000; Get that character
    .ENDWHILE					 ;AN000;
    .IF < CHAR_COUNT EQ 0 > AND 		 ;AN000;
    .IF < MAX_CHAR   EQ 8 >			 ;AN000;
	 DEC  NUM_PATHS 			 ;AN000;
	 JMP  INVALID_CHAR			 ;AN000;
    .ENDIF					 ;AN000;
    CLC 					 ;AN000; Indicate the name is valid
    JMP  CK_V_FILENAME				 ;AN000; Exit.
						 ;
INVALID_CHAR:					 ;AN000; Indicate that the name is not valid
    STC 					 ;AN000;
CK_V_FILENAME:					 ;AN000;
    RET 					 ;AN000;
						 ;
CHECK_VALID_FILENAME	ENDP			 ;AN000;
;********************************************************************************
; VALID_CHAR: Determine if a character is valid for a filename.
;
; INPUT:
;    AL = The character to check.
;
; OUTPUT:
;    If CY = 1, the character is not valid.
;    If CY = 0, the character IS valid.
;
;********************************************************************************
VALID_CHAR    PROC NEAR 			 ;AN000;
						 ;
    PUSH CX					 ;AN000; Save the registers used.
    PUSH DI					 ;AN000;
    PUSH ES					 ;AN000;
						 ;
    MOV  DI, OFFSET INVALID_STRING		 ;AN000; Get the address of string containing invalid characters
    PUSH DS					 ;AN000; Save the data segment
    POP  ES					 ;AN000; Make ES=DS
    MOV  CX, SIZE_INVALID_STR			 ;AN000; Get the size of the string
    CLD 					 ;AN000; Scan forward
    REPNZ     SCASB				 ;AN000; See if this character is in the invalid string
    .IF < Z >					 ;AN000; If so,
	 STC					 ;AN000; Indicate the character is invalid
    .ELSE					 ;AN000; Otherwise...
	 CLC					 ;AN000; Indicate the character is valid
    .ENDIF					 ;AN000;
    POP  CX					 ;AN000; Restore the registers
    POP  DI					 ;AN000;
    POP  ES					 ;AN000;
    RET 					 ;AN000;
						 ;
VALID_CHAR    ENDP				 ;AN000;
;********************************************************************************
; ISOLATE_NEXT_PATH: Search the filename for a '\'.  If found, it is replaced
;	  by a zero making the string into an ASCII-Z string.
;
; INPUT:
;    SI - Points to the first character in the path string
;    AL - Contains the character to search for
;    CX - Contains the length of the string
;
; OUTPUT:
;    DI - Points to the character following the next '\'
;    If this character is the last path element, DI = 0.
;
;    ZEROED_CHAR is loaded with the character which is made into a zero.
;
;********************************************************************************
ISOLATE_NEXT_PATH  PROC NEAR			 ;AN000;
						 ;
    PUSH AX					 ;AN000; Save registers used.
    PUSH BX					 ;AN000;
    PUSH CX					 ;AN000;
    PUSH SI					 ;AN000;
						 ;
    PUSH ES					 ;AN000; Make ES = DS
    PUSH DS					 ;AN000;
    POP  ES					 ;AN000;
    MOV  DI, SI 				 ;AN000; Copy the string pointer
						 ; CX holds the length of string after the pointer DI
						 ; AL holds the character to search for
    CLD 					 ;AN000; Search in the forward direction
    REPNZ     SCASB				 ;AN000; Search...
    JNZ  END_FOUND				 ;AN000; If NZ, we reached the string's end
    MOV  ZEROED_CHAR, AL			 ;AN000; Character overwritten with zero
    MOV  SEP_POSITION, DI			 ;AN000; Save the position of overwritten char
    DEC  SEP_POSITION				 ;AN000;
    MOV  BYTE PTR [DI-1], 0			 ;AN000; Make the character a zero
    CMP  CX,0					 ;AN031; SEH  User may have entered semicolon as last char, so check
    JE	 END_FOUND2				 ;AN031; SEH	   if it is last char instead of just a separator
    JMP  EXIT_ISOLATE				 ;AN000; Exit the subroutine
END_FOUND:					 ;AN000;
    MOV  AL, [DI]				 ;AN000; Get the last character
    MOV  ZEROED_CHAR, AL			 ;AN000; Save it.
    MOV  SEP_POSITION, DI			 ;AN000; Save its position
    MOV  BYTE PTR [DI], 0			 ;AN000; Make into a zero
END_FOUND2:					 ;AN031; SEH  Handle case of semicolon as last character in path
    MOV  DI, 0					 ;AN000; Indicate the string is finished
EXIT_ISOLATE:					 ;AN000;
    POP  ES					 ;AN000; Restore the registers.
    POP  SI					 ;AN000;
    POP  CX					 ;AN000;
    POP  BX					 ;AN000;
    POP  AX					 ;AN000;
						 ;
    RET 					 ;AN000;
						 ;
ISOLATE_NEXT_PATH  ENDP 			 ;AN000;
;********************************************************************************
; RESTORE_SEPARATOR: Restore the character which separates the characters in
;	  a path.
;
; INPUT:
;    SEP_POSITION - Contain the address of the location to restore the separator.
;    ZEROED_CHAR  - Contains the character to be restored.
;
; OUTPUT:
;    None.
;
;********************************************************************************
RESTORE_SEPARATOR  PROC NEAR			 ;AN000;
						 ;
    PUSH AX					 ;AN000; Save registers used
    PUSH SI					 ;AN000;
    MOV  SI, SEP_POSITION			 ;AN000; Get the position of the character
    MOV  AL, ZEROED_CHAR			 ;AN000; Get the character
    MOV  [SI], AL				 ;AN000; Save character in this position
    POP  SI					 ;AN000; Restore the registers
    POP  AX					 ;AN000;
    RET 					 ;AN000;
						 ;
RESTORE_SEPARATOR  ENDP 			 ;AN000;
;********************************************************************************
; CHANGE_ATTRIBUTE_ROUTINE:  Change the attributes on a group of files.
;
; INPUT:
;    SI = The address of a list of files to change the attributes of.
;    AX = 0: Attach a new attribute to the file.
;    AX = 1: Restore the original attribute to the files.
;    BX = The number of files in the list.
;
; OUTPUT:
;    If CY = 1, there were error encountered.
;    If CY = 0, there were no errors.
;
;********************************************************************************
PUBLIC	 CHANGE_ATTRIBUTE_ROUTINE		 ;AN000;
CHANGE_ATTRIBUTE_ROUTINE     PROC FAR		 ;AN000;
						 ;
    CALL HOOK_INT_24				 ;AN000;
						 ;
    MOV  WAY, AX				 ;AN000; Save flag indicating whether we are setting or restoring the attrb.
    MOV  NEW_ATTRIB, 02h			 ;AN000; Set new attribute to hidden.
    MOV  DI, 0					 ;AN000; Count of files processed
    .WHILE < DI B BX >				 ;AN000;
	 .IF < WAY EQ 0 >			 ;AN000; Setting the attribute?
	      MOV  WORD PTR [SI+12],0		 ;AN000; Make filename into a ASCII-Z string
	      MOV  DX, SI			 ;AN000; Load address of filename into DX
	      MOV  AX, 4300H			 ;AN000; Get the file's current attribute
	      DOSCALL				 ;AN000;
	      .IF < C > 			 ;AN000; Was there an error?
		   JMP	CHMOD_ERROR		 ;AN000; If so, exit the subroutine
	      .ENDIF				 ;AN000;
	      MOV  OLD_ATTRIB, CX		 ;AN000; Save the attribute
	      MOV  CX, NEW_ATTRIB		 ;AN000; Get the new attribute
	 .ELSE					 ;AN000; Otherwise, we are restoring the attribute
	      MOV  CX, [SI+12]			 ;AN000; Get the old attribute
	      MOV  OLD_ATTRIB, CX		 ;AN000; Save.
	      MOV  WORD PTR [SI+12], 0		 ;AN000; Make filename into an ASCII-Z string
	 .ENDIF 				 ;AN000;
	 MOV  DX, SI				 ;AN000; Pointer to the filename
	 MOV  AX, 4301H 			 ;AN000; DOS function for setting the attribute
	 DOSCALL				 ;AN000; Set it.
	 .IF < C >				 ;AN000; Was there an error?
	      JMP  CHMOD_ERROR			 ;AN000; If so, exit the subroutine
	 .ENDIF 				 ;AN000;
	 MOV  CX, OLD_ATTRIB			 ;AN000; Get the old attribute
	 MOV  [SI+12], CX			 ;AN000; Save in the table
	 ADD  SI, 14				 ;AN000; Point to the next filename
	 INC  DI				 ;AN000; Increment count of files processed
    .ENDWHILE					 ;AN000;
    CLC 					 ;AN000; Indicate there were no errors
    RET 					 ;AN000;
						 ;
CHMOD_ERROR:					 ;AN000;
    STC 					 ;AN000; Indicate there were errors
						 ;
    CALL RESTORE_INT_24 			 ;AN000;
						 ;
    RET 					 ;AN000;
						 ;
CHANGE_ATTRIBUTE_ROUTINE     ENDP		 ;AN000;
;****************************************************************************
;
;   COMPARE_ROUTINE:  Compare two strings.
;
;   INPUT:
;	SI = The address of the first string. (ASCII-N string)
;	DI = The address of the second string. (ASCII-N string)
;
;   OUTPUT:
;	If CY = 1, the strings do no compare.
;	If CY = 0, the strings are the same.
;
;   OPERATION:
;
;****************************************************************************
PUBLIC	 COMPARE_ROUTINE			 ;AN000;
COMPARE_ROUTINE    PROC FAR			 ;AN000;
						 ;
    PUSH ES					 ;AN000; Make ES = DS
    PUSH DS					 ;AN000;
    POP  ES					 ;AN000;
						 ;
    MOV  CX, [SI]				 ;AN000; Get the length of the first string
    .IF < [DI] NE CX >				 ;AN000; Are the lengths of the strings the same?
	 JMP  DO_NOT_COMPARE			 ;AN000; If not, the strings are not the same
    .ENDIF					 ;AN000;
    ADD  SI, 2					 ;AN000; Move points past the length words
    ADD  DI, 2					 ;AN000;
    CLD 					 ;AN000; Compare in the forward direction
    REPZ CMPSB					 ;AN000; Compare the strings
    JNZ  DO_NOT_COMPARE 			 ;AN000; If the zero flag cleared, strings are not the same
    CLC 					 ;AN000; Indicate the strings do compare
    JMP  EXIT_COMPARE				 ;AN000;
DO_NOT_COMPARE: 				 ;AN000;
    STC 					 ;AN000; Indicate the strings do no compare
EXIT_COMPARE:					 ;AN000;
    POP  ES					 ;AN000;
    RET 					 ;AN000;
						 ;
COMPARE_ROUTINE    ENDP 			 ;AN000;
;****************************************************************************
;
;   REMOVE_END_BLANKS: Removes the trailing blanks from a string.
;
;   INPUT:
;	ES:DI Points to the last character in the string.
;
;   OUTPUT:
;	ES:DI Points to the new end of the string after the blanks have been
;	      removed.
;
;   OPERATION:
;
;****************************************************************************
PUBLIC	 REMOVE_END_BLANKS			 ;AN000;
REMOVE_END_BLANKS  PROC FAR			 ;AN000;
						 ;
    MOV  CX, 0FFFFH				 ;AN000;
    MOV  AL, ' '                                 ;AN000;
    STD 					 ;AN000;
    REPZ SCASB					 ;AN000;
    .IF < NZ >					 ;AN000;
	 INC  DI				 ;AN000;
    .ENDIF					 ;AN000;
    RET 					 ;AN000;
						 ;
REMOVE_END_BLANKS  ENDP 			 ;AN000;
						 ;
;****************************************************************************
;
;   CHECK_WRITE_ROUTINE  Determine if the diskette in drive A is write
;		       protected.
;
;   INPUT:
;	CX = 0 - drive A		   ;AN000;JW
;	   = 1 - drive B		   ;AN000;JW
;
;   OUTPUT:
;	If CY = 1, The disk IS write protected.
;	If CY = 0, The disk is NOT write protected.
;
;   OPERATION:
;
;****************************************************************************
PUBLIC	 CHECK_WRITE_ROUTINE			 ;AN000;
CHECK_WRITE_ROUTINE	PROC FAR		 ;AN000;
						 ;
    MOV  DRIVE_FLAG,CL				 ;AN000;
						 ;
    CALL HOOK_INT_24				 ;AN000;
						 ;
    MOV  CHK_W_PROTECT_FLAG, TRUE		 ;AN000; Indicate to INT 24H handler we are looking for error
    MOV  W_PROTECT_FLAG, FALSE			 ;AN000; Error has not occured yet.
						 ;
    MOV  W_P_FILENAME_A+3, 0			 ;AN000; Make drive string into ASCII-Z string
    MOV  W_P_FILENAME_B+3, 0			 ;AN000; Make drive string into ASCII-Z string	  JW
    .IF < DRIVE_FLAG eq DRIVE_A >		 ;AN000;JW
       MOV  DX, OFFSET W_P_FILENAME_A		 ;AN000; Get address of the string
    .ELSE					 ;AN000;JW
       MOV  DX, OFFSET W_P_FILENAME_B		 ;AN000; Get address of the string JW
    .ENDIF					 ;AN000;JW
    MOV  CX, 0					 ;AN000; Attribute to give the file
    MOV  INT_24_ERROR, FALSE			 ;AN000;
    MOV  AH, 5AH				 ;AN000; DOS Fn. call to create a unique file
    DOSCALL					 ;AN000; Create the file
    .IF < C >					 ;AN000; Was there an error?
	 .IF < W_PROTECT_FLAG EQ TRUE > 	 ;AN000; If the INT 24H handler was call...
	      JMP  WRITE_PROTECTED		 ;AN000; The disk is write protected.
	 .ELSE					 ;AN000; Otherwise...
	      JMP  CHECK_ERROR			 ;AN000; There was some other disk error
	 .ENDIF 				 ;AN000;
    .ELSE					 ;AN000;
	.IF < W_PROTECT_FLAG EQ TRUE >		 ;AN000; If the INT 24H handler was call...
	     JMP  WRITE_PROTECTED		 ;AN000; The disk is write protected.
	.ENDIF					 ;AN000;
    .ENDIF					 ;AN000; There were no errors...
    CLOSE_FILE	   AX				 ;AN000; Close the created file
    .IF < DRIVE_FLAG eq DRIVE_A >		 ;AN000;JW
       MOV  DX, OFFSET W_P_FILENAME_A		 ;AN000; Get address of the string
    .ELSE					 ;AN000;JW
       MOV  DX, OFFSET W_P_FILENAME_B		 ;AN000; Get address of the string JW
    .ENDIF					 ;AN000;JW
    MOV  AH, 41H				 ;AN000; DOS Fn. for erasing a file
    DOSCALL					 ;AN000; Erase the file
    MOV  AX, 0					 ;AN000; Indicate the file is NOT write protected
    CLC 					 ;AN000; Indicate there were no errors
    JMP  CHECK_EXIT				 ;AN000; Exit the routine

WRITE_PROTECTED:				 ;AN000;
    MOV  AX, 1					 ;AN000; Indicate the file IS write protected
    CLC 					 ;AN000; Indicate there were no errors
    JMP  CHECK_EXIT				 ;AN000;

CHECK_ERROR:					 ;AN000;
    MOV  AX, 0					 ;AN000; Indicate the file is NOT write protected
    STC 					 ;AN000; Indicate that there WERE errors

CHECK_EXIT:					 ;AN000;
    CALL RESTORE_INT_24 			 ;AN000; Restore the original INT 24H handler
    MOV  CHK_W_PROTECT_FLAG, FALSE		 ;AN000; We are no longer expecting a write protect error
    RET 					 ;AN000;

CHECK_WRITE_ROUTINE	ENDP			 ;AN000;
;****************************************************************************
;
;   MATCH_FILES_ROUTINE  Determine is a list of file exist on a drive.
;
;   INPUT:
;	DI = Address of the ASCII-N string containing the drive and path to
;	     search for the files.
;	SI = The address of the list of file.  If AX = 2, the first two bytes
;	      in the list are ignored.
;	AX = The type of list to use.
;	   = 1: Use a list with only 12 bytes between the filenames.
;	   = 2: Use a list with only 14 bytes between the filenames.
;	CX = The number of files in the list.
;
;   OUTPUT:
;	If CY = 1, There was an error access the disk.
;	If CY = 0, There were no errors.
;	      AX = 1: All the files are on the disk.
;	      AX = 0: All the files are NOT on the disk.
;
;   OPERATION:
;
;****************************************************************************
PUBLIC	 MATCH_FILES_ROUTINE			 ;AN000;
MATCH_FILES_ROUTINE	PROC FAR		 ;AN000;
						 ;
						 ;
    PUSH ES					 ;AN000; Make ES = DS
    PUSH DS					 ;AN000;
    POP  ES					 ;AN000;
						 ;
    MOV  NUM_FILES, CX				 ;AN000; Save the number of files
    MOV  LIST_TYPE, AX				 ;AN000; Save the type of the list
						 ;
    MOV  CX, [DI]				 ;AN000; Get the length of the string
    ADD  DI, 2					 ;AN000; Point SI to the start of the string
    MOV  DX, DI 				 ;AN000; Copy the address of the string
    ADD  DI, CX 				 ;AN000; Point to the end of the string
    .IF < LIST_TYPE EQ 2 >			 ;AN000; If this list is a 14 byte list...
	 ADD  SI, 2				 ;AN000; Bypass the first two bytes in the list
    .ENDIF					 ;AN000;
    MOV  STR_PTR, DI				 ;AN000; Save the pointer to the path string
    MOV  FILE_PTR, SI				 ;AN000; Save the pointer to the file list
    MOV  BX, 0					 ;AN000; Initialize the count of files checked
						 ;
    .WHILE < BX B NUM_FILES >			 ;AN000; Perform NUM_FILES interations
	 CLD					 ;AN000;
	 MOV  CX, 12				 ;AN000; Move 12 bytes for the filename
	 REP  MOVSB				 ;AN000; Move the filename after the path string
	 MOV  BYTE PTR [DI], 0			 ;AN000; Make string into an ASCII-Z string
	 MOV  AH, 4EH				 ;AN000; DOS Fn. for find a file
	 MOV  CX, 0				 ;AN000; Attribute used for search
	 DOSCALL				 ;AN000; Get the matching filename
	 .IF < C >				 ;AN000; Was there an error?
	      .IF < AX EQ 18 >			 ;AN000; If error no = 18, then file not found
		   JMP	FILE_NOT_FOUND		 ;AN000; Return to the user
	      .ELSE				 ;AN000; Otherwise
		   JMP	MATCH_ERROR		 ;AN000; There was some other type of disk error
	      .ENDIF				 ;AN000; Exit the subroutine
	 .ENDIF 				 ;AN000;
	 MOV  DI, STR_PTR			 ;AN000; Get the pointer to the string
	 MOV  SI, FILE_PTR			 ;AN000; Get the pointer to the file list
	 .IF < LIST_TYPE EQ 1 > 		 ;AN000; Check list type for incrementing the file pointer
	      ADD  SI, 12			 ;AN000; 12 bytes between files for list type 1
	 .ELSE					 ;AN000;
	      ADD  SI, 14			 ;AN000; 14 bytes between files for list type 2
	 .ENDIF 				 ;AN000;
	 MOV  FILE_PTR, SI			 ;AN000; Save the new file name pointer
	 INC  BX				 ;AN000; Increment the count of files searched for
    .ENDWHILE					 ;AN000;
    CLC 					 ;AN000; Indicate there were no errors
    MOV  AX, 1					 ;AN000; Indicate that all the files were found
    JMP  EXIT_MATCH				 ;AN000;
FILE_NOT_FOUND: 				 ;AN000;
    CLC 					 ;AN000; Indicate that there were no errors
    MOV  AX, 0					 ;AN000; But, all the files were not found
    JMP  EXIT_MATCH				 ;AN000;
MATCH_ERROR:					 ;AN000;
    STC 					 ;AN000; Indicate that there were errors
EXIT_MATCH:					 ;AN000;
    POP  ES					 ;AN000;
    RET 					 ;AN000;
						 ;
MATCH_FILES_ROUTINE	ENDP			 ;AN000;
;************************************************************************
;
;   CLOSE_FILE_ROUTINE: Close File
;
;   INPUT:
;      BX = The file handle of the file to close.
;
;   OUTPUT:
;	     CY = 0, AX = undefined,  successful
;	     CY = 1, AX = error code
;
;   OPERATION:
;
;   THIS MACROS CLOSES THE FILE WITH THE GIVEN FILE HANDLE.
;   IT MAKES USE OF INT 21 (AH=3EH).
;   IF AN ERROR OCCURS, THE CARRY FLAG IS SET, AND THE ERROR CODE
;   IS RETURNED IN AX.
;
;**************************************************************************
PUBLIC	 CLOSE_FILE_ROUTINE			 ;AN000;
CLOSE_FILE_ROUTINE PROC FAR			 ;AN000;
						 ;
						 ;
    CALL HOOK_INT_24				 ;AN000; Hook in the critical error handler
    MOV  INT_24_ERROR, FALSE			 ;AN000; Indicate no critical error have occured yet
    MOV  AH, 3EH				 ;AN000; DOS Fn. for closing a file
    DOSCALL					 ;AN000; Close the file
    CALL RESTORE_INT_24 			 ;AN000; Restore the old critical error handler
    RET 					 ;AN000;
						 ;
CLOSE_FILE_ROUTINE ENDP 			 ;AN000;
;**************************************************************
;
;   CREATE_FILE: Create new File
;
;   INPUT:
;	 DI = The address of the filename in ASCII-N format
;	 CX = The attribute to give the file
;
;   OUTPUT:
;	 If CY = 0: There were no errors.
;	      AX - The file handle of the created file.
;	 If CY = 1: There were file errors.  AX contains the error code.
;
;   OPERATION:
;
;   CREATE_FILE CREATES A FILE WITH THE GIVEN NAME USING INT 21H (AH=5BH)
;   IF AN ERROR OCCURS, THE CARRY FLAG IS SET, AND THE ERROR CODE
;   IS RETURNED IN AX.
;
;**************************************************************************
PUBLIC	 CREATE_FILE_ROUTINE			 ;AN000;
CREATE_FILE_ROUTINE	PROC FAR		 ;AN000;
						 ;
    CALL HOOK_INT_24				 ;AN000;
    CALL POS_ZERO				 ;AN000;
    MOV  DX, DI 				 ;AN000;
    ADD  DX, 2					 ;AN000;
    MOV  INT_24_ERROR, FALSE			 ;AN000;
    MOV  AH,5BH 				 ;AN000;
    DOSCALL					 ;AN000;
    CALL RESTORE_INT_24 			 ;AN000;
    RET 					 ;AN000;
						 ;
CREATE_FILE_ROUTINE	ENDP			 ;AN000;
;****************************************************************************
;
;   ERASE_FILE_ROUTINE: Routine to erase a file.
;
;   INPUT:
;	 DI - The address of an ASCII-N string containing the name of the file
;	      to erase.
;
;   OUTPUT:
;	 If CY = 0, there were no error encountered.
;	 If CY = 1, there were errors.	AX contains the DOS error code.
;
;   OPERATION:
;
;   ERASE_FILE ERASES THE FILE USING INT 21H (AH=41H).
;   IF AN ERROR OCCURS, THE CARRY FLAG IS SET, AND THE ERROR CODE
;   IS RETURNED IN AX.
;
;****************************************************************************
PUBLIC	 ERASE_FILE_ROUTINE			 ;AN000;
ERASE_FILE_ROUTINE PROC FAR			 ;AN000;

    CALL HOOK_INT_24				 ;AN000;
    CALL POS_ZERO				 ;AN000;
    MOV  INT_24_ERROR, FALSE			 ;AN000;
    MOV  DX, DI 				 ;AN000;
    ADD  DX, 2					 ;AN000;
    MOV  AH,41H 				 ;AN000;
    DOSCALL					 ;AN000;
    CALL RESTORE_INT_24 			 ;AN000;
    RET 					 ;AN000;

ERASE_FILE_ROUTINE ENDP 			 ;AN000;
;****************************************************************************
;
;   CHMOD_FILE_ROUTINE: Change file attributes to read/write
;
;   SYNTAX:  CHMOD_FILE_ROUTINE
;
;   INPUT:   DI = POINTER TO ASCII-N STRING - FILE NAME
;
;   OUTPUT:  None.
;
;   OPERATION:
;      The CHMOD dos call is executed (43H) to change the file's attributes
;      to read/write.
;
;****************************************************************************
PUBLIC CHMOD_FILE_ROUTINE			 ;AN000;
CHMOD_FILE_ROUTINE PROC FAR			 ;AN000;

	CALL HOOK_INT_24			 ;AN000;
	CALL POS_ZERO				 ;AN000;
	MOV  INT_24_ERROR, FALSE		 ;AN000;
	MOV  DX, DI				 ;AN000;
	ADD  DX, 2				 ;AN000;
	MOV  AH,043H				 ;AN000;
	MOV  AL,01				 ;AN000;
	XOR  CX,CX				 ;AN000;
	DOSCALL 				 ;AN000;
	CALL RESTORE_INT_24			 ;AN000;
	RET					 ;AN000;

CHMOD_FILE_ROUTINE ENDP 			 ;AN000;
;************************************************************************
;   FIND_FILE: Find File
;
;   INPUT:
;	 DI - The address of an ASCII-N string contian the name of the file
;	      to find.
;	 CX - The attribute to be used in the search.
;
;   OUTPUT:
;	 If CY = 1, there were errors encountered. AX contians the DOS error
;		    code.
;	 If CY = 0, there were no errors.
;
;   OPERATION:
;
;   FINDFILE FINDS THE FIRST FILENAME SPECIFIED USING INT 21 (AH=4EH).
;   AND LOADS INFORMATION INTO THE CURRENT DTA.
;   NOTE : THE DEFAULT DTA IS AT 80H IN THE PSP.
;   IF AN ERROR OCCURS, THE CARRY FLAG IS SET, AND THE ERROR CODE
;   IS RETURNED IN AX.
;
;************************************************************************
PUBLIC	 FIND_FILE_ROUTINE			 ;AN000;
FIND_FILE_ROUTINE  PROC FAR			 ;AN000;
						 ;
    CALL HOOK_INT_24				 ;AN000;
    CALL POS_ZERO				 ;AN000;
    MOV  DX, DI 				 ;AN000;
    ADD  DX, 2					 ;AN000;
    MOV  INT_24_ERROR, FALSE			 ;AN000;
						 ; CX Contains the attribute to be used in the search
    MOV  AH,4EH 				 ;AN000;
    DOSCALL					 ;AN000;
    CALL  RESTORE_INT_24			 ;AN000;
    RET 					 ;AN000;

FIND_FILE_ROUTINE  ENDP 			 ;AN000;
;**************************************************************************
;
;   OPEN_FILE_ROUTINE - Open File
;
;   INPUT:
;	 DI - The address of an ASCII-N string containing the name of the
;	      file to open.
;	 AL - The mode to open the file with ( 0 = read, 1 = write,
;	      2 = read/write)
;
;   OUTPUT:
;	 If CY = 1, there were errors encountered.  AX contains the DOS error
;		    code.
;	 If CY = 0, there were no errors.  AX contains the file handle.
;
;   OPERATION:
;
;   THIS MACRO OPENS A FILE FOR READ/WRITE OPERATIONS.
;   IT MAKES USE OF INT 21 (AH=3DH).
;   IF AN ERROR OCCURS, THE CARRY FLAG IS SET, AND THE ERROR CODE
;   IS RETURNED IN AX.
;
;**************************************************************************
PUBLIC	 OPEN_FILE_ROUTINE			 ;AN000;
OPEN_FILE_ROUTINE  PROC FAR			 ;AN000;
						 ;
    CALL HOOK_INT_24				 ;AN000;
    CALL POS_ZERO				 ;AN000;
    MOV  INT_24_ERROR, FALSE			 ;AN000;
    MOV  DX, DI 				 ;AN000;
    ADD  DX, 2					 ;AN000;
						 ; AL contains the mode for opening the file.
    MOV  AH,3DH 				 ;AN000;
    DOSCALL					 ;AN000;
    CALL RESTORE_INT_24 			 ;AN000;
    RET 					 ;AN000;

OPEN_FILE_ROUTINE  ENDP 			 ;AN000;
;**************************************************************************
;
;   RENAME_FILE_ROUTINE - Rename File
;
;   INPUT:
;	 SI - The address of an ASCII-N string containing the file to rename
;	      current file name.
;	 DI - The address of an ASCII-N string containing the new name for the
;	      file.
;
;   OUTPUT:
;	 If CY = 1, there were errors encountered.  AX contains the DOS error
;		   error code.
;	 If CY = 0, there were no errors.
;
;
;   OPERATION:
;
;   THIS MACRO RENAMES A FILE GIVEN 2 NAMES.
;   IT MAKES USE OF INT 21 (AH=56H).
;   IF AN ERROR OCCURS, THE CARRY FLAG IS SET, AND THE ERROR CODE
;   IS RETURNED IN AX.
;
;**************************************************************************
PUBLIC	 RENAME_FILE_ROUTINE			 ;AN000;
RENAME_FILE_ROUTINE	PROC FAR		 ;AN000;

    CALL HOOK_INT_24				 ;AN000;
    PUSH ES					 ;AN000;
    PUSH DS					 ;AN000;
    POP  ES					 ;AN000;
    PUSH DI					 ;AN000;
    ; SI Contains the address of the string containing the old filename.
    MOV  DI, SI 				 ;AN000;
    CALL POS_ZERO				 ;AN000;
    MOV  DX, DI 				 ;AN000;
    ADD  DX, 2					 ;AN000;

    POP  DI					 ;AN000;
    ; DI contains the address of the string containing the new filename.
    CALL POS_ZERO				 ;AN000;
    ADD  DI, 2					 ;AN000;

    MOV  INT_24_ERROR, FALSE			 ;AN000;
    MOV  AH,56H 				 ;AN000;
    DOSCALL					 ;AN000;
    POP  ES					 ;AN000;
    CALL  RESTORE_INT_24			 ;AN000;
    RET 					 ;AN000;

RENAME_FILE_ROUTINE	ENDP			 ;AN000;
;**************************************************************************
;
;   READ_FILE_ROUTINE: Transfer the specified number of bytes from a file into a
;	buffer location.
;
;   INPUT:
;	BX - The handle of the file to read.
;	DX - The address of where to store the data
;	CX - The number of characters to read
;
;   OUTPUT:
;	CY = 0, Read success.  AX - number of bytes read
;	CY = 1, Read error. AX contains the error code.
;
;   OPERATION:
;
;   THIS MACRO READS TO AN ALREADY OPENED FILE.
;   IT MAKES USE OF INT 21 (AH=3FH).
;   AX WILL RETURN THE NUMBER BYTES ACTUALLY WRITTEN.
;
;************************************************************************
PUBLIC	 READ_FILE_ROUTINE			 ;AN000;
READ_FILE_ROUTINE  PROC FAR			 ;AN000;
						 ;
    CALL HOOK_INT_24				 ;AN000;
    MOV  INT_24_ERROR, FALSE			 ;AN000;
						 ; BX - The file handle
						 ; CX - The number of bytes to read
						 ; DX - The address of the buffer to store the data
    MOV  AH,3FH 				 ;AN000;
    DOSCALL					 ;AN000;
    CALL RESTORE_INT_24 			 ;AN000;
    RET 					 ;AN000;

READ_FILE_ROUTINE  ENDP 			 ;AN000;
;**************************************************************************
;
;   WRITE_FILE_ROUTINE: Transfer the specified number of bytes from a buffer into a
;	specified file.
;
;   INPUT:
;	BX - The handle of the file to write to.
;	DX - The address of where the data is stored.
;	CX - The number of characters to write.
;
;   OUTPUT:
;	CY = 0, Write success.	AX - number of bytes written.
;	CY = 1, Write error. AX contains the error code.
;
;   OPERATION:
;
;   THIS MACRO WRITES TO AN ALREADY OPENED FILE.
;   IT MAKES USE OF INT 21 (AH=3DH).
;   AX WILL RETURN THE NUMBER BYTES ACTUALLY WRITTEN.
;
;************************************************************************
PUBLIC	 WRITE_FILE_ROUTINE			 ;AN000;
WRITE_FILE_ROUTINE PROC FAR			 ;AN000;
						 ;
    CALL HOOK_INT_24				 ;AN000;
    MOV  INT_24_ERROR, FALSE			 ;AN000;
						 ; BX - The file handle
						 ; CX - The number of bytes to read
						 ; DX - The address of the buffer to store the data
    MOV  AH,40H 				 ;AN000;
    DOSCALL					 ;AN000;
    CALL RESTORE_INT_24 			 ;AN000;
    RET 					 ;AN000;

WRITE_FILE_ROUTINE ENDP 			 ;AN000;
;***************************************************************************
;
;   CHECK_DISK:  Check is the specified fixed disk is present.	If disk is
;	present, return disk partition status.
;
;   INPUT:
;	AX = 1: First fixed disk.
;	   = 2: Second fixed disk.
;
;   OUTPUT:
;	CX = 0: Disk not present.
;	   = 1: Disk present - No DOS or EDOS partitions
;	   = 2: Disk present - DOS or EDOS partitions exist
;	BX = 01H: Primary DOS partition exists
;	   = 02H: Extended DOS partitions exists
;	   = 04H: Logical drives exist
;	   = 08H: Free space exists in EDOS partition
;	   = 10H: Free space exists on disk
;			      More than one status bit can be set
;	DX = 0: There is no free space in EDOS partition and the
;		disk.
;	   = 1: There is free space in the EDOS partition.
;	   = 2: There is no EDOS partition, but there is free
;			  disk space.
;	DI = Buffer for fixed disk status information.
;
;   OPERATION:	A call is performed to the FDISK utility (GET_DISK_STATUS)
;	to get the status of the specified fixed disk drive.  The returned
;	status information is checked and the memory variables are set as
;	specified above.
;
;***************************************************************************
PUBLIC	 CHECK_DISK_ROUTINE			 ;AN000;
CHECK_DISK_ROUTINE PROC FAR			 ;AN000;
						 ;
    PUSH ES					 ;AN000; Make ES = DS
    PUSH DS					 ;AN000;
    POP  ES					 ;AN000;
    PUSH DI					 ;AN000;
    ADD  DI, 2					 ;AN000;
    CALL GGET_STATUS				 ;AN000;
    POP  DI					 ;AN000;
    MOV  [DI+1], CL				 ;AN000; Store the number of table entries
    .IF < ZERO AX >				 ;AN000;
	 .IF < BIT BX AND M_DOS_EDOS_PART >	 ;AN000;
	      MOV  CX, PRESENT_WITH_PART	 ;AN000;
	 .ELSE					 ;AN000;
	      MOV  CX, PRESENT_WITHOUT_PART	 ;AN000;
	 .ENDIF 				 ;AN000;
    .ELSE					 ;AN000;
	 MOV  CX, NOT_PRESENT			 ;AN000;
    .ENDIF					 ;AN000;
    MOV  DX, NO_EDOS_SPACE			 ;AN000; Initialize
    .IF < BIT BX AND M_EDOS_EXISTS >		 ;AN000; Does the extended DOS partition exist?
	 .IF < BIT BX AND M_EDOS_SPACE >	 ;AN000; Yes! Is there free space in it?
	      MOV  DX, FREE_EDOS_SPACE		 ;AN000; Indicate that there is free space
	 .ELSEIF < BIT BX NAND M_FREE_SPACE >	 ;AN000; Is there no free space on the disk?
	      MOV  DX, NO_EDOS_SPACE		 ;AN000; Indicate there is no free space in EDOS or on the disk.
	 .ENDIF 				 ;AN000;
    .ELSEIF < BIT BX AND M_FREE_SPACE >		 ;AN000; No! There is no EDOS partition
	 MOV  DX, NO_EDOS_BUT_SPACE		 ;AN000; But there is free space on the disk
    .ENDIF					 ;AN000;
    POP  ES					 ;AN000;
    RET 					 ;AN000;
						 ;
CHECK_DISK_ROUTINE ENDP 			 ;AN000;
;************************************************************************;;
;
;   CHECK_VALID_MEDIA:	Check if the diskettes attached will support
;	installation of SELECT.  Also, check if install destination will
;	be selected by user or determined by SELECT.
;
;   SYNTAX:  CHECK_VALID_MEDIA	var_disk_a, var_disk_b, var_tot, var_disk,
;				var_def, var_index, var_option
;
;   INPUT:
;	var_disk_a  =  diskette A presence and type
;	var_disk_b  =  diskette B presence and type
;	var_tot     =  total number of dikettes
;	var_disk    =  0: first fixed disk is not present
;		    >  0: first fixed disk is present
;
;   OUTPUT:
;	CY = 0: Success variables are returned as defined below.
;	CY = 1: Error - invalid media
;	var_def   =  0 use default destination drive
;		  =  1 default destination drive not applicable
;	var_index =  1 default destination is drive C
;		  =  2 default destination is drive B
;	var_option = 1 possible drive B or C
;		   = 2 possible drive A or C
;		   = 3 possible drive A or B or C
;		   = 4 possible drive A or B
;
;   OPERATION:	The diskette drive types are checked for valid media type.
;	If the diskette media types are valid, a check is made to determine if
;	install destination will be user selected or will be determined by
;	SELECT.  The following checks are made.
;
;	 - if one diskette, return valid media and default destination is A
;
;	 - If two diskettes only, return valid and:
;	       if A = B, default = B
;	       if A <> B, default = A
;	       if A and B are mixed 720 and 1.44, destination option is A or B
;
;	 - If one diskette and a fixed disk only, return valid media and
;	   destination option is drive A or C.
;
;	 - If two diskettes and a fixed disk, return valid media and:
;	       if A = B, destination option is B or C
;	       if A <> B, destination option is A or C
;	       if A and B are mixed 720 and 1.44, destination option is
;		 A or B or C
;
;************************************************************************;;
PUBLIC	 CHECK_VALID_MEDIA_ROUTINE		 ;AN111;JW
CHECK_VALID_MEDIA_ROUTINE    PROC FAR		 ;AN111;JW


    VAR_DISK_A	   EQU	     AL 		 ;AN111;JW
    VAR_DISK_B	   EQU	     BL 		 ;AN111;JW
    VAR_DEF	   EQU	     CL 		 ;AN111;JW
    VAR_INDEX	   EQU	     DX 		 ;AN111;JW
    VAR_DISK	   EQU	     SI 		 ;AN111;JW
    VAR_OPTION	   EQU	     DI 		 ;AN111;JW

    .IF < VAR_DISK_A NE E_DISKETTE_INV >	 ;AN111; Is disk A present
	 .IF <VAR_DISK_B NE E_DISKETTE_INV>	 ;AN111; Is disk B present
	      .IF < VAR_DISK GT 0 >		 ;AN111; Hard disk is present?
		   MOV	  VAR_DEF, DO_NOT_USE_DEFAULT ;AN111; Yes! Destination drive is undefined
		   MOV VAR_OPTION,E_OPTION_B_C	 ;AN111; options will be B or C
		   MOV	  VAR_INDEX,DEF_DEST_C	 ;AN073; SEH highlight option C
		   CLC				 ;AN111; Indicate valid media
	      .ELSE				 ;AN111;
		   MOV	VAR_DEF, USE_DEFAULT	 ;AN111; Yes! Use the default destination = B
		   MOV	VAR_INDEX, DEF_DEST_B	 ;AN111; Drive B is that default
		   CLC				 ;AN111; Indicate valid media
	      .ENDIF				 ;AN111;
	 .ELSE					 ;AN111;
	      .IF < VAR_DISK GT 0 >		 ;AN111; Hard disk is present?
		   MOV	  VAR_DEF, DO_NOT_USE_DEFAULT ;AN111; Yes! Destination drive is undefined
		   MOV	  VAR_OPTION, E_OPTION_A_C ;AN111; options are A or C
		   MOV	  VAR_INDEX,DEF_DEST_C	 ;AN073; SEH highlight option C
		   CLC				 ;AN111; Indicate valid media
	      .ELSE				 ;AN111;
		   MOV	  VAR_DEF, USE_DEFAULT	 ;AN111; no, Use the default destination
		   MOV	  VAR_INDEX, DEF_DEST_A  ;AN111; Drive A is that default
		   CLC				 ;AN111; Indicate valid media
	      .ENDIF				 ;AN111;
	 .ENDIF 				 ;AN111;
    .ELSE					 ;AN111;
	 STC					 ;AN111; Indicate invalid media
    .ENDIF					 ;AN111;
    RET 					 ;AN111;

CHECK_VALID_MEDIA_ROUTINE    ENDP		 ;AN111;JW
;************************************************************************;;
;
;   SCAN_DISK_TABLE:  Scan the specified disk status table from the
;	specified index item for specified field and return status information.
;
;   INPUT:
;	CX = 1: First fixed disk
;	   = 2: Second fixed disk
;	AX = Index of the information to return
;
;   OUTPUT:
;	AX = 0: Success - Index into table is valid
;	   = 1: Error - Index invalid or end of table
;	N_NAME_PART   = Partition name.
;	N_SIZE_PART   = Partition size.
;	N_STATUS_PART = Partition status
;	P_DRIVE_PART  = Drive letter assigned.
;	P_LEVEL1_PART  = Version number (1st part).   For DOS 4.00 1st part = blank
;	P_LEVEL2_PART  = Version number (2nd part).   For DOS 4.00 2nd part = 4
;	P_LEVEL3_PART  = Version number (3rd part).   For DOS 4.00 3rd part = .
;	P_LEVEL4_PART  = Version number (4th part).   For DOS 4.00 4th part = 0
;
;   OPERATION:
;      Starts scanning the disk table from the point indicated by var_index
;      for either the name, status or type.  The table is scanned until either
;      the desired entry is found, or the end of the table is reached.	If
;      the end of the table is reached before a matching entry is found, then
;      var_ret returns 1, else if an entry is found, it returns 0.
;      If found, var_index will also return the index of the entry.
;
;      Note:  The index of the first entry in the table is 1.
;
;************************************************************************;;
PUBLIC	 SCAN_DISK_TABLE_ROUTINE							     ;AN000;
SCAN_DISK_TABLE_ROUTINE PROC FAR							     ;AN000;

    MOV  BX, 0					 ;AN000;
    .IF < CX EQ TABLE_ONE >			 ;AN000;
	 MOV  SI, OFFSET DISK_1_START		 ;AN000; Get the address of the first table
	 MOV  BL, DISK_1_VAL_ITEM		 ;AN000; Number of entries in the first table
    .ELSE					 ;AN000;
	 MOV  SI, OFFSET DISK_2_START		 ;AN000; Get the address of the second table
	 MOV  BL, DISK_2_VAL_ITEM		 ;AN000; Number of entries in the second table
    .ENDIF					 ;AN000;
    .IF < AX BE BX >				 ;AN000;
						 ; AX contains the index
	 DEC  AX				 ;AN000; Make the first index a 0
	 MOV  DX, TYPE DISK_STATUS		 ;AN000; Number of bytes in the structure
	 MUL  DX				 ;AN000; Calculate the offset into the table
	 ADD  SI, AX				 ;AN000; Add to the address of the table
	 COPY_BYTE	 N_NAME_PART,	[SI].N_PART_NAME   ;AN000; Copy the table entries
	 COPY_WORD	 N_SIZE_PART,	[SI].N_PART_SIZE   ;AN000;
	 COPY_BYTE	 N_STATUS_PART, [SI].N_PART_STATUS ;AN000;
	 COPY_BYTE	 P_DRIVE_PART,	[SI].P_PART_DRIVE  ;AN000;
	 COPY_BYTE	 N_TYPE_PART,	[SI].N_PART_TYPE   ;AN000;
	 COPY_BYTE	 N_LEVEL1_PART, [SI].N_PART_LEVEL1 ;AN065;SEH 1st part of version number   For DOS 4.00 1st part = blank
	 COPY_BYTE	 N_LEVEL2_PART, [SI].N_PART_LEVEL2 ;AN065;SEH 2nd part of version number   For DOS 4.00 2nd part = 4
	 COPY_BYTE	 N_LEVEL3_PART, [SI].N_PART_LEVEL3 ;AN065;SEH 2nd part of version number   For DOS 4.00 3rd part = .
	 COPY_BYTE	 N_LEVEL4_PART, [SI].N_PART_LEVEL4 ;AN065;SEH 2nd part of version number   For DOS 4.00 4th part = 0
	 MOV  AX, DATA_VALID			 ;AN000;
    .ELSE					 ;AN000;
	 MOV  AX, DATA_INVALID			 ;AN000;
    .ENDIF					 ;AN000;
    RET 					 ;AN000;

SCAN_DISK_TABLE_ROUTINE ENDP			 ;AN000;
;************************************************************************;;
;
;   UPDATE_DISK_TABLE:	Update the specifed disk status table for the
;	specified index item.
;
;   INPUT:
;	CX = 1: First fixed disk
;	   = 2: Second fixed disk
;	AX = Index into table
;
;   OUTPUT:
;	AX = 0: Success - Index into table is valid
;	   = 1: Error	- Index into table is not valid
;	partition name	 = N_NAME_PART
;	partition size	 = N_SIZE_PART
;	partition status = N_STATUS_PART
;	partition type	 = N_TYPE_PART
;	drive letter	 = P_DRIVE_PART
;
;   OPERATION:	If the index into the disk table is valid, the disk table
;	is updated for the specifed index.  Disk status information is obtained
;	from pre-defined locations as specified above.
;
;************************************************************************;;
PUBLIC	 UPDATE_DISK_TABLE_ROUTINE		 ;AN000;
UPDATE_DISK_TABLE_ROUTINE    PROC FAR		 ;AN000;

    MOV  BH, 0					 ;AN000;
    .IF < CX EQ TABLE_ONE >			 ;AN000;
	 MOV  SI, OFFSET DISK_1_START		 ;AN000; Get the address of the first table
	 MOV  BL, DISK_1_VAL_ITEM		 ;AN000; Number of entries in the first table
    .ELSE					 ;AN000;
	 MOV  SI, OFFSET DISK_2_START		 ;AN000; Get the address of the second table
	 MOV  BL, DISK_2_VAL_ITEM		 ;AN000; Number of entries in the second table
    .ENDIF					 ;AN000;
						 ; AX contains the index.
    DEC  AX					 ;AN000; Make the first index a 0
    MOV  DX, TYPE DISK_STATUS			 ;AN000; Number of bytes in the structure
    MUL  DX					 ;AN000; Calculate the offset into the table
    ADD  SI, AX 				 ;AN000; Add to the address of the table
    .IF < VAR_INDEX BE BX >			 ;AN000;
	 COPY_BYTE	[SI].N_PART_NAME,   N_NAME_PART    ;AN000;
	 COPY_WORD	[SI].N_PART_SIZE,   N_SIZE_PART    ;AN000;
	 COPY_BYTE	[SI].N_PART_STATUS, N_STATUS_PART  ;AN000;
	 COPY_BYTE	[SI].P_PART_DRIVE,  P_DRIVE_PART   ;AN000;
	 COPY_BYTE	[SI].N_PART_TYPE,   N_TYPE_PART    ;AN000;
	 MOV	 AX, DATA_VALID 		 ;AN000; No error.
    .ELSE					 ;AN000;
	 MOV	 AX, DATA_INVALID		 ;AN000; Indicate an error
    .ENDIF					 ;AN000;
    RET 					 ;AN000;
						 ;
UPDATE_DISK_TABLE_ROUTINE    ENDP		 ;AN000;
						 ;
CODE_FAR ENDS					 ;AN000;
						 ;
END						 ;AN000;