-
Notifications
You must be signed in to change notification settings - Fork 2
/
0021-Juniper.st
1153 lines (1096 loc) · 38.7 KB
/
0021-Juniper.st
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
'From Smalltalk 5.5k XM November 24 on 22 November 1980 at 2:57:08 am.'
"JuniperFileController"
Class new title: 'JuniperFileController'
subclassof: File
fields: '
fLongFileHandle
fShortFileHandle
'
declare: '';
sharing: gJuniperConstants;
asFollows
JUNIPER FILE CONTROLLER
DOCUMENTATION
implementationNotes
[
"
FIELDS
fLongFileHandle : a LongInteger representing the unique identifier for the file.
fShortFileHandle : an Integer representing the identifier for the file during a particular transaction.
"
]
FILE REDEFINITIONS (restricted)
allocatePage
"...returns a new packet (Pacbuf) to be used as the data buffer in a JuniperPageBuffer."
[
⇑ ((self interface) newPacket)
]
close
"...closes the file on the Juniper file system."
[
self doAction: sCloseFile requestPrs: nil.
]
endFile: pPageBuffer
"...adjusts the file length, writes pPageBuffer (a JuniperPageBuffer) on the file, and returns pPageBuffer."
[
self length: ( ((pPageBuffer pageNumber)-1)*(pPageBuffer dataLength)
+ pPageBuffer length
). "1"
self writePage: pPageBuffer. "2"
⇑ pPageBuffer
]
"
1. Set the file length to the total number of bytes in the file.
2. Write the page on Juniper.
"
entryClass
"...returns the class of objects managed by JuniperFileController objects."
[
⇑ JuniperPageBuffer
]
Get: pPageBuffer
"...reads the data from the page whose number is that specified by pPageBuffer (a JuniperPageBuffer) if such a page exists. Otherwise pPageBuffer should contains data to be written on the file at the appropriate place."
| tNextPageNumber tNewPageNumber i
[
tNewPageNumber ← pPageBuffer pageNumber.
tNewPageNumber ≤ lastpn and⦂ (
lastpn > 1 or⦂ self length > 0)⇒ [ ⇑ (self Read: pPageBuffer) ]
tNextPageNumber ← lastpn + 1.
[ tNewPageNumber = tNextPageNumber ⇒ [ ]
self length: (tNewPageNumber-1) * (pPageBuffer dataLength).
for⦂ i from: tNextPageNumber to: (tNewPageNumber-1) do⦂
[ pPageBuffer pageNumber: i.
pPageBuffer ← self writePage: pPageBuffer.
].
].
pPageBuffer pageNumber: tNewPageNumber.
pPageBuffer length: 0.
⇑ pPageBuffer
]
lastFullPage [⇑self length / self entryClass new dataLength]
length
"...returns the number of bytes (an Integer) in the file."
| tResult
[
tResult ← self doAction: sReadLength requestPrs: nil. "1"
⇑ (tResult longInteger: 1) "2"
]
"
1. Issue a 'read length' command to Juniper.
2. Return the length from the result parameter block.
"
length: pLength
"...sets the number of bytes in the file to the integer pLength (an Integer)."
| tRequest
[
tRequest ← self newRequestParameterBlock. "1"
tRequest longInteger: 1 ← pLength. "2"
self doAction: sSetLength requestPrs: tRequest. "3"
lastpn ← self pageFrom: pLength. "4"
]
"
1. Create a new request parameter block.
2. Set the length parameter to pLength.
3. Issue a 'set length' command to Juniper.
4. Determine the number of the last page of the file and assign it to the superClass field lastpn.
"
open
"...opens the file on the Juniper file system."
| tRequest tResult
[
tRequest ← self newRequestParameterBlock. "1"
tRequest nextDataBlockString ← fLongFileHandle. "2"
tResult ← (self interface) doAction: sOpenFile requestPrs: tRequest. "3"
fShortFileHandle ← tResult parameter: 1. "4"
self findLastPage. "5"
]
"
1. Create a new request parameter block.
2. Set the long file handle parameter.
3. Issue an 'open file' command to Juniper.
4. Set the superClass field serialNumber to the short file handle returned by Juniper in the result parameter block.
5. Set the number of the last page of the file.
"
Read: pPageBuffer
"...returns false if the page number of pPageBuffer (a JuniperPageBuffer) is greater than the page number of the last page of the file; otherwise, the data for that page is read from the Juniper file system into the packet of pPageBuffer."
| tRequest tResult pn
[
[ (pn ← pPageBuffer pageNumber) > lastpn ⇒ [ ⇑ false ] "1"
lastpn=1 and⦂ self empty⇒ [
pPageBuffer length: 0. "⇑ self Get: pPageBuffer" ]
tRequest ← self newRequestParameterBlock. "2"
tRequest parameter: 1 ← (pn - 1). "3"
tResult ← self doAction: sReadPage requestPrs: tRequest. "4"
pPageBuffer page: (tResult packet). "5"
pPageBuffer length: (tResult parameter: 1). "6"
].
⇑ pPageBuffer "7"
]
"
1. Return false if the page number of pPageBuffer is greater than that of the last page of the file.
2. Create a new request parameter block.
3. Set the page number parameter to that of pPageBuffer less 1. Juniper page numbers start at 0.
4. Issue a 'read page' request.
5. Set the pPageBuffer packet to that of the result parameter block.
6. Set the length of pPageBuffer to that returned in the result parameter block.
7. Return pPageBuffer modified.
"
Write: pPageBuffer
"...adjusts the length of the file if pPageBuffer is the last page, sends the data buffered in pPageBuffer (a JuniperPageBuffer) to the Juniper file system, and returns pPageBuffer."
| tPageNumber
[
tPageNumber ← pPageBuffer pageNumber.
[ tPageNumber < lastpn ⇒ [ ] "1"
[ tPageNumber > (lastpn+1) ⇒
[ ⇑ self error: 'invalid page number' ] "2"
self length:
( (tPageNumber-1)*(pPageBuffer dataLength)+(pPageBuffer length)
). "3"
].
].
self writePage: pPageBuffer. "4"
⇑ pPageBuffer "5"
]
"
1. If the page number of pPageBuffer is less than the last page number of the file, do not readjust the file length.
2. If the page number of pPageBuffer is greater than the next available page number for the file, invoke error handling.
3. If pPageBuffer is the last page of the file or will immediately follow the last page of the file, adjust the file length.
4. Send the data to Juniper.
5. Return pPageBuffer unmodified.
"
writePage: pPageBuffer
"...sends the data buffered in pPageBuffer (a JuniperPageBuffer) to the Juniper file system provided the buffer is not empty."
| tRequest
[
[ (pPageBuffer length = 0) ⇒ [ ] "1"
tRequest ← JuniperRequestParameterBlock new. "2"
tRequest
packet ← pPageBuffer page; "3"
leader: 1 ← 0;
leader: 2 ← 0; "4"
parameter: 1 ← ((pPageBuffer pageNumber) - 1). "5"
self doAction: sWritePage requestPrs: tRequest. "6"
].
]
"
1. If there is no data in pPageBuffer do nothing.
2. Create a new request parameter block.
3. Set the packet to that of pPageBuffer. This contains the data to be written on the Juniper file.
4. Set the authentication key and reserved word to 0.
5. Set the page number parameter to that of pPageBuffer less 1. Juniper page numbers start at 0.
6. Issue a 'write page' request.
"
MISC (internal)
doAction: pAction requestPrs: pRequest
"...the specified request, pAction, (selected from gJuniperConstants) with its corresponding request parameter block, pRequest ( a JuniperRequestParameter Block), is issued to the Juniper file server (through the file interface). If no errors are found, a JuniperResultParameterBlock is returned; otherwise, error handling is invoked. If no request parameters are required, pRequest can be specified as nil."
[
[ pRequest ≡ nil ⇒ [ pRequest ← self newRequestParameterBlock. ] "1"
].
pRequest shortFileHandle ← fShortFileHandle. "2"
⇑ ((self interface) doAction: pAction requestPrs: pRequest) "3"
]
"
1. Create a new request parameter block if one is not specified.
2. Set the short file handle parameter.
3. Issue the request to the Juniper interface and return the result parameter block.
"
interface
"...returns the JuniperInterface controlling the file."
[
⇑ directory
]
longFileHandle: pLongFileHandle
"...sets the file's long file handle."
[
fLongFileHandle ← pLongFileHandle.
]
newRequestParameterBlock
"...initializes and returns a new JuniperRequestParameterBlock."
[
⇑ ((self interface) newRequestParameterBlock)
]
SystemOrganization classify: ↪JuniperFileController under: 'Juniper'.
"JuniperInterface"
Class new title: 'JuniperInterface'
subclassof: FileDirectory
fields: '
fName
fPassword
fDefaultDirectory
fJuniperSocket
fTimer
fExceptionHandler
fSpecialError
fOpenIndicator
'
declare: '';
sharing: gJuniperConstants;
asFollows
JUNIPER INTERFACE
DOCUMENTATION
implementationNotes
[
"
FIELDS
fName : a String representing the name of an account on the Juniper file system.
fPassword : a String representing the password of the same account.
fDefaultDirectory : a String representing the directory to be used as the default if one is not specified as part of a file name.
fJuniperSocket : a JuniperSocket used to interface to the etherWorld mechanism.
fTimer : a Timer used to periodically send noop commands when the interface is open to prevent timeout.
fExceptionHandler : an ExceptionHandler to invoke when a transaction is aborted.
fSpecialError : an error code to ignore on a command request.
fOpenIndicator : the interface status: true means open; nil means not open.
"
]
USER CALLABLE
close
"...ends the current transaction and closes the interface."
| tResult
[
[ fOpenIndicator ≡ nil ⇒ [ ] "1"
tResult ← self doAction: sLogout requestPrs: nil. "2"
user
cr;
show: (tResult nextDataBlockString). "3"
super close. "4"
].
]
"
1. If the interface is not open, do nothing.
2. Issue a logout command.
3. Display the logout message returned by Juniper.
4. delete self from externalViews, Release timers, fields, etc.
"
closeTransaction
"...closes the current transaction leaving the user logged in with all open files still open, and all read and write locks still in effect."
[
self doAction: sCloseTransaction requestPrs: nil.
]
directory: pDirectory
"...specifies the directory name to be added to any file name that does not begin with a directory name."
[
fDefaultDirectory ← pDirectory.
]
exceptionHandler: pExceptionHandler
"...specifies an exception handler to be invoked if any subsequent request discovers that the expected transaction has been closed. If no exception handler is set, a notify window is displayed."
[
fExceptionHandler ← pExceptionHandler.
]
name: pName password: pPassword
"...specifies the name and password of an account on the Juniper file system. This account will be logged into whenever the interface is opened."
[
fName ← pName.
fPassword ← pPassword.
]
obsolete [⇑fOpenIndicator ≡ nil]
open
"...opens the interface allowing access to files on the Juniper file system. A new transaction is started."
| tResult
[
[ fOpenIndicator ≡ true ⇒ [ ] "1"
E wakeup. "2"
self release. "3"
fJuniperSocket ← JuniperSocket new hostName: self server. "4"
tResult ← self login: (self userName) password: (self userPassword). "5"
user
cr;
show: (tResult nextDataBlockString). "6"
self timerOn. "7"
super open. "8"
fOpenIndicator ← true. "9"
].
]
"
1. If the interface is already open, do nothing.
2. Make sure that the EtherWorld mechanism is in a valid state.
3. Release the interface to insure a valid initial state. (An invalid state can occur from an error on some statement in a previous invocation of 'open').
4. Create and initialize a JuniperSocket (parameterize server name?).
5. Issue a login command.
6. Display the login message that is returned by Juniper.
7. Turn on a timer to issue periodic noop commands to prevent timeout.
8. Do predefined open operations.
9. Set the interface status to 'open'.
"
release
"...leaves the interface in a valid state after an error."
[
fOpenIndicator ← nil. "1"
self timerOff. "2"
[ fJuniperSocket ≡ nil ⇒ [ ]
fJuniperSocket close. "3"
fJuniperSocket ← nil. "4"
].
]
"
1. Set the interface status to 'not open'.
2. Turn off the timer.
3. Close the JuniperSocket if it exists.
4. Release the JuniperSocket field.
"
versionNumbers [⇑true]
FILE DIRECTORY (restricted)
Delete: pFile
"...deletes from the Juniper file system the file whose file name is specified by pFile (a JuniperFileController)."
| tRequest
[
tRequest ← self newRequestParameterBlock. "1"
tRequest nextDataBlockString ← (self checkDirectory: (pFile name)). "2"
self doAction: sDestroyFile requestPrs: tRequest. "3"
]
"
1. Create a new request parameter block.
2. Get the file name from pFile, add the default directory name to it if necessary, and write it in the request parameter block.
3. Issue a 'destroy file' request.
"
entryClass
"...returns the file class handled by the JuniperInterface."
[
⇑ JuniperFileController
]
Find: pFile
"...sends a 'look up file' request to the Juniper file server. If the file is found, its name and long file handle are set in pFile (a JuniperFileController). Otherwise false is returned."
| tRequest tResult
[
fSpecialError ← sFileNotFound. "1"
tRequest ← self newRequestParameterBlock. "2"
tRequest nextDataBlockString ← (self checkDirectory: (pFile name)). "3"
tResult ← self doAction: sLookupFile requestPrs: tRequest. "4"
[ fSpecialError ≡ true ⇒ [ fSpecialError ← nil. ⇑ false ] "5"
fSpecialError ← nil. "6"
pFile
longFileHandle: tResult nextDataBlockString;
name: tResult nextDataBlockString. "7"
].
]
"
1. Set the error handling mechanism to ignore a 'file not found' error.
2. Create a new request parameter block.
3. Get the file name from pFile, add the default directory name to it if necessary, and write it in the request parameter block.
4. Issue a 'look up file' request and get the result parameter block.
5. If a 'file not found' error was encountered, reset the error handling mechanism and return false.
6. (no error encountered) Reset the error handling mechanism.
7. Set the long file handle and name (from the result parameter block) in pFile.
"
Insert: pFile
"...issues a 'create file' request to the Juniper file server and sets the returned long file handle and name in pFile (a JuniperFileController)."
| tRequest tResult
[
tRequest ← self newRequestParameterBlock. "1"
tRequest longInteger: 2 ← 0 "user rawtotalsecs". "1.5"
tRequest nextDataBlockString ← (self checkDirectory: (pFile name)). "2"
tRequest nextDataBlockString ← ''. "3"
tResult ← self doAction: sCreateFile requestPrs: tRequest. "4"
pFile
longFileHandle: tResult nextDataBlockString;
name: tResult nextDataBlockString. "5"
]
"
1. Create a new request parameter block.
1.5 Set creation date (to default (current) -- later some specific date&time?)
2. Get the file name from pFile, add the default directory name to it if necessary, and write it in the request parameter block.
3. Blank the file server field of the request parameter block.
4. Issue a 'create file' request and get the result parameter block.
5. Set the long file handle and name (from the result parameter block) in pFile.
"
Match: entries to: strm | entry pat ents name i p lastname [
"search for Files matching patterns"
for⦂ pat from: entries do⦂ [
name ← self checkDirectory: pat name.
i ← name find: '*'◦1. p ← name find: '#'◦1.
i ← [p=0⇒ [i] i=0⇒ [p] i min: p].
i=0⇒ [
(self Find: pat)⇒ [
"exact name found"
strm next ← pat]]
"pattern match over range of first to last possible matches"
pat ← self makeEntry: name.
entry ← self makeEntry: (name copy: 1 to: i-1).
lastname ← name copy: 1 to: i.
lastname◦i ← 0377.
while⦂ ((entry ← self nextFile: entry) and⦂ entry name < lastname) do⦂ [
pat match: entry⇒ [
strm next ← entry.
"copy entry since nextFile smashes into it"
entry ← self makeEntry: entry name]
].
]]
nextFile: pFile
"same as LookupFile (Find:), but it pertains to the file whose name is lexically next after the fileName specified in the request. If the file is found, its name and long file handle are set in pFile (a JuniperFileController). Otherwise (no next file) false is returned."
| tRequest tResult
[
fSpecialError ← sFileNotFound. "1"
tRequest ← self newRequestParameterBlock. "2"
tRequest nextDataBlockString ← (self checkDirectory: (pFile name)). "3"
tResult ← self doAction: sNextFile requestPrs: tRequest. "4"
[ fSpecialError ≡ true ⇒ [ fSpecialError ← nil. ⇑ false ] "5"
fSpecialError ← nil. "6"
pFile
longFileHandle: tResult nextDataBlockString;
name: tResult nextDataBlockString. "7"
⇑pFile
].
]
Rename: oldFile from: newFile
"renames a file on Juniper"
| tRequest newName
[
tRequest ← self newRequestParameterBlock.
tRequest nextDataBlockString ← oldFile name.
tRequest nextDataBlockString ← (newName ← self checkDirectory: newFile name).
self doAction: sRenameFile requestPrs: tRequest.
oldFile name: newName.
]
server ["should be an instance variable?" ⇑'Juniper']
JUNIPER COMMAND INTERFACE (restricted)
checkResult: pResult
"...is sent by doAction and doLogin to check the result of a Juniper request. pResult is a result parameter block (JuniperResultParameterBlock) containing the packet (Pacbuf) returned as a result of the request."
[
[
[ ((pResult packet) ≡ false or⦂ pResult pupType = 4) or⦂ (pResult resultCode = sCommandNak and⦂ (pResult parameter: 1) = sTransactionAborting) ⇒ "3"
[ [ fExceptionHandler ≡ nil ⇒ [ ]
fExceptionHandler trap. "4"
].
self error: 'No Juniper Transaction'. "5"
]
].
[ (pResult resultCode)
= sCommandAck ⇒ [ ⇑ true ]; "6"
= sCommandNak ⇒ "7"
[
[ self checkRetry: (pResult parameter: 1) ⇒ [ ⇑ false ] "8"
[ (pResult parameter: 1) = fSpecialError ⇒
[ fSpecialError ← true. ⇑ true ] "9"
self error: (pResult nextDataBlockString). "10"
].
].
]
⇑ true "11"
].
].
]
"
1. JuniperSocket sendRequest: and sendLogin: return false if no response is received from Juniper.
2. Invoke error handling if there is no response from Juniper.
3. Juniper returns an error pup (packet pup type = 4) if the transaction has been aborted.
4. If the transaction has been aborted and there is an exception handler, then trap to the handler.
5. Invoke error handling if control is returned or if there is no exception handler.
6. Return true if a 'command acknowledged' was returned (this means success with no result parameters).
7. If a 'command not acknowledged' was returned, an error condition exists.
8. Check for a retryable error and return false if so.
9. If the error is the same as that specified in the special error indicator, then indicate it and return true.
10. If not, invoke error handling.
11. Return true if a 'command not acknowledged' was not returned (this means success with result parameters).
"
checkRetry: pErrorCode
"...is sent by checkResult to test for a retryable error. Returns true if so; false otherwise."
[
[ pErrorCode
= sSequenceNumberGap ⇒ [ ⇑ true ];
= sRecoveryUnderWay ⇒ [ ⇑ true ];
= sTransactionClosing ⇒ [ ⇑ true ];
= sCongestion ⇒ [ ⇑ true ]
⇑ false
].
]
doAction: pAction requestPrs: pRequest
"...corresponds to the Pine Protocol function. The specified request, pAction, (selected from gJuniperConstants) with its corresponding request parameter block, pRequest (a JuniperRequestParameterBlock), is issued to the Juniper file server (through JuniperSocket sendRequest:). A packet is returned and inserted into a result parameter block and checked for error conditions. If no errors are found, the result parameter block (a JuniperResultParameterBlock) is returned; otherwise, error handling is invoked. If no request parameters are required, pRequest can be specified as nil."
| tResult
[
[ fOpenIndicator ≡ nil ⇒ [ self open. ] "1"
].
[ pRequest ≡ nil ⇒ [ pRequest ← self newRequestParameterBlock. ] "2"
].
fTimer disable. "3"
pRequest opcode ← pAction. "4"
pRequest pupType ← sRequest. "5"
tResult ← JuniperResultParameterBlock new. "6"
tResult packet ← (fJuniperSocket sendRequest: (pRequest packet)). "7"
fTimer reset. "8"
[ self checkResult: tResult ⇒ [ ⇑ tResult ] "9"
⇑ (self doAction: pAction requestPrs: pRequest) "10"
].
]
"
1. Make sure the JuniperInterface is open.
2. Create a request parameter block if none was specified.
3. Disable the timer to insure that a noop command is not sent while the current request is in progress.
4. Set the command code in the packet.
5. Set the packet pup type to 'request'.
6. Create a result parameter block.
7. Send the request to the JuniperSocket with the packet from the request parameter block; set the result packet in the result parameter block.
8. Reset the timer.
9. Check the result; if valid, return it.
10. If false is returned, then a retryable error was encountered so retry the command.
"
doLogin: pRequest
"...is sent by login:password: with pRequest (a JuniperRequestParameter Block) set. doLogin: issues a login request, checks for errors, and retries if necessary. If no error occurs, a JuniperResultParameterBlock is returned; otherwise, error handling is invoked."
| tResult
[
tResult ← JuniperResultParameterBlock new. "1"
tResult packet ← (fJuniperSocket sendLogin: (pRequest packet)). "2"
[ self checkResult: tResult ⇒ [ ⇑ tResult ] "3"
⇑ (self doLogin: pRequest) "4"
].
]
"
1. Create a new result parameter block.
2. Issue a login request and set the result packet in the result parameter block.
3. Check the result and return it if there is no error.
4. Retry the request if there is a retryable error.
"
error: pMessage
"...is sent by checkResult if an error is encountered on a Juniper request. (pMessage is a String containing an appropriate error message.) The error message is formed and a notify window is displayed."
| tMessage
[
tMessage ← Stream default.
tMessage append: pMessage.
super error: (tMessage contents).
]
newPacket
"...returns a new packet (Pacbuf) from the JuniperSocket."
[
[ fJuniperSocket ≡ nil ⇒ [ self open. ]
].
⇑ (fJuniperSocket freePacket)
]
newRequestParameterBlock
"...initializes and returns a new JuniperRequestParameterBlock."
| tRequestParameterBlock
[
[ fJuniperSocket ≡ nil ⇒ [ self open. ] "1"
].
tRequestParameterBlock ← JuniperRequestParameterBlock new. "2"
tRequestParameterBlock
packet ← (fJuniperSocket freePacket); "3"
dataBlockLength ← 0; "4"
leader: 1 ← 0;
leader: 2 ← 0. "5"
⇑ tRequestParameterBlock "6"
]
"
1. Open the interface if it is not already.
2. Create a new request parameter block.
3. Create a new packet and set it in the request parameter block.
4. Set the data block length to 0.
5. Set the authentication key and reserved word to 0.
6. Return the initialized request parameter block.
"
TIMER (internal)
noOp
"...sends a noop command to the Juniper file server. It's only effect is to reset the timout mechanism of the server."
| tPacket
[
tPacket ← fJuniperSocket freePacket. "1"
tPacket pupType ← sNoop. "2"
tPacket dataString ← ''. "3"
fJuniperSocket setAddressesAndComplete: tPacket; timerOff. "4"
]
"
1. Get a new packet (Pacbuf).
2. Set packet pup type to 'noop'.
3. Set the dataString to the empty string. This has the necessary effect of setting the packet length to 0.
4. Send the packet. It is necessary to bypass the normal JuniperSocket interface (sendRequest:) because no acknowledgement is returned for the noop.
"
timerOff
"...disables fTimer and sets it to nil."
[
[ fTimer ≡ nil ⇒ [ ]
fTimer disable.
fTimer ← nil.
].
]
timerOn
"...assigns fTimer to a new timer object that wakes up every 100 seconds (6000 1/60 seconds) and issues a 'noop' command to Juniper. This is used to prevent Juniper from timing out during periods of inactivity."
[
self timerOff. "1"
fTimer ← Timer new. "2"
fTimer for: 6000 action⦂ [ self noOp. user show: '.'. fTimer reset. ]. "3"
user cr.
fTimer reset. "4"
]
"
1. Make sure that the current timer is released.
2. Create a new timer.
3. Set the timer interval to 100 seconds. The action of the timer is to send a noop, show a dot in the dispFrame, and reset.
4. Activate the timer.
"
MISC (internal)
checkDirectory: pFileName | ps
"...returns a string that has the default directory name added to the beginning of pFileName (a String) unless pFileName already begins with a directory name in which case it is returned unchanged. max length of ~58"
[
ps ← (String new: 60) asStream.
[pFileName length > 0 and⦂ (pFileName◦1) = ('<'◦1)⇒ []
ps append: '<'; append: self directory; append: '>'].
ps append: pFileName.
[ps last = ('.'◦1)⇒ [ps skip: ¬1]].
⇑ps contents
]
directory
"...Returns a string specifying the default file name directory. The default directory is fDefaultDirectory if it is not nil, otherwise it is self name"
[
[ fDefaultDirectory ≡ nil ⇒
[ ⇑ self userName
]
⇑ fDefaultDirectory
].
]
hash: pString
"...returns a hash value for pString (a String)."
| tHash1 tHash2 i
[
tHash1 ← 0. tHash2 ← 0.
for⦂ i from: 1 to: (pString length) by: 2 do⦂
[ tHash1 ← tHash1 lxor: (UpperCase◦(pString◦i+1)).
tHash2 ← tHash2 lxor:
[ i = (pString length) ⇒ [ 040 ]
UpperCase◦(pString◦(i+1)+1)
].
].
⇑ (tHash1*256 + tHash2)
]
login: pName password: pPassword
"...sets the necessary request parameters and invokes doLogin to issue a login request. pName (a String) specifies the name of an account on the Juniper file system. pPassword (a String) specifies the password of the account."
| tRequest
[
tRequest ← self newRequestParameterBlock. "1"
tRequest
leader: 3 ← sLogin; "2"
leader: 4 ← 512; "3"
leader: 5 ← 7; "4"
leader: 6 ← (self hash: pPassword); "5"
nextDataBlockString ← pName; "6"
pupType ← sCustodian. "7"
⇑ (self doLogin: tRequest) "8"
]
"
1. Create a new request parameter block.
2. Set the command to Login.
3. Set the number of bytes per page to 512.
4. Set the Juniper version number to 7 (Nov 80).
5. Set the hashed account password to pPassword hashed.
6. Set the account name to pName.
7. Set the packet pup type to custodian.
8. Issue the request and return the result.
"
userName
"...returns the account name or a default (a String)."
[
[ fName ≡ nil ⇒ [ ⇑ super userName ]
⇑ fName
].
]
userPassword
"...returns the account password or a default (a String)."
[
[ fPassword ≡ nil ⇒ [ ⇑ super userPassword ]
⇑ fPassword
].
]
CLASS INIT
classInit
"...initializes the constants in gJuniperConstants
(see [ivy] <Juniper>4.4> CommonPineDefs.mesa).
Do the following to initialize:
Smalltalk declare: ↪gJuniperConstants as: (SymbolTable new init: 256).
JuniperInterface classInit."
| tIndex tConstant
[
for⦂ tConstant from: (self juniperConstants) do⦂
[ [ tConstant is: Integer ⇒
[ tIndex ← tConstant. ]
gJuniperConstants declare: tConstant as: tIndex.
tIndex ← tIndex+1.
].
].
]
juniperConstants
"...returns the set of constants used to specify requests to and to interpret results from the Juniper file system."
[
⇑
↪(
0250 "PineMsgType (Pup types)"
sRequest sResult sUnsolicited sCustodian sSync sPineAck sNoop
5 "DataRequest"
sReadPage sWritePage sSetLength sReadLength sCloseFile sDestroyAnonymousFile sReadData sWriteData sReadAttribute sWriteAttribute sSetWriteLock sSetReadLock sReleaseReadLock
32 "TransactionRequest"
sChangePassword sRoom sOpenFile sCreateAnonymousFile sFindFile sLockQuery sTransCompletionQuery
42 "ActivityRequest"
sLogout sCloseTransaction sAbortTransaction sLoginRequest
60 "DirectoryRequest"
sLookupFile sCreateFile sDestroyFile sRenameFile sNextFile sNextFewFiles
1 "ResultCode"
sCommandNak sCommandAck sHeresData sHeresEntry sHeresFileList sHeresLFH sHeresFile sHeresLength sLoginResponse sLogoutResponse sTransactionClosed sTransCompletionInfo sResourceData sHeresRoom
0 "Unsolicited Code"
sTransactionAborted sReadLockBroken
8 "CustodianCode"
sLogin sAddServer sResourceLocation
42 "PineErrorCode"
"TransTroubleCode"
sUnimplementedFeature sIllegalLoginAttempt sSoftwareVersionMismatch sNoSuchUser sPasswordMismatch sBytesPerPageUnacceptable sServerTooBusy sOutOfSpace sNoFilesHere sNoDirectoryHere sIllegalFileName sFileNotFound sFileAlreadyExists sTransactionAborting sNoSuchOpenFile sBigFilesNotImplemented sIllegalAttribute sReadAttributeProtectionError sUserAskedForIt sWriteAttributeProtectionError sPackNotOnDrive sTooManyOpenFiles sProtectionViolation sFileNotOnPack sPackFull sFileRefOutOfBounds sFileSizeExcessive sByteRangeExcessive sPackNotOnMachine sNoSuchTransaction sBrokenLock sInconvenientUnwind
"Retryable"
sSequenceNumberGap sRecoveryUnderWay sTransactionClosing sCongestion
120 "UserDetected"
sBlockTooLarge sNoRouteToServer sServerUnknown sBadBytesPerPage sInvalidRequest sPupGlitch sUnableToDecrypt sNoResponseToRequest sNewServerUnwilling sTransactionInUnknownState sBadTimeForRequests
)
]
TEST / DIAGNOSTIC
test
["
dpj ← nil.
dpj ← JuniperInterface new.
dpj release.
dpj open.
dpj close.
| f [ dpj open. f ← dpj file: 'test.test'.
until⦂ [ f end ] do⦂ [ user show: (f next) inString. ]. f close. dpj close. ]
JuniperSocket howMany 2 1 2 2 2 11 Timer howMany 33 NameUser howMany 1 1 1
"]
SystemOrganization classify: ↪JuniperInterface under: 'Juniper'.
JuniperInterface classInit
"JuniperPageBuffer"
Class new title: 'JuniperPageBuffer'
subclassof: EtherFilePage
fields: '
fLength
fPageNumber
fSerialNumber
'
declare: '';
asFollows
JUNIPER PAGE BUFFER
DOCUMENTATION
implementationNotes
[
"
FIELDS
fLength : an Integer specifiying the number of bytes in the page buffer.
fPageNumber : an Integer specifying the number of the page buffer.
fSerialNumber : an Integer specifying the serial number of the page buffer.
"]
FILE PAGE OPERATIONS
length
"...returns the number of bytes in the page buffer."
[
⇑ fLength
]
length: pLength
"...sets the number of bytes in the page buffer."
[
fLength ← pLength.
super length: pLength.
]
pageNumber
"...returns the page number of the page buffer."
[
⇑ fPageNumber
]
pageNumber: pPageNumber
"...sets the page number of the page buffer."
[
fPageNumber ← pPageNumber.
]
serialNumber
"...returns the serial number of the page buffer."
[
⇑ fSerialNumber
]
serialNumber: pSerialNumber
"...sets the serial number of the page buffer."
[
fSerialNumber ← pSerialNumber.
]
SystemOrganization classify: ↪JuniperPageBuffer under: 'Juniper'.
"JuniperParameterBlock"
Class new title: 'JuniperParameterBlock'
subclassof: Object
fields: '
fPacket
fDataBlockPosition
'
declare: '';
asFollows
JUNIPER PARAMETER BLOCK
DOCUMENTATION
implementationNotes
[
"
FIELDS
fPacket : a Pacbuf for sending request commands and parameters to the Juniper file server.
fDataBlockPosition : an Integer specifying the current position in the data block of fPacket.
"
]
OPERATIONS
dataBlockAdvance: pIncrement
"...advances the data block position by pIncrement (an Integer)."
[
fDataBlockPosition ← fDataBlockPosition + pIncrement.
]
dataBlockGet
"...returns the current data block as a Stream."
[
⇑ ( (Stream new)
of: (fPacket pupString)
from: fDataBlockPosition
to: ( 4 + (fPacket pupLength) - 2 )
)
]
dataBlockLength
"...returns the number of bytes (an Integer) in the data block."
[
⇑ ((fPacket pupLength) - 42)
]
dataBlockLength ← pLength
"...sets the number of bytes in the data block to pLength (an Integer)."
[
fPacket pupLength ← pLength + 42.
]
leader: pIndex
"...returns the leader word (an Integer) specified by pIndex (an Integer) from the packet."
[
⇑ (fPacket word: (12 + pIndex))
]
leader: pIndex ← pValue
"...sets pValue (an Integer) in the packet at the leader word specified by pIndex (an Integer)."
[
fPacket word: (12 + pIndex) ← pValue.
]
packet
"...returns the packet (a Pacbuf)."
[
⇑ fPacket
]
packet ← pPacket
"...sets the packet to pPacket (a Pacbuf) and resets the data block position."
[
fPacket ← pPacket.
fDataBlockPosition ← 45.
]
pupType
"...returns the pup type (an Integer) of the packet."
[
⇑ (fPacket pupType)
]
pupType ← pPupType
"...sets the pup type of the packet to pPupType (an Integer)."
[
fPacket pupType ← pPupType.
]
SystemOrganization classify: ↪JuniperParameterBlock under: 'Juniper'.
"JuniperRequestParameterBlock"
Class new title: 'JuniperRequestParameterBlock'
subclassof: JuniperParameterBlock
fields: ''
declare: '';
asFollows
JUNIPER REQUEST PARAMETER BLOCK
OPERATIONS
longInteger: pPosition ← pValue
"...sets pValue (4 bytes of long integer) in the packet at the parameter position specified by pPosition (an Integer)."
| tString tPosition
[
tString ← fPacket pupString.
tPosition ← (pPosition + 16)*2 - 1.
( (Stream new) of: tString from: tPosition to: (tPosition+3)
) nextNumber: 4 ← pValue.
tString
swap: tPosition with: tPosition+2;
swap: tPosition+1 with: tPosition+3.
]
"
1. the two words of a Juniper long integers are stored in reverse order from those in Smalltalk.
"
nextDataBlockString ← pString
"...sets pString (a String) in the data block at the current position and advances the position."
| tDataBlock tString tLength tMaxLength
[
fPacket pupLength ← 554. "1"
tDataBlock ← self dataBlockGet. "2"
tLength ← pString length. "3"
tMaxLength ← (tLength+1) land: 0177776. "4"
tDataBlock
nextword ← tLength; "5"
nextword ← tMaxLength; "6"
append: pString; "7"
next: (tMaxLength - tLength) ← 0. "8"
self dataBlockAdvance: (4 + tMaxLength). "9"
]
"
1. Set the length of the packet to the maximum size. The length of each string in the data block is specified in the data block itself.
2. Get the data block (as a Stream) from the current position to the end.
3. Get the length of pString.
4. Determine the length of pString including padding needed to make its length even.
5. Set the length of pString in the data block (2 bytes).
6. Set the length of pString with padding in the data block (2 bytes).
7. Set pString in the data block.
8. Add padding in the data block.
9. Advance the current data block position past length, maximum length, and pString.
"
opcode ← pOpcode
"...sets the request opcode field in the packet to pOpcode (an Integer)."
[
fPacket word: 16 ← pOpcode.
]
parameter: pIndex ← pValue
"...sets pValue (an Integer) in the packet at the request parameter field specified by pIndex (an Integer)."
[
fPacket word: (16+pIndex) ← pValue.
]
shortFileHandle ← pShortFileHandle
"...sets the request short file handle field in the packet to pShortFileHandle (an Integer)."
[
fPacket word: 15 ← pShortFileHandle.
]
SystemOrganization classify: ↪JuniperRequestParameterBlock under: 'Juniper'.
"JuniperResultParameterBlock"
Class new title: 'JuniperResultParameterBlock'
subclassof: JuniperParameterBlock
fields: ''
declare: '';
asFollows