1 TITLE SORT FILTER FOR
MS-DOS
5 ; /+n -> sort on column n
7 ; Written by: Chris Peters
9 ; Modification History:
10 ; 3-18-83 MZ Fix CR-LF at end of buffer
11 ; Fix small file sorting
12 ; Fix CR-LF line termination bug
13 ; Comment the Damn source
18 ;NOTE: "internat" must be false if KANJI version
28 sys
MACRO name
; system call macro
32 save
MACRO reglist
; push those registers
37 restore MACRO reglist
; pop those registers
43 MAXREC EQU
256 ; MAXIMUM NUL RECORD SIZE
45 SPACE EQU
0 ; Offset zero in the allocated block
46 BUFFER EQU MAXREC
; Offset MAXREC in the allocated block
48 SUBTTL Segments
used in load order
54 CONST
SEGMENT PUBLIC BYTE
58 DB 128 DUP (0) ; initial stack to be clear
61 DG GROUP
CODE,CONST
,CSTACK
64 ASSUME
CS:DG
,DS:NOTHING
,ES:NOTHING
,SS:CSTACK
66 COLUMN
DW 0 ; COLUMN TO USE FOR KEY + 1
71 ; check for proper version number of system
74 XCHG AH,AL ; Turn it around to AH.AL
75 CMP AX,200H
; Version 2.00 only
77 MOV DX,OFFSET DG
:BADVER
; Get error message
78 PUSH CS ; Get DS addressability
80 sys STD_CON_STRING_OUTPUT
; Send to STDOUT
81 PUSH ES ; long segment
82 PUSH COLUMN
; offset zero
84 RET ; long return to OS
87 ; get proper switch character
90 MOV AL,0 ; Get current switch character
96 MOV SI,80H
; pointer to command line
97 CLD ; go left to right
100 MOV CL,AL ; CX = length of command line
102 CALL GET_CHAR
; get a character
103 CMP AL,SWITCH
; beginning of switch?
104 JNZ SWITCH_LOOP
; No, get next character
105 CALL GET_CHAR
; get 1st char of switch
106 CMP AL,'+' ; Column to sort?
107 JZ SWITCH_NUMBER
; Yes, parse a number
108 OR AL,20h
; convert to lower case
109 CMP AL,'r' ; Reverse sort?
110 JNZ SWITCH_LOOP
; No, get next switch
111 MOV CS:CODE_PATCH
,72h
; sleaze JAE into JB
112 JMP SWITCH_LOOP
; get next switch
114 MOV COLUMN
,0 ; start off at 0
116 CALL GET_CHAR
; get supposed digit
117 SUB AL,'0' ; convert to number
118 JB SWITCH_LOOP
; less than '0'
119 CMP AL,9 ; is it a valid digit?
120 JA SWITCH_LOOP
; nope, get next switch
121 CBW ; make it a full word
122 MOV BX,AX ; save byte away
123 MOV AX,10 ; decimal number system
124 MUL COLUMN
; take previous result
125 ADD AX,BX ; add in low order digit
126 MOV COLUMN
,AX ; save away value
127 JMP SWITCH_NEXT_NUMBER
; get next character
129 JCXZ END_GET
; End of line
130 DEC CX ; dec char count
131 LODSB ; get the character
134 POP AX ; nuke return on stack
136 ; set up column for proper sort offset
145 ; Get sorting area, no more than 64K
148 MOV BX,1000H
; 64K worth of paragraphs
150 sys ALLOC
; allocate them from somewhere
151 JNC GOT_MEM
; if error, BX has amount free, try to get it
152 OR BX,BX ; but, is BX = 0?
153 JNZ GET_MEM
; nope, try to allocate it
154 JMP SIZERR
; complain
157 MOV DS,AX ; Point DS to buffer
158 MOV ES,AX ; and point ES to buffer
159 MOV CL,4 ; 2^4 bytes per paragraph
160 SHL BX,CL ; Find out how many bytes we have
163 ; clear out temporary record area
165 MOV CX,MAXREC
/2 ; Size of temporary buffer (words)
166 MOV AX,' ' ; Character to fill with
167 MOV DI,SPACE
; Beginning of temp buffer
170 ; read in file from standard input
172 MOV DX,BUFFER
+ 2 ; DX = place to begin reading
173 MOV CX,BX ; CX is the max number to read
174 SUB CX,MAXREC
+ 2 ; remember offset of temp buffer
176 XOR BX,BX ; Standard input
177 sys READ
; Read it in
178 ADD DX,AX ; Bump pointer by count read
179 SUB CX,AX ; subtract from remaining the count read
180 JZ SIZERR
; if buffer is full then error
181 OR AX,AX ; no chars read -> end of file
182 JNZ SORTL
; there were chars read. go read again
183 JMP SHORT SIZOK
; trim last ^Z terminated record
185 MOV SI,OFFSET DG
:ERRMSG
; not enough memory error
187 PUSH CS ; DS addressability
190 MOV CX,AX ; put into appropriate register
191 MOV DX,SI ; get output destination
192 MOV BX,2 ; output to standard error
193 sys WRITE
; and write it out
194 MOV AL,1 ; return an error code
198 ; Look for a ^Z. Terminate buffer at 1st ^Z.
201 MOV BX,DX ; save end pointer
202 MOV CX,DX ; get pointer to end of text
203 SUB CX,BUFFER
+2 ; dif in pointers is count
204 MOV AL,1
AH ; char is ^Z
205 MOV DI,BUFFER
+2 ; point to beginning of text
206 REPNZ SCASB ; find one
207 JNZ NoBack
; nope, try to find CRLF
208 DEC BX ; pretend that we didn't see ^Z
210 SUB BX,CX ; sub from endpointer the number left
211 SUB BX,2 ; Hope for a CR LF at end
212 CMP WORD PTR [BX],0A0Dh ; Was there one there?
213 JZ GOTEND
; yep, here is the end
214 ADD BX,2 ; nope, bump back to SCASB spot
215 CMP BYTE PTR [BX],AL ; Was there ^Z there?
216 JZ GOTEND
; yep, chop it
217 INC BX ; Nope, skip last char
219 MOV BP,BX ; BP = filesize-2(CRLF)+temp buffer+2
220 MOV WORD PTR DS:[BP],0 ; 0 at end of the file
222 ; We now turn the entire buffer into a linked list of chains by
223 ; replacing CRLFs with the length of the following line (with 2 for CRLF)
225 MOV BX,BUFFER
; pointer to line head (length)
226 MOV DI,BUFFER
+2 ; pointer to line text
228 MOV AL,13 ; char to look for is CR
229 MOV CX,BP ; count = end pointer
230 SUB CX,DI ; chop off start point to get length
233 REPNZ SCASB ; look for CR
234 JNZ REPLACE_SKIP
; count exhausted
235 CMP BYTE PTR [DI],10 ; LF there?
236 JNZ REPLACE_SCAN
; nope, continue scanning
238 MOV AX,DI ; AX to point after CR
239 DEC AX ; AX to point to CR
240 save
<AX> ; save pointer
241 SUB AX,BX ; AX is length of line found
242 MOV [BX],AX ; stuff it in previous link
243 restore <BX> ; get pointer to next
245 JCXZ END_REPLACE_LOOP
; no more to scan -> go sort
246 JMP REPLACE_LOOP
; look for next
249 MOV WORD PTR [BX],0 ; terminate file with nul
250 LEA BP,[BX+2] ; remember the null line at end
251 MOV DI,BUFFER
; DI is start of unsorted section
254 ; begin sort. Outer loop steps over all unsorted lines
257 MOV BX,DI ; BX is start of unsorted section
258 MOV SI,BX ; SI is scanning place link
259 CMP WORD PTR [BX],0 ; are we at the end of the buffer?
260 JNZ INNER_SORT_LOOP
; No, do inner process
261 JMP END_OUTER_SORT_LOOP
; yes, go dump out
264 ; BX points to best guy found so far. We scan through the sorted section
265 ; to find an appropriate insertion point
268 ADD SI,[SI] ; link to next fellow
269 MOV AX,[SI] ; get length of comparison guy
270 OR AX,AX ; test for end of buffer
271 JZ END_INNER_SORT_LOOP
; if zero then figure out insertion
272 save
<SI,DI> ; save SI,DI
273 MOV DI,BX ; DI = pointer to tester link
274 SUB AX,COLUMN
; adjust length for column
275 JA AXOK
; more chars in tester than column?
276 MOV SI,SPACE
; point SI to blank area
277 MOV AX,MAXREC
; make AX be max length
279 MOV DX,[DI] ; get length of best guy
280 SUB DX,COLUMN
; adjust length for column
281 JA DXOK
; there are more chars after column
282 MOV DI,SPACE
; point air to a space
283 MOV DX,MAXREC
; really big record
285 MOV CX,AX ; AX is shortest record
286 CMP AX,DX ; perhaps DX is shorter
287 JB SMALL
; nope, leace CX alone
288 MOV CX,DX ; DX is shorter, put length in CX
290 ADD DI,COLUMN
; offset into record
291 ADD SI,COLUMN
; offset into other record
293 REPZ CMPSB ; compare every one
298 mov bx,offset dg
:table
300 xlat byte ptr cs:[bx]
304 xlat byte ptr cs:[bx]
310 restore <DI,SI> ; get head pointers back
311 JNZ TESTED_NOT_EQUAL
; didn't exhaust counter, conditions set
312 CMP AX,DX ; check string lengths
315 ; note! jae is patched to a jbe if file is to be sorted in reverse!
317 CODE_PATCH
LABEL BYTE
318 JAE INNER_SORT_LOOP
; if this one wasn't better then go again
319 MOV BX,SI ; it was better, save header
320 JMP INNER_SORT_LOOP
; and scan again
323 MOV SI,BX ; SI is now the best person
324 CMP SI,DI ; check best for current
325 JZ END_INSERT
; best equals current, all done
328 ; SI points to best line found so far
329 ; DI points to a place to insert this line
330 ; DI is guaranteed to be < SI
331 ; make room for line at destination
333 MOV DX,[SI] ; get length of line
334 save
<SI,DI> ; save positions of people
335 STD ; go right to left
336 MOV CX,BP ; get end of file pointer
337 SUB CX,DI ; get length from destination to end
338 MOV SI,BP ; start from end
339 DEC SI ; SI points to end of file
340 MOV DI,SI ; destination is end of file
341 ADD DI,DX ; DI points to new end of file
342 REP MOVSB ; blam. Move every one up
343 CLD ; back left to right
344 restore <DI,SI> ; get old source and destination
346 ; MOVE NEW LINE INTO PLACE
348 save
<DI> ; save destination
349 ADD SI,DX ; adjust for previous movement
350 save
<SI> ; save this value
351 MOV CX,DX ; get number to move
352 REP MOVSB ; blam. move the new line in
353 restore <SI,DI> ; get back destination and new source
355 ; DELETE LINE FROM OLD PLACE
357 save
<DI> ; save destination
358 MOV CX,BP ; pointer to end
359 ADD CX,DX ; remember bump
360 SUB CX,SI ; get count of bytes to move
361 INC CX ; turn it into a word
362 SHR CX,1 ; or a count of words
363 MOV DI,SI ; new destination of move
364 ADD SI,DX ; offset of block
365 REP MOVSW ; blam, squeeze out the space
366 restore <DI> ; get back original destination
367 MOV WORD PTR DS:[BP-2],0 ; remake the end of file mark
370 ADD DI,[DI] ; link to next guy
371 JMP OUTER_SORT_LOOP
; and continue
373 ; PUT BACK IN THE CR-LF
376 MOV DI,BUFFER
; start at beginning (where else)
377 MOV CX,[DI] ; count of butes
380 ADD DI,CX ; point to next length
381 MOV CX,[DI] ; get length
382 MOV WORD PTR [DI],0A0DH ; replace length with CRLF
383 CMP CX,0 ; check for end of file
384 JNZ INSERT_LOOP
; nope, try again
387 MOV DX,BUFFER
+2 ; get starting point
388 MOV CX,BP ; pointer to end of buffer
389 SUB CX,DX ; dif in pointers is number of bytes
390 MOV BX,1 ; to standard output
391 sys WRITE
; write 'em out
392 JC BADWRT
; some bizarre error -> flag it
393 CMP AX,CX ; did we write what was expected?
394 JZ WRTOK
; yes, say bye bye
396 MOV SI,OFFSET dg
:ERRMSG2
; strange write error
397 JMP ERROR_EXIT
; bye bye
399 XOR AL,AL ; perfect return (by convention)
404 CONST
SEGMENT PUBLIC BYTE
405 EXTRN BADVER
:BYTE,ERRMSG
:BYTE,ERRMSG2
:BYTE
411 SUBTTL Initialized
Data