]> wirehaze git hosting - MS-DOS.git/blob - v4.0/src/CMD/SORT/SORT.ASM

wirehaze git hosting

MZ is back!
[MS-DOS.git] / v4.0 / src / CMD / SORT / SORT.ASM
1 TITLE SORT FILTER FOR DOS
2 PAGE ,132 ;\ f
3 ;**********************************************************
4 ;*
5 ;* UTILITY NAME: sort
6 ;*
7 ;* SOURCE FILE NAME: sort.asm
8 ;*
9 ;* UTILITY FUNCTION:
10 ;*
11 ;* External non-resident utility, written in Assembler.
12 ;* Reads from the standard input device until end-of-file,
13 ;* sorts the data (up to 64k) and writes the results to
14 ;* the standard output device. Input and output can be
15 ;* redirected.
16 ;*
17 ;* INPUT (Command line)
18 ;*
19 ;* SORT [/R] [/+ n]
20 ;*
21 ;* /R - Sort in reverse order
22 ;* /+n - Start sorting in column "n" , default 1
23 ;*
24 ;* OUTPUT:
25 ;* Sorted data will be written to the standard output device.
26 ;*
27 ;* ERROR CONDITIONS:
28 ;* Incorrect DOS version
29 ;* Insufficient disk space on target
30 ;* Insufficient memory to allocate SORT buffer
31 ;* Invalid parameter
32 ;*
33 ;* INTERNAL REFERENCES:
34 ;* Main
35 ;*
36 ;* SOURCE HISTORY:
37 ;* Modification History:
38 ;* 3-18-83 MZ (Microsoft)
39 ;* Fix CR-LF at end of buffer
40 ;* Fix small file sorting
41 ;* Fix CR-LF line termination bug
42 ;* Comment the Damn source
43 ;*
44 ;* 6-23-86 RW (IBM)
45 ;* Add DOS 3.30 support for multiple languages
46 ;* Inclusion of common DOS VERSION check equate
47 ;*
48 ;* ;AN000; Code added in DOS 4.0
49 ;* 5-19-87 RW (IBM) (DOS 4.0)
50 ;* Addition of IBM Parser Service Routines
51 ;* Addition of DOS Message Retriever Service Routines
52 ;* Add code page file tag support
53 ;*
54 ;* ;AN001; Code added in DOS 4.0
55 ;* ;AN002; DCR 191
56 ;* 9-18-87 BL (IBM) (DOS 4.0)
57 ;* Added Extended Attribute support for code page checking
58 ;* and file type checking.
59 ;* ;AN003; PTM 1805
60 ;* 10-19-87 BL (IBM) (DOS 4.0)
61 ;*
62 ;* ;AN004; PTM
63 ;* 01-29-87 BL (IBM) (DOS 4.0)
64 ;* Ran tool INSPECT on .lst file for optimizations
65 ;**********************************************************
66
67 PAGE ;\f
68 ;-------------------------
69 ;--- Macro definitions
70 ;-------------------------
71 BREAK MACRO subtitle
72 SUBTTL subtitle
73 PAGE
74 ENDM
75
76
77
78 sys MACRO name ;system call macro
79 MOV AH,name
80 INT 21h
81 ENDM
82
83
84
85 save MACRO reglist ;push those registers
86 IRP reg,<reglist>
87 PUSH reg
88 ENDM
89 ENDM
90
91
92
93 restore MACRO reglist ;pop those registers
94 IRP reg,<reglist>
95 POP reg
96 ENDM
97 ENDM
98
99
100 PAGE ;\f
101 ;-------------------------------
102 ;--- Equates
103 ;-------------------------------
104 FALSE EQU 0
105 TRUE EQU NOT FALSE
106 MAXREC EQU 256 ;MAXIMUM NUL RECORD SIZE
107
108 SPACE EQU 0 ;Offset zero in the allocated block
109 BUFFER EQU MAXREC ;Offset MAXREC in the allocated block
110
111 RETCODE_NOERROR equ 0 ;AN000; DOS return code (errorlevel)
112 RETCODE_ERROR equ 1 ;AN000; DOS return code (errorlevel)
113
114 NO_CODEPAGE equ 0 ;AN000; Tag for files with no codepage
115
116 GetCPSW equ 3303h ;AN000; Int 021h function calls
117 GetExtAttr equ 5702h ;AN000;
118 SetExtAttr equ 5704h ;AN000;
119 ;-----------------------
120 ;-- Parser equates
121 ;-----------------------
122 EOL EQU -1 ;AN000; Indicator for End-Of-Line
123 NOERROR EQU 0 ;AN000; Return Indicator for No Errors
124
125 FarSW equ 0 ;AN000;
126 DateSW equ 0 ;AN000;
127 TimeSW equ 0 ;AN000;
128 FileSW equ 0 ;AN000;
129 CAPSW equ 0 ;AN000;
130 CmpxSW equ 0 ;AN000;
131 NumSW equ 1 ;AN000;
132 KeySW equ 0 ;AN000;
133 SwSW equ 1 ;AN000;
134 Val1SW equ 1 ;AN000;
135 Val2SW equ 0 ;AN000;
136 Val3SW equ 0 ;AN000;
137 DrvSW equ 0 ;AN000;
138 QusSW equ 0 ;AN000;
139
140 ;-----------------------
141 ;-- Message equates
142 ;-----------------------
143 STDIN equ 0
144 STDOUT equ 1
145 STDERR equ 2
146
147 Msg_NoMem equ 2 ;AC003;
148 Msg_NoDisk equ 4 ;AC003;
149 Msg_sort equ 5 ;AN003;
150 Msg_switch equ 3 ;AN003;
151
152 ;------------------------------
153 ; EXTENDED ATTRIBUTE Equates
154 ;------------------------------
155 EAISBINARY equ 02h ;AN001; ea_type
156 EASYSTEM equ 8000h ;AN001; ea_flags
157
158 PAGE ;\f
159 ;---------------------;
160 .xlist ;
161 .xcref ;
162 INCLUDE syscall.inc ;
163 INCLUDE sysmsg.inc ; ;AN000; Include message equates and MACROS
164 .cref ;
165 .list ;
166 ;---------------------;
167
168 MSG_UTILNAME <SORT> ;AN000;
169
170 SUBTTL Segments used in load order
171
172
173 CODE SEGMENT
174 CODE ENDS
175
176 CONST SEGMENT PUBLIC BYTE
177 CONST ENDS
178
179
180 ;-----------------------
181 ;--- Stack Segment
182 ;-----------------------
183 CSTACK SEGMENT STACK
184 db 128 DUP (0) ;initial stack to be clear
185
186 CSTACK ENDS
187
188
189
190 ;-------------------------------
191 ;--- Group
192 ;-------------------------------
193 DG GROUP CODE,CONST,CSTACK
194
195
196 ;-------------------------------
197 ;--- Code Segment
198 ;-------------------------------
199 CODE SEGMENT
200 ASSUME CS:DG,DS:DG,ES:NOTHING,SS:CSTACK
201
202 ;-------------------------------
203 ;--- Data Definition
204 ;-------------------------------
205 COLUMN dw 0 ;COLUMN TO USE FOR KEY + 1
206 cp_reset db FALSE ;AN000;Flag indicating if Code Page was reset on target file
207
208 ;------------------------------------------DOS 3.30 - Russ Whitehead
209 CTRY_INFO db ?
210 CTRY_TABLE_OFF dw ?
211 CTRY_TABLE_SEG dw ?
212 ;------------------------------------------
213
214 MSG_SERVICES <MSGDATA> ;AN000;
215 ASSUME ds:nothing
216
217 ;----------------------------------------
218 ;- STRUCTURE TO QUERY EXTENDED ATTRIBUTES
219 ;----------------------------------------
220 querylist struc ;AN001; ;query general list
221 qea_num dw 1 ;AN001;
222 qea_type db EAISBINARY ;AN001;
223 qea_flags dw EASYSTEM ;AN001;
224 qea_namelen db ? ;AN001;
225 qea_name db " " ;AN001;
226 querylist ends ;AN001;
227
228 cp_qlist querylist <1,EAISBINARY,EASYSTEM,2,"CP"> ;AN001; ;query code page attr.
229
230 cp_list label word ;AN001; ;code page attr. get/set list
231 dw 1 ;AN001; ; # of list entries
232 db EAISBINARY ;AN001; ; ea type
233 dw EASYSTEM ;AN001; ; ea flags
234 db ? ;AN001; ; ea return code
235 db 2 ;AN001; ; ea name length
236 dw 2 ;AN001; ; ea value length
237 db "CP" ;AN001; ; ea name
238 cp dw ? ;AN001; ; ea value (code page)
239 cp_len equ ($ - cp_list) ;AN001;
240
241 ;-------Save area for Code Pages
242 src_cp dw ? ;AN000; Save area for current code page
243 tgt_cp dw ? ;AN000; Save area for current code page
244 endlist label word ;AN000;
245
246 PAGE ;\f
247 ;******************************************************************************
248 ;* PARSER DATA STRUCTURES FOLLOW
249 ;******************************************************************************
250
251 ;------------------------------
252 ;- STRUCTURE TO DEFINE ADDITIONAL COMMAND LINE DELIMITERS
253 ;------------------------------
254 parms label word ;AN000;
255 dw parmsx ;AN000; POINTER TO PARMS STRUCTURE
256 db 1 ;AN000; DELIMITER LIST FOLLOWS
257 db 1 ;AN000; NUMBER OF ADDITIONAL DELIMITERS
258 db ";" ;AN000; ADDITIONAL DELIMITER
259
260 ;------------------------------
261 ;- STRUCTURE TO DEFINE SORT SYNTAX REQUIREMENTS
262 ;------------------------------
263 parmsx label word ;AN000;
264 db 0,0 ;AN000; THERE ARE NO POSITIONAL PARAMETERS
265 db 2 ;AN000; THERE ARE 2 SWITCHES (/R AND /+n)
266 dw sw1 ;AN000; POINTER TO FIRST SWITCH DEFINITION AREA
267 dw sw2 ;AN000; POINTER TO SECOND SWITCH DEFINITION AREA
268 dw 0 ;AN000; THERE ARE NO KEYWORDS IN SORT SYNTAX
269
270 ;------------------------------
271 ;- STRUCTURE TO DEFINE THE /R SWITCH
272 ;------------------------------
273 sw1 label word ;AN000;
274 dw 0 ;AN000; NO MATCH FLAGS
275 dw 0 ;AN000; NO FUNCTION FLAGS
276 dw switchbuff ;AN000; PLACE RESULT IN switchbufF
277 dw novals ;AN000; NO VALUE LIST
278 db 1 ;AN000; ONLY ONE SWITCH IN FOLLOWING LIST
279 rev_sw db "/R",0 ;AN000; /R INDICATES REVERSE SORT
280
281 ;------------------------------
282 ;- STRUCTURE TO DEFINE THE /+n SWITCH
283 ;------------------------------
284 NUMERIC equ 08000h ;AN000; Control flag for numeric value
285 NO_COLON equ 0020h ;AN000;
286
287 sw2 label word ;AN000;
288 dw NUMERIC ;AN000; MATCH_FLAGS
289 dw NO_COLON ;AN000; NO FUNCTION FLAGS
290 dw switchbuff ;AN000; PLACE RESULT IN switchbufF
291 dw valuelist ;AN000; NEED VALUE LIST FOR n
292 db 1 ;AN000; ONLY 1 SWITCH ON FOLLOWING LIST
293 col_sw db "/+",0 ;AN000; /+n INDICATES BEGIN SORT IN COLUMN n
294
295 ;------------------------------
296 ;- VALUE LIST DEFINITION FOR NO VALUES
297 ;------------------------------
298 novals label word ;AN000;
299 DB 0 ;AN000; VALUE LIST
300
301 ;------------------------------
302 ;- VALUE LIST DEFINITION FOR /+n
303 ;------------------------------
304 valuelist label word ;AN000;
305 db 1 ;AN000; ONE VALUE ALLOWED
306 db 1 ;AN000; ONLY ONE RANGE
307 db 1 ;AN000; IDENTIFY THE RANGE
308 dd 1,65535 ;AN000; USER CAN SPECIFY /+1 THROUGH /+65535
309
310 ;------------------------------
311 ;- RETURN BUFFER FOR SWITCH INFORMATION
312 ;------------------------------
313 switchbuff label word ;AN000;
314 sb_type db ? ;AN000; TYPE RETURNED
315 sb_item_tag db ? ;AN000; SPACE FOR ITEM TAG
316 sb_synonym dw ? ;AN000; ES:sb_synonym points to synonym
317
318 sb_value dw ? ;AN000; SPACE FOR VALUE
319 sb_value_extra dw ? ;AN000; UNUSED SPACE FOR VALUE
320
321 PAGE ;\f
322 ;**************************************************************
323 ;*
324 ;* SUBROUTINE NAME: main
325 ;*
326 ;* SUBROUTINE FUNCTION:
327 ;* Mainline routine, performs SYSLODMSG, calls routines to
328 ;* parse command line, performs the SORT and writes the
329 ;* results.
330 ;*
331 ;* INPUT:
332 ;* Command Line.
333 ;*
334 ;* File to be sorted will be read from Standard Input
335 ;* device handle 0.
336 ;*
337 ;* OUTPUT:
338 ;* Sorted data will be written to the Standard Output
339 ;* device handle 1.
340 ;*
341 ;* NORMAL EXIT:
342 ;* SORT will normally exit when data was successfully read
343 ;* in up to 64k or EOF, sorted, and displayed to the
344 ;* standard output device.
345 ;*
346 ;* ERROR EXIT:
347 ;* If any of the following errors, SORT will display the
348 ;* corresponding error message and terminate.
349 ;*
350 ;* Insufficient disk space on target device
351 ;* Incorrect DOS version
352 ;* Insufficient memory to sort
353 ;*
354 ;************************************************************
355
356 ;-------------------------
357 ; Preload messages
358 ;-------------------------
359 MSG_SERVICES <SORT.ctl,SORT.cla,SORT.cl1,SORT.cl2> ;AN000;
360 MSG_SERVICES <DISPLAYmsg,LOADmsg,CHARmsg,NOCHECKSTDIN> ;AN002; Make retriever services available
361
362 mov ax,cs ;AN003; ;load ES to the right area,
363 mov es,ax ;AN003;
364 mov ds,ax ;AN003;
365 SORT:
366 call sysloadmsg ;AN000; Preload messages, Check DOS Version.
367 ;If Inc DOS Ver or error loading messages,
368 ;SYSLOADMSG will show msg and terminate for us
369 jnc parser ;AN000; If no error, parse command line
370 call sysdispmsg ;AN000; There was error. Let SYSDISPMSG Display
371 cmp bx,-1 ;AN000; Is this DOS 1.0 or 1.1 ?
372 je OLD_ABORT ;AN000; Yes, terminate old way
373
374 mov ah,Exit ;AN000; No, terminate new way
375 mov al,0 ;AN000; Errorlevel 0 (Compatible!)
376 int 021h ;AN000; Bye bye!
377
378 OLD_ABORT: ;AN000; CS should point to PSP
379 mov ah,Abort ;AN000; Terminate program (AH=0)
380 int 021h ;AN000; Bye bye!
381 ;-----------------------------------
382 ;- DOS version is ok. Parse cmd line
383 ;-----------------------------------
384 PARSER: ;AN000; message and terminate
385 call parse ;AN000; Parse command line
386
387 ;-----------------------------------
388 ; set up column for proper sort offset
389 ;-----------------------------------
390
391 ADD COLUMN,2
392 CMP COLUMN,2
393 JZ GOT_COL
394 DEC COLUMN
395
396 ;------------------------------------
397 ; Get sorting area, no more than 64K
398 ;------------------------------------
399 GOT_COL:
400 MOV BX,1000H ;64K worth of paragraphs
401 GET_MEM:
402 mov bp,bx ;AN003; save buffer length
403 sys ALLOC ;allocate them from somewhere
404 JNC GOT_MEM ;if error, BX has amount free, try to get it
405 OR BX,BX ;but, is BX = 0?
406 JNZ GET_MEM ;nope, try to allocate it
407 JMP short SIZERR ;AN004; ;complain
408
409 GOT_MEM:
410 ;------------------------------------RussW:--Following add in DOS 3.3 for Nat Lang Support
411 push ax ;Save AX
412 push ds ;Save DS
413 push es ;Save ES
414 mov al,6 ;Function for Get collating sequence
415 mov bx,-1 ;Get active code page
416 mov dx,-1 ;Get info from active country
417 mov cx,5 ;Number of bytes to be returned
418 push cs ;Place code segment
419 pop es ;in ES
420 mov di,offset ctry_info ;Return area for 5 byte requested information
421 sys GetExtCntry ;Get extended country information
422 ;Ok, now copy the table in DOS to our segment
423 lds si,dword ptr cs:ctry_table_off
424 mov di,seg dg
425 mov es,di
426 mov di,offset dg:table
427 mov cx,word ptr [si]
428 add si,2
429 mov ax,256
430 sub ax,cx
431 add di,ax
432 cld
433 rep movsb
434 ;Done copying, so restore regs and cont
435 pop es ;Restore ES
436 pop ds ;Restore DS
437 pop ax ;Restore AX
438 ;------------------------------------RussW:--End 3.3 addition
439 MOV DS,AX ;Point DS to buffer
440 MOV ES,AX ;and point ES to buffer
441 MOV CL,4 ;2^4 bytes per paragraph
442 MOV BX,BP ;AN003; restore buffer length
443 SHL BX,CL ;Find out how many bytes we have
444 MOV BP,BX ;AN003; save buffer length in bytes
445
446 ;---------------------------
447 ; Clear out temporary record area
448 ;---------------------------
449 MOV CX,MAXREC/2 ;Size of temporary buffer (words)
450 MOV AX,' ' ;Character to fill with
451 XOR DI,DI ;AN004; ;Beginning of temp buffer
452 REP STOSW ;Blam.
453 ;-----------------------------------
454 ; Make sure source and target code pages are the same
455 ;-----------------------------------
456 call match_codepages ;AN000; Make sure codepages are the same
457 ;---------------------------
458 ; read in file from standard input
459 ;---------------------------
460 MOV DX,BUFFER + 2 ;DX = place to begin reading
461 MOV CX,BP ;AN003; ;CX is the max number to read
462 SUB CX,MAXREC + 2 ;remember offset of temp buffer
463 SORTL:
464 XOR BX,BX ;Standard input
465 sys READ ;Read it in
466 ADD DX,AX ;Bump pointer by count read
467 SUB CX,AX ;subtract from remaining the count read
468 JZ SIZERR ;if buffer is full then error
469 OR AX,AX ;no chars read -> end of file
470 JNZ SORTL ;there were chars read. go read again
471 JMP SHORT SIZOK ;trim last ^Z terminated record
472 SIZERR:
473 mov ax,msg_NoMem ;AN000; not enough memory error
474 mov dh,-1 ;AN003; class: utility error
475 call error_exit ;AN000; and write it out
476
477 ;---------------------------
478 ; Look for a ^Z. Terminate buffer at 1st ^Z.
479 ;---------------------------
480 SIZOK:
481 MOV BX,DX ;save end pointer
482 MOV CX,DX ;get pointer to end of text
483 SUB CX,BUFFER+2 ;dif in pointers is count
484 MOV AL,1AH ;char is ^Z
485 MOV DI,BUFFER+2 ;point to beginning of text
486 REPNZ SCASB ;find one
487 JNZ NoBack ;nope, try to find CRLF
488 DEC BX ;pretend that we didn't see ^Z
489 NoBack:
490 SUB BX,CX ;sub from endpointer the number left
491 SUB BX,2 ;Hope for a CR LF at end
492 CMP WORD PTR [BX],0A0Dh ;Was there one there?
493 JZ GOTEND ;yep, here is the end
494 ADD BX,2 ;nope, bump back to SCASB spot
495 CMP BYTE PTR [BX],AL ;Was there ^Z there?
496 JZ GOTEND ;yep, chop it
497 INC BX ;Nope, skip last char
498 GOTEND:
499 MOV BP,BX ;BP = filesize-2(CRLF)+temp buffer+2
500 MOV WORD PTR DS:[BP],0 ;0 at end of the file
501
502 ;---------------------------
503 ; We now turn the entire buffer into a linked list of chains by
504 ; replacing CRLFs with the length of the following line (with 2 for CRLF)
505 ;---------------------------
506 MOV BX,BUFFER ;pointer to line head (length)
507 MOV DI,BUFFER+2 ;pointer to line text
508 REPLACE_LOOP:
509 MOV AL,13 ;char to look for is CR
510 MOV CX,BP ;count = end pointer
511 SUB CX,DI ;chop off start point to get length
512 INC CX ;add 1???
513 REPLACE_SCAN:
514 REPNZ SCASB ;look for CR
515 JNZ REPLACE_SKIP ;count exhausted
516 CMP BYTE PTR [DI],10 ;LF there?
517 JNZ REPLACE_SCAN ;nope, continue scanning
518 REPLACE_SKIP:
519 MOV AX,DI ;AX to point after CR
520 DEC AX ;AX to point to CR
521 save <AX> ;save pointer
522 SUB AX,BX ;AX is length of line found
523 MOV [BX],AX ;stuff it in previous link
524 restore <BX> ;get pointer to next
525 INC DI ;skip LF???
526 JCXZ END_REPLACE_LOOP ;no more to scan -> go sort
527 JMP REPLACE_LOOP ;look for next
528
529 END_REPLACE_LOOP:
530 MOV WORD PTR [BX],0 ;terminate file with nul
531 LEA BP,[BX+2] ;remember the null line at end
532 MOV DI,BUFFER ;DI is start of unsorted section
533
534 ;---------------------------
535 ; begin sort. Outer loop steps over all unsorted lines
536 ;---------------------------
537 OUTER_SORT_LOOP:
538 MOV BX,DI ;BX is start of unsorted section
539 MOV SI,BX ;SI is scanning place link
540 CMP WORD PTR [BX],0 ;are we at the end of the buffer?
541 JNZ INNER_SORT_LOOP ;No, do inner process
542 JMP END_OUTER_SORT_LOOP ;yes, go dump out
543
544 ;---------------------------
545 ; BX points to best guy found so far. We scan through the sorted section
546 ; to find an appropriate insertion point
547 ;---------------------------
548 INNER_SORT_LOOP:
549 ADD SI,[SI] ;link to next fellow
550 MOV AX,[SI] ;get length of comparison guy
551 OR AX,AX ;test for end of buffer
552 JZ END_INNER_SORT_LOOP ;if zero then figure out insertion
553 save <SI,DI> ;save SI,DI
554 MOV DI,BX ;DI = pointer to tester link
555 SUB AX,COLUMN ;adjust length for column
556 JA AXOK ;more chars in tester than column?
557 XOR SI,SI ;AN004; ;point SI to blank area
558 MOV AX,MAXREC ;make AX be max length
559 AXOK:
560 MOV DX,[DI] ;get length of best guy
561 SUB DX,COLUMN ;adjust length for column
562 JA DXOK ;there are more chars after column
563 XOR DI,DI ;AN004; ;point air to a space
564 MOV DX,MAXREC ;really big record
565 DXOK:
566 MOV CX,AX ;AX is shortest record
567 CMP AX,DX ;perhaps DX is shorter
568 JB SMALL ;nope, leace CX alone
569 MOV CX,DX ;DX is shorter, put length in CX
570 SMALL:
571 ADD DI,COLUMN ;offset into record
572 ADD SI,COLUMN ;offset into other record
573 push bx
574 push ax
575 mov bx,offset dg:table
576 tloop: lodsb
577 xlat byte ptr cs:[bx]
578 mov ah,al
579 mov al,es:[di]
580 inc di
581 xlat byte ptr cs:[bx]
582 cmp ah,al
583 loopz tloop
584 pop ax
585 pop bx
586 restore <DI,SI> ;get head pointers back
587 JNZ TESTED_NOT_EQUAL ;didn't exhaust counter, conditions set
588 CMP AX,DX ;check string lengths
589 TESTED_NOT_EQUAL:
590
591 ;---------------------------
592 ; NOTE! jae is patched to a jbe if file is to be sorted in reverse!
593 ;---------------------------
594 CODE_PATCH label byte
595 JAE INNER_SORT_LOOP ;if this one wasn't better then go again
596 MOV BX,SI ;it was better, save header
597 JMP INNER_SORT_LOOP ;and scan again
598
599 END_INNER_SORT_LOOP:
600 MOV SI,BX ;SI is now the best person
601 CMP SI,DI ;check best for current
602 JZ END_INSERT ;best equals current, all done
603
604 ;---------------------------
605 ; SI points to best line found so far
606 ; DI points to a place to insert this line
607 ; DI is guaranteed to be < SI
608 ; make room for line at destination
609 ;---------------------------
610 MOV DX,[SI] ;get length of line
611 save <SI,DI> ;save positions of people
612 STD ;go right to left
613 MOV CX,BP ;get end of file pointer
614 SUB CX,DI ;get length from destination to end
615 MOV SI,BP ;start from end
616 DEC SI ;SI points to end of file
617 MOV DI,SI ;destination is end of file
618 ADD DI,DX ;DI points to new end of file
619 REP MOVSB ;blam. Move every one up
620 CLD ;back left to right
621 restore <DI,SI> ;get old source and destination
622 ;---------------------------
623 ; MOVE NEW LINE INTO PLACE
624 ;---------------------------
625 save <DI> ;save destination
626 ADD SI,DX ;adjust for previous movement
627 save <SI> ;save this value
628 MOV CX,DX ;get number to move
629 REP MOVSB ;blam. move the new line in
630 restore <SI,DI> ;get back destination and new source
631 ;---------------------------
632 ; DELETE LINE FROM OLD PLACE
633 ;---------------------------
634 save <DI> ;save destination
635 MOV CX,BP ;pointer to end
636 ADD CX,DX ;remember bump
637 SUB CX,SI ;get count of bytes to move
638 INC CX ;turn it into a word
639 SHR CX,1 ;or a count of words
640 MOV DI,SI ;new destination of move
641 ADD SI,DX ;offset of block
642 REP MOVSW ;blam, squeeze out the space
643 restore <DI> ;get back original destination
644 MOV WORD PTR DS:[BP-2],0 ;remake the end of file mark
645
646 END_INSERT:
647 ADD DI,[DI] ;link to next guy
648 JMP OUTER_SORT_LOOP ;and continue
649 ;------------------------------
650 ; PUT BACK IN THE CR-LF
651 ;------------------------------
652 END_OUTER_SORT_LOOP:
653 MOV DI,BUFFER ;start at beginning (where else)
654 MOV CX,[DI] ;count of butes
655
656 INSERT_LOOP:
657 ADD DI,CX ;point to next length
658 MOV CX,[DI] ;get length
659 MOV WORD PTR [DI],0A0DH ;replace length with CRLF
660 AND CX,CX ;AN004; ;check for end of file
661 JNZ INSERT_LOOP ;nope, try again
662
663 WRITE_FILE:
664 MOV DX,BUFFER+2 ;get starting point
665 MOV CX,BP ;pointer to end of buffer
666 SUB CX,DX ;dif in pointers is number of bytes
667 MOV BX,1 ;to standard output
668 sys WRITE ;write 'em out
669 JC BADWRT ;some bizarre error -> flag it
670 CMP AX,CX ;did we write what was expected?
671 JZ WRTOK ;yes, say bye bye
672 BADWRT:
673
674 ;;;;; mov ax,msg_NoDisk ;AN000; Strange write error
675 ;;;;; mov dh,-1 ;AN003; class: extended error
676 ;;;;; call error_exit ;AN000; Bye bye
677 mov al,RETCODE_ERROR ;AN000; return an error code (errorlevel)
678 sys EXIT ;AN000;
679 WRTOK:
680 MOV AL,RETCODE_NOERROR ;AN000; Errorlevel 0 (No error!)
681 sys EXIT ;bye!
682
683 PAGE ;\f
684 ;************************************************************
685 ;*
686 ;* SUBROUTINE NAME: display_msg
687 ;*
688 ;* SUBROUTINE FUNCTION:
689 ;* Display the requested message to the specified handle
690 ;*
691 ;* INPUT:
692 ;* 1) AX = Number of the message to be displayed.
693 ;* 2) BX = Handle to be written to.
694 ;*
695 ;* OUTPUT:
696 ;* The message corresponding to the requested msg number will
697 ;* be written to the requested handle. There is no substitution
698 ;* text in SORT.
699 ;*
700 ;* NORMAL EXIT:
701 ;* Message will be successfully written to requested handle.
702 ;*
703 ;* ERROR EXIT:
704 ;* None. Note that theoretically an error can be returned from
705 ;* SYSDISPMSG, but there is nothing that the application can do.
706 ;*
707 ;* INTERNAL REFERENCES:
708 ;* System Display Message service routines
709 ;*
710 ;* EXTERNAL REFERENCES:
711 ;* None
712 ;*
713 ;************************************************************
714
715 display_msg proc near ;AN000;
716 push ds ;AN000; save DS value
717 push cs ;AN000; get DS addressability
718 pop ds ;AN000;
719
720 xor cx,cx ;AN004; ;AN000; No substitution text
721 ;; mov dh,-1 ;AN003; Message class
722 ; 1=DOS Extended error
723 ; 2=DOS Parse error
724 ; -1=Utility message
725 mov dl,0 ;AN000; DOS INT 21H function number to use for input
726 ; 00H=No input, 01H=Keyboard input,
727 ; 07H=Direct Console Input Without Echo,
728 ; 08H=Console Input Without Echo, 0AH=Buffered Keyboard Input
729 call SYSDISPMSG ;AN000;
730
731 pop ds ;AN000; restore DS
732 ret ;AN000;
733 display_msg ENDP ;AN000;
734
735 PAGE ;\f
736 ;************************************************************
737 ;*
738 ;* SUBROUTINE NAME: parse
739 ;*
740 ;* SUBROUTINE FUNCTION:
741 ;* Call the DOS PARSE Service Routines to process the command
742 ;* line. Search for valid switches (/R and /+n) and take
743 ;* appropriate action for each. Display error message and
744 ;* terminate on error.
745 ;*
746 ;* INPUT: None
747 ;*
748 ;* OUTPUT: None
749 ;*
750 ;* NORMAL EXIT:
751 ;*
752 ;* If /R specified, then patches code to perform reverse sort
753 ;* by changing JAE to a JB.
754 ;*
755 ;* If /+n entered, COLUMN will be set to "n," otherwise COLUMN
756 ;* will be set to 1.
757 ;*
758 ;* ERROR EXIT:
759 ;*
760 ;* If user enters any parameter or switch other than /R or /+n,
761 ;* or an invalid value for "n", then this routine will display
762 ;* the "Invalid Parameter" error message and terminate with
763 ;* errorlevel 1.
764 ;*
765 ;* EXTERNAL REFERENCES:
766 ;* System parse service routines
767 ;* INT21 - GET PSP Function Call 062h
768 ;*
769 ;************************************************************
770
771 parse proc near ;AN000;
772
773 sys GetCurrentPSP ;AN000; Get PSP address, returned in BX
774
775 mov ds,bx ;AN000; Put PSP Seg in DS
776 mov si,081h ;AN000; Offset of command line in PSP
777 cmp byte ptr ds:080h,0 ;AN000; Check length of command line
778 je end_parse ;AN000; If 0 len, the we are done parsing
779 xor cx,cx ;AN000; Number of parms processed so far = 0
780 push cs ;AN000; Put CS
781 pop es ;AN000; in ES
782 ;---------------------------------
783 ;- Loop for each operand at DS:SI (Initially PSP + 081h)
784 ;---------------------------------
785 parse_loop: ;AN000;
786 mov di,offset parms ;AN000; Address of parse control block
787 xor dx,dx ;AN000; Reserved
788 call sysparse ;AN000; Parse parm at DS:SI
789 cmp ax,EOL ;AN000; Q: Are we at end of command line?
790 je end_parse ;AN000; YES: We are done
791 and ax,ax ;AN004; ;AN000; NO: Q: Any errors?
792 jne parse_error ;AN000; YES: Display msg and terminate
793 mov bx,sb_synonym ;AN000; Get offset of switch entered
794 ;----------------------------------
795 ;- If user said /R, then patch code
796 ;----------------------------------
797 cmp bx,offset rev_sw ;AN000; If user specified /R
798 jne check_column ;AN000;
799 mov cs:code_patch,072h ;AN000; Sleazy patch to make reverse order sort
800 jmp parse_loop ;AN000; Look for another parm
801
802 ;---------------------------------------------
803 ;- If user said /+n, then save COLUMN index
804 ;---------------------------------------------
805 check_column: ;AN000;
806 cmp bx,offset col_sw ;AN000; Q: Did user specified /+n ?
807 jne switch_error ;AC003; No: Unrecognized parm
808 mov ax,sb_value ;AN000; Yes: Get number entered by user
809 mov column,ax ;AN000; Set up column to begin sort
810 jmp parse_loop ;AN000; Check for next parm
811
812 ;------------------------------------------------------------
813 ;- If any other parameter specified, display message and die
814 ;------------------------------------------------------------
815 switch_error: ;AN003;
816 mov ax,Msg_switch ;AN003;
817 parse_error: ;AN000;
818 mov dh,2 ;AN003; class: parse error
819 call error_exit ;AN000; Terminate utility
820
821 end_parse: ;AN000;
822 ret ;AN000;
823 parse endp ;AN000;
824
825 PAGE ;\f
826 ;************************************************************
827 ;*
828 ;* SUBROUTINE NAME: error_exit
829 ;*
830 ;* SUBROUTINE FUNCTION:
831 ;* Displays the message number in AX to the standard
832 ;* error device, then terminates with errorlevel 1.
833 ;*
834 ;* INPUT: AX = Message number
835 ;*
836 ;* INTERNAL REFERENCES:
837 ;* display_msg
838 ;*
839 ;* EXTERNAL REFERENCES:
840 ;* INT 021h - Terminate Function 043h
841 ;*
842 ;************************************************************
843 error_exit proc near ;AN000;
844 call prt_sort ;AN003;
845 mov bx,STDERR ;AN000; output to standard error
846 xor cx,cx ;AN004; ;AN003;
847 call display_msg ;AN000; and write it out
848 mov al,RETCODE_ERROR ;AN000; return an error code (errorlevel)
849 sys EXIT ;AN000;
850 ret ;AN000; Meaningless RET
851 error_exit endp ;AN000;
852
853
854 ;************************************************************
855 ;*
856 ;* SUBROUTINE NAME: match_codepages
857 ;*
858 ;* SUBROUTINE FUNCTION:
859 ;* Check to see if Code Page Support is active. If so,
860 ;* check code page of input and output handles. If the
861 ;* source file has a code page file tag AND the target
862 ;* handles code page is different, then set code page
863 ;* of the target to that of the source.
864 ;*
865 ;* INTERNAL REFERENCES:
866 ;* none
867 ;*
868 ;* EXTERNAL REFERENCES:
869 ;* INT 021h - Check CPSW
870 ;* INT 021h - Get Extended Attributes by Handle
871 ;* INT 021h - Set Extended Attributes by Handle
872 ;*
873 ;************************************************************
874
875 match_codepages proc near ;AN000;
876
877 ;-----------------------------------
878 ; Check status of Code page support
879 ;-----------------------------------
880 push es ;AN000; Save ES register
881 push ds ;AN001; Save DS register
882 ;
883 mov ax,cs ;AN001; ES, DS -> CS
884 mov ds,ax ;AN001;
885 mov es,ax ;AN001;
886 ;
887 mov ax,GetCPSW ;AN000; Get CPSW state, assume support is OFF
888 int 021h ;AN000; DL: 0=NotSupported,1=Supported
889 cmp dl,1 ;AN000; CPSW supported if DL=1
890 jne done_cpsw ;AN000; If not supported, we're done
891 ;-----------------------------------
892 ; Get Code Pages of STDIN and STDOUT
893 ;-----------------------------------
894 mov ax,GetExtAttr ;AN000; Get Extended Attributes by Handle
895 mov bx,STDOUT ;AN000; For Standard output device
896 mov di,offset cp_list ;AC001; Return buffer address
897 mov si,offset cp_qlist ;AN001; Query the code page attribute
898 mov cx,cp_len ;AN001; return buffer length
899 int 021h ;AN000;
900 jc done_cpsw ;AN000; Error condition, let system handle
901 mov ax,cp ;AN000; Save target code page
902 mov tgt_cp,ax ;AN000; for later reference
903
904 mov ax,GetExtAttr ;AN000; Get Extended Attributes by Handle
905 xor bx,bx ;AN004; ;AN000; bx = STDIN (0) For Standard input device
906 mov di,offset cp_list ;AC001; Return buffer address
907 mov si,offset cp_qlist ;AN001; Query the code page attribute
908 mov cx,cp_len ;AN001; return buffer length
909 int 021h ;AN000;
910 jc done_cpsw ;AN000; Error condition, let system handle
911 mov ax,cp ;AN000; Save source code page
912 mov src_cp,ax ;AN000; for later reference
913
914 mov ax,src_cp ;AN000; Get source codepage
915 and ax,ax ;AN004; ;AN000; IF no codepage
916 je done_cpsw ;AN000; THEN no action required;
917 cmp ax,tgt_cp ;AN000; IF src_cp = tgt_cp
918 je done_cpsw ;AN000; THEN no action required;
919 ;-------------------------------------
920 ;- Set CP of target to that of source
921 ;-------------------------------------
922 mov cp_reset,TRUE ;AN000; Set flag indicating change
923 mov ax,SetExtAttr ;AN000; Set Extended Attributes by Handle
924 mov bx,STDOUT ;AN000; For Standard output device
925 mov di,offset cp_list ;AC001; Input buffer address
926 int 021h ;AN000;
927
928 done_cpsw: ;AN000;
929 pop ds ;AN001; Restore DS register
930 pop es ;AN000; Restore ES register
931 ret ;AN000;
932 match_codepages endp ;AN000;
933
934 PAGE ;\f
935 ;************************************************************
936 ;*
937 ;* SUBROUTINE NAME: prt_sort
938 ;*
939 ;* SUBROUTINE FUNCTION:
940 ;* Preceeds all error messages with "SORT: ".
941 ;*
942 ;* INTERNAL REFERENCES:
943 ;* none
944 ;* EXTERNAL REFERENCES:
945 ;* none
946 ;************************************************************
947 prt_sort proc near ;AN003;
948 push ax ;AN003;
949 push dx ;AN003;
950 ;
951 mov dh,-1 ;AN003;
952 mov ax,Msg_sort ;AN003;
953 xor cx,cx ;AN004; ;AN003;
954 mov bx,STDERR ;AN003;
955 call display_msg ;AN003;
956 ;
957 pop dx ;AN003;
958 pop ax ;AN003;
959 ;
960 ret ;AN003;
961 prt_sort endp ;AN003;
962
963
964 PAGE ;\f
965 ;--------------------
966 .xlist
967 .xcref
968 INCLUDE parse.asm
969 include msgdcl.inc
970 .cref
971 .list
972 ;--------------------
973
974 CODE ENDS
975
976
977
978
979
980
981
982 CONST SEGMENT PUBLIC BYTE
983
984 extrn table:byte
985
986 CONST ENDS
987
988
989
990
991 SUBTTL Initialized Data
992 ;-------------------------------
993 ;--- Stack Segment
994 ;-------------------------------
995 CSTACK SEGMENT STACK
996 db (362 - 80h) + 96 dup (0) ;(362 - 80h) == New - Old IBM
997 ;interrupt reqs. == size of growth
998 CSTACK ENDS
999
1000 END SORT