// SORT.VDM -- Sorting multi-line records such as a mailing list. // // Originally by: Thomas C. Burt, Greenview Data, Inc. // Last change: 04/29/98 // // Requires: VEDIT 5.00 or later. // // From VEDIT: Select {MISC, More macros, SORT}. // // From OS: VEDIT -X SORT.VDM [-Nn] // // Or: VEDIT -X SORT.VDM [-Nn] infile -a outfile // // where option 'N' specifies the # lines in each record to be sorted. // If 'n' == "0", records are presumed to be blank-line separated. // // Note: Sorting is case sensitive if {CONFIG, Search options, // Default case sensitive} is enabled. // //////////////// // #85 = 2 // Prompt for record size when no "-Nnn" parameter // #85 = 0 // Default is blank-line separated records // #85 = n // Default is n-line records // // where 'n' = 1, 3, 4, ... // // Edit Buffer Usage // // [1] Output file // [2] Input file // // Text Register Usage // // 103 Temporary // 104 Temporary // // Numeric Register Usage // // 71 ID of source edit buffer // 72 ID of sorted edit buffer // 73 temporary // 74 Save Config(U_CFG_ALL) setting // 75 Flag whether sorting is case sensitive // 81 ID of T-Reg this macro executes in // 82 ID of T-Reg holding 1st line of record being sorted // 83 temporary // 84 Size of input file-header // 85 Default # lines per record option // 86 # lines per record = function(N_Option,#85,User) // 87 ID of T-Reg used as macro stripper then as file-header saver // 88 Flag that current edit buffer's file being sorted w/o prompts // 90 Records-processed counter // 91 Lowest file position for binary search // 92 Highest file position for binary search // 93 Extra file position into input file (for block delete) // 95 File position for inserting current record // 97 Return value from last Compare() // 98 Flag - give "BREAK-OUT" message // Num_Push(70,99) // Save Numeric regs #81 = Macro_Num // Store ID of T-Reg this macro is in #74 = Config(U_CFG_ALL,0) // Reset/save Config All Buffers value // // Determine whether case sensitive sorting. // if (Config(SR_CASE_OPT)) { #75=CASE } else { #75=0 } // WCRE($,10,BOTTOM) // Uncomment for debugging aid // WCRE(2,Win_Cols/2,RIGHT) // UPDATE // WS($) // ? // // Setup register to strip comments from the macro for runtime efficiency. // #87 = Reg_Free Reg_Set(#87," Reg_Ins(#81,BEGIN) // Copy macro into work buffer Search(/:START:/,COUNT,2) Line(1) Del_Line(-ALL) // Erase initializing commands Replace(`|}|Y|N`,``,NOERR+ALL) // Remove full-line comments Replace(`//|B|Y|>`,``,BEGIN+NOERR+ALL) // Remove comments from EOL Replace(`|W|>`,``,BEGIN+NOERR+ALL) // Remove trailing blanks Replace(`|<|N`,``,BEGIN+NOERR+ALL) // Remove empty lines Reg_Copy_Block(#81,0,EOB_Pos) // Overwrite source register Buf_Quit(OK) // Discard work buffer Chain(#81) // Chain to stripped macro ") // // Chain to comment-stripping-register for optimal time/size operation. // Buf_Switch(Buf_Free(EXTRA),LOCAL) // Switch to work buffer Config(F_F_TYPE,0,LOCAL) Chain(#87) // Chain to comment stripper // // Start SORT macro. // :START: #82 = Reg_Free() // #82 = available T-Reg ID # Win_Clear() Message(" ************************************ * * * SORT.VDM - 04/29/98 * * File Sorter * * * ************************************ ") Type_Newline(1) if (File_Size > 0) { // If file already loaded... #71 = Buf_Num // Source #72 = Buf_Free // Sink #83 = Config(F_F_TYPE) // File type #84 = Config(F_REC_HEAD) // File header size #88 = 1 // Flag no file prompts Config(F_OVER_MODE,0) // Allow normal edit operations } else { // Else prompt for source filename #72 = Buf_Num // Sink the current (empty) buffer #71 = Buf_Free // Source the next available buffer #88 = 0 // Flag that user prompted for files repeat (ALL) { // LOOP until valid filename Get_Input(103,"Please enter name of file to be sorted: ",NOCR) #83 = 0 // Reset flag if (Reg_Size(103)==0) {#83 = 1; Reg_Set(103,"*") } Get_Filename(103,@103) if (File_Exist(@103)==0) { Message("File not found. Try again.\n") } else { if (#83) { Reg_Type(103) } Break } } Buf_Switch(#71,LOCAL) // Switch to input buffer File_Open_Read(@103) // Open the source file for reading File_Read(0) // Read in one buffer's worth #83 = Config(F_F_TYPE) // File type #84 = 0 // File header size if (#83 > 2) { Message("\nInput file has fixed length records.") Message("\nPlease enter # bytes per record: ") #83 = Get_Num() // #83 = record length Config(F_F_TYPE,#83,LOCAL) // Set record length Config(F_OVER_MODE,0,LOCAL) // Allow normal edit operations Message("\nPlease enter # bytes in any file header") Message("\nOr press if none: ") #84 = Get_Num() // #84 = file-header size Config(F_REC_HEAD,#84,LOCAL) } } Reg_Empty(#87) // Empty macro-stripping intermediary if (#84 > 0) { Reg_Copy_Block(#87,0,#84,DELETE) // Move header into T-Reg Config(F_REC_HEAD,0,LOCAL) // Reconfigure for no header } #73 = File_Size Buf_Switch(#72,LOCAL) // Switch to sink if (#88) { File_Open_Write("VMSORT.TMP") } else { Get_Input(104,"\nPlease enter filename for sorted output ( if same): ",NOCR) if (Reg_Size(104)==0) { // Open sink file File_Open_Write(@103) } else { File_Open_Write(@104) } } Config(F_F_TYPE,#83,LOCAL) Config(F_OVER_MODE,0,LOCAL) #86 = N_Option if (N_Option < 0) { if (#85 <> 2) { #86 = #85 } else { Message("\nPress for records separated by blank lines") Message("\nOr enter # lines per record: ") #86 = Get_Num() } } Buf_Switch(#71,LOCAL) BOF() if (#86 == 0) { Match("|[|W]|N",ADVANCE+ALL) // Scan over any empty/blank lines BOL() // In case record begins with spaces Del_Line(-ALL) // Delete initial blank/empty lines } Message("Sorting. Please wait... \n") // time #90 = 0 // Init record counter #95 = 0 // Init insertion PTR at B-O-F #96 = 0 // Points past last inserted record ////////////////////////////////////////////////// // // // Sort loop for each record. // // // ////////////////////////////////////////////////// repeat (ALL) { // // Put 1st line of next source record into Reg[#82]. // Quit when source file is empty. // Buf_Switch(#71,LOCAL) // Switch to input file buffer if (At_EOF) { // If at E-O-F, sorting is done #98 = 0 // Suppress "BREAK-OUT" error if (#88==0) { // If explicitly prompted filenames Buf_Quit(OK) // Empty and release input buffer } Break } if (Reg_Size(#71+BUFFER)<2000) { File_Read(0) } // Keep input file buffer full // // Put 1st line of next record into Reg[#82]. // if (#86==0) { // For blank-line separated records Match("|N|[|W]",ADVANCE+ALL) // Scan over empty/blank lines } BOL() Del_Line(-ALL) Reg_Copy(#82,1) // Copy 1st line into Reg[#82] // // Determine optimized "floor" (#91) and "ceiling" (#92) values. // Buf_Switch(#72,LOCAL) // Switch to "output" file Goto_Pos(#95) // Goto start of Last Record Inserted if (Compare(#82,#75)<>1) { // If Record Being Sorted >= LRI #91 = #96 // Set floor just past LRI repeat (ALL) { // Loop to find ceiling/drag floor if (At_EOF) { // If at end of file #92 = Cur_Pos // Set ceiling break // and exit loop } if (!At_EOB) { Goto_Pos(EOB_Pos) // Goto End Of Buffer Line(-1,NOERR) // Goto start of last line in buffer // Then backup to start of record if (#86) { // Fixed record length? Line(-((Cur_Line-1)%#86)) // Modulus to start of record } else { // Else blank-line separated records repeat (ALL) { // Reverse scan over blank lines Match("|W",ADVANCE) if (Match("|N",ADVANCE)!=0) { Break } Line(-2) } repeat (ALL) { // Backup to beginning of record Line(-1,ERRBREAK) Match("|W",ADVANCE) if (Match("|N",ADVANCE)==0) { break } } } #95 = Cur_Pos if (Compare(#82,#75) == 1) { //If RBS < LRIB #92 = #95 // set ceiling break // and exit loop } else { // else #91 = #95 // drag floor } } Char(10000) // Take 1 giant step forward } } else { #92 = #95 // Set ceiling at LRI repeat (ALL) { // Loop to find floor/drag ceiling if (At_BOF) { // If at beginning of file #91 = 0 // Set floor break // and exit loop } if (!At_BOB) { Goto_Pos(BOB_Pos) // Goto Beginning Of Buffer #95 = Cur_Pos if (!At_BOF) { // Goto First (full) Record In Buffer if (#86) { Line(#86 - ((Cur_Line-1)%#86), NOERR) } else { if (Search("|N|N",ADVANCE+NOERR)!=0) { // Advance past next blank line Match("|N|[|W]",ADVANCE+ALL) // Skip any blank/empty lines BOL() // In case record begins with spaces } else { // If no final end of record EOF() // Goto end of file } } if ((#95 = Cur_Pos) >= #92) { // In case we're at ceiling Char(-10000) // Back up 1 giant step Continue // and try again } } } if (Compare(#82,#75) <> 1) { //If RBS >= FRIB #91 = #95 // set floor break // and exit loop } else { // else #92 = #95 // drag ceiling } Char(-10000) // Take 1 giant step backwards } } // // Binary search loop. // while (#91 <> #92) { // Until no more room... Goto_Pos((#91 + #92) >> 1) // Go midway between floor & ceiling BOL() // Goto start of line // Then backup to start of record if (#86) { // Fixed record length? Line(-((Cur_Line-1)%#86)) // Modulus to start of record } else { // Else blank-line separated records repeat (ALL) { // Reverse scan over blank lines Match("|W",ADVANCE) if (Match("|N",ADVANCE)!=0) { Break } Line(-2) } repeat (ALL) { // Backup to beginning of record Line(-1,ERRBREAK) Match("|W",ADVANCE) if (Match("|N",ADVANCE)==0) { break } } } #95 = Cur_Pos // Save start of current record Compare(#82,#75) // Compare 1st lines of each record // // Key < current key: lower ceiling. // if (Return_Value==1) { #92 = #95 // Lower ceiling Continue // Binary search } // // Key >= current key: raise floor. // Goto_Pos(#95) // Goto start of current record if (#86) { // If # lines per record is defined Line(#86,NOERR) // Advance record-length lines } else { // Else look for blank line separator if (Search("|N|N",ADVANCE+NOERR)!=0) { // Advance past next blank line Match("|N|[|W]",ADVANCE+ALL) // Skip any blank/empty lines BOL() // In case record begins with spaces } else { // If no final end of record EOF() // Goto end of file } } #91 = #95 = Cur_Pos // Raise floor } // Repeat Binary Search Loop ////////////////////////////////////////// // // // Insert sorted record. // // // ////////////////////////////////////////// Buf_Switch(#71,LOCAL) // Goto source buffer #91 = Cur_Pos // #91 = source record start if (#86) { // If fixed length records Line(#86,NOERR) // Move to next record #92 = #93 = Cur_Pos // Copy/delete end markers are equal } else { if (Search("|N|N",ADVANCE+NOERR)==0) { // Scan for blank/empty line // If final empty line not found... End_Of_File() Char(-Newline_Chars) if (Match("|N",ADVANCE)!=0) { // If not even a single ... End_Of_File() Ins_Newline() // Add 1st } Ins_Newline() // Add 2nd #92 = #93 = Cur_Pos // Copy & delete through 2d } else { // Else... #92 = Cur_Pos // Include empty line in copy Match("|N|[|W]",ADVANCE+ALL) // Scan over extra empty/blank lines BOL() #93 = Cur_Pos // Delete extra empty/blank lines } } Reg_Copy_Block(0,#91,#92,DELETE) // Move record to Reg[0] Buf_Switch(#72,LOCAL) // Reenter output buffer Goto_Pos(#95) Reg_Ins(0) // Insert record into output file #96 = Cur_Pos // For determining next "floor" #90++ // Count the record Win_Hor(1) Num_Type(#90,NOCR) // Update/display counter } // Repeat Main Loop ////////////////////////////////////////// // // // Macro Termination // // // ////////////////////////////////////////// if (#88) { // If default files were sorted Buf_Switch(#71,LOCAL) // Enter source file EOF() // Goto the (nearest) end Del_Line(-ALL) // Erase source file #73 = Mem_Free - 1000 // Maximum size of "source" buffer Buf_Switch(#72) // Enter output file BOF() // Goto the beginning repeat (ALL) { // Loop if (At_EOF) { // If at end of output file Buf_Quit(OK) // Release it break // Exit this loop } File_Read(0) // Ensure full buffer if (EOB_Pos > #73) { Goto_Pos(#73) Line(0) File_Write(0,REVERSE) } Buf_Switch(#71) // Switch to original source file File_Write(ALL) // Write out all previous contents Reg_Ins(#72+BUFFER) // Copy in a buffer of sorted output Buf_Switch(#72,LOCAL) // Switch to the "output" file Goto_Pos(EOB_Pos) // Go to end of buffer Del_Line(-ALL) // Delete now copied contents } // Repeat loop Buf_Switch(#71) // Switch to sorted output copy EOF() // Go to the end of the file } else { Buf_Switch(#71,LOCAL) // Enter source buffer Buf_Quit(OK) // Release the source file Buf_Switch(#72) // Enter output buffer } Reg_Empty(#82) // Empty T-Reg used locally if (#84>0) { // If input file had a file header Config(F_REC_HEAD,Reg_Size(#87),LOCAL) // Configure output file BOF() Reg_Ins(#87) // Copy input file's header Reg_Empty(#87) // Empty header-holding T-Reg } Config(U_CFG_ALL,#74) Num_Pop(70,99) // Restore numeric regs Break_Out(EXTRA) // Exit to visual mode