#!/usr/local/bin/kermit +
;
; m e r g e
; 
; Merges any number of files, each of which is already sorted in ascending
; order, into a new file that is also sorted in ascending order.  Blank
; lines are discarded.  The result file is sent to standard output.  The
; files may be of different lengths, including empty.  UNIX usage, in which
; standard output is redirected to a file:
;
;   merge file1 file2 file3 ... > output-file
;
; Requires:    C-Kermit 7.0 or later.
; Illustrates: FOPEN/FREAD/FCLOSE operating on multiple files at once.
;
; Note syntax of comparison at bottom of FOR loop.  The record array
; reference is accessed through \fcontents() to prevent unwanted processing
; of backslashes in the record, and enclosed in braces to preserve leading
; and trailing spaces.
;
; In this example, the sort key is the whole record.  To use other sort keys
; or orders, modify the comparison statement accordingly.  Other modifications
; would be needed for discarding duplicate records, flagging sequence errors
; in source files, etc.
;
; Author: F. da Cruz, the Kermit Project, Columbia University, July 1999
;
local \%i \%n \&c[] \&r[]             ; Local variables

.\%n ::= (\v(argc)-1)                 ; Number of files from command line

dcl \&c[\%n]                          ; Channel numbers for each file
dcl \&r[\%n]                          ; Current record from each file

for \%i 1 \%n 1 {                     ; Loop through command-line arguments
    .\&c[\%i] = -1                    ; Remember which files are not open
    fopen /read \&c[\%i] \&_[\%i]     ; Try to open this one
}
while 1 {                             ; Loop till done
    .\%k = -1                         ; Index of next output record
    set flag off                      ; No records yet
    for \%i 1 \%n 1 {                 ; For each file...
        if < \&c[\%i] 0 continue      ; Skip this one if it's not open
	while not def \&r[\%i] {      ; Skip blank lines
            if \f_eof(\&c[\%i]) {     ; If at end of file
                fclose \&c[\%i]       ; close this file
                .\&c[\%i] = -1        ; and mark it as closed
                break
            }
            fread \&c[\%i] \&r[\%i]   ; Otherwise read a record
        }    
        if < \&c[\%i] 0 continue      ; Next file if this file closed above
        if not flag {                 ; First record this round
            .\%k := \%i               ; Remember its index
            set flag on               ; Not first record any more
            continue                  ; On to next file
        }
        ; Lexically compare this record with the current smallest record
	if ( llt {\fcont(\&r[\%i])} {\fcont(\&r[\%k])} ) .\%k := \%i
    }
    if ( < \%k 0 ) break              ; If no more records we're done.
    echo \&r[\%k]                     ; Otherwise output this record
    undef \&r[\%k]                    ; and force it to be replaced.
}
exit 0
