home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 5 Edit
/
05-Edit.zip
/
thesrc15.zip
/
sort.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-11-17
|
21KB
|
540 lines
/***********************************************************************/
/* SORT.C - Functions related to the SORT command. */
/***********************************************************************/
/*
* THE - The Hessling Editor. A text editor similar to VM/CMS xedit.
* Copyright (C) 1991-1993 Mark Hessling
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to:
*
* The Free Software Foundation, Inc.
* 675 Mass Ave,
* Cambridge, MA 02139 USA.
*
*
* If you make modifications to this software that you feel increases
* it usefulness for the rest of the community, please email the
* changes, enhancements, bug fixes as well as any and all ideas to me.
* This software is going to be maintained and enhanced as deemed
* necessary by the community.
*
* Mark Hessling email: M.Hessling@gu.edu.au
* 36 David Road Phone: +61 7 849 7731
* Holland Park Fax: +61 7 875 5314
* QLD 4121
* Australia
*/
/*
$Header: C:\THE\RCS\sort.c 1.4 1993/09/01 16:25:34 MH Interim MH $
*/
#include <stdio.h>
#include "the.h"
#include "proto.h"
static int cmp(); /* this deliberately has no prototype */
#define MAX_SORT_FIELDS 10
#define SF_ERROR 0
#define SF_START 1
#define SF_ORDER 2
#define SF_LEFT 3
struct sort_field
{
char order; /* A - ascending, D - descending */
int left_col; /* left column */
int right_col; /* right column */
};
typedef struct sort_field SORT_FIELD;
char *sort_field_1;
char *sort_field_2;
SORT_FIELD sort_fields[MAX_SORT_FIELDS];
int num_fields;
/*-------------------------- external data ----------------------------*/
extern VIEW_DETAILS *vd_current,*vd_first,*vd_mark;
extern char current_screen;
/***********************************************************************/
#ifdef PROTO
int execute_sort(char *params)
#else
int execute_sort(params)
#endif
/***********************************************************************/
{
/*-------------------------- external data ----------------------------*/
extern char in_profile; /* indicates if processing profile */
extern char *temp_cmd;
/*--------------------------- local data ------------------------------*/
#define SOR_PARAMS 32
char *word[SOR_PARAMS+1];
unsigned short num_params;
LINE **lfirst,**lp,*curr,*first,*last;
long num_lines,i,true_line;
int rc,max_column_width=0;
int left_col,right_col,state;
char order='A';
/*--------------------------- processing ------------------------------*/
#ifdef TRACE
trace_function("sort.c: execute_sort");
#endif
/*---------------------------------------------------------------------*/
/* Parse the parameters and set up the sort_fields[] array with valid */
/* values. */
/*---------------------------------------------------------------------*/
num_params = param_split(params,word,SOR_PARAMS,WORD_DELIMS,TEMP_PARAM);
true_line = get_true_line();
if ((num_lines = valid_target(word[0],true_line)) == TARGET_ERROR)
{
display_error(4,(char *)word[0]);
#ifdef TRACE
trace_return();
#endif
return(RC_TARGET_NOT_FOUND);
}
/*---------------------------------------------------------------------*/
/* Handle special targets of BLOCK and ALL. Set default columns to be */
/* ZONE settings if not sort field(s) or BLOCK specified and it is BOX */
/* BLOCK. */
/*---------------------------------------------------------------------*/
if (equal((char *)"all",word[0],3))
true_line = 1L;
if (equal((char *)"block",word[0],5))
{
if (marked_block(TRUE) != RC_OK)
{
#ifdef TRACE
trace_return();
#endif
return(RC_INVALID_ENVIRON);
}
num_lines = MARK_VIEW->mark_end_line-MARK_VIEW->mark_start_line+1L;
true_line = MARK_VIEW->mark_start_line;
}
/*---------------------------------------------------------------------*/
/* If the true_line is on the top or bottom of file lines and we are */
/* searching forward or backward respectively, set the true_line to be */
/* the next line in the appropriate direction and reduce the number of */
/* lines to sort by 1. */
/*---------------------------------------------------------------------*/
if (!equal((char *)"all",word[0],3))
{
if (CURRENT_VIEW->current_window == WINDOW_COMMAND
|| in_profile)
{
if (TOF(true_line))
{
true_line++;
if (!valid_integer(word[0]))
num_lines = (num_lines == 0) ? 0 : num_lines - 1;
}
if (BOF(true_line))
{
true_line--;
if (!valid_integer(word[0]))
num_lines = (num_lines == 0) ? 0 : num_lines + 1;
}
}
else
{
if (TOF(true_line) || BOF(true_line))
{
display_error(38,(char *)"");
#ifdef TRACE
trace_return();
#endif
return(RC_INVALID_ENVIRON);
}
}
}
/*---------------------------------------------------------------------*/
/* If the number of lines is negative, adjust the true line and make */
/* the number of lines positive. */
/*---------------------------------------------------------------------*/
if (num_lines < 0)
{
true_line = true_line + num_lines + 1;
num_lines = -num_lines;
}
/*---------------------------------------------------------------------*/
/* Don't need to do anything if < 2 lines to be sorted. */
/*---------------------------------------------------------------------*/
if (num_lines < 2)
{
display_error(55,(char *)"");
#ifdef TRACE
trace_return();
#endif
return(RC_OK);
}
/*---------------------------------------------------------------------*/
/* Process parameters differently, depending on the number... */
/* 1 parameter (target only) - if BOX BLOCK, then the sort field will */
/* be the block columns, otherwise ZONE */
/* settings will be used. */
/* 2 parameters (target & order) - same as above but also validate the */
/* ordering value. */
/* > 2 parameters (target & sort fields) - validate each parameter. */
/*---------------------------------------------------------------------*/
switch(num_params)
{
case 1:
case 2:
sort_fields[0].left_col = CURRENT_VIEW->zone_start;
sort_fields[0].right_col = CURRENT_VIEW->zone_end;
sort_fields[0].order = order;
num_fields = 1;
if (equal((char *)"block",word[0],5)
&& MARK_VIEW->mark_type == M_BOX)
{
sort_fields[0].left_col = MARK_VIEW->mark_start_col;
sort_fields[0].right_col = MARK_VIEW->mark_end_col;
}
/*---------------------------------------------------------------------*/
/* No more processing if only 1 parameter. */
/*---------------------------------------------------------------------*/
if (num_params == 1)
break;
/*---------------------------------------------------------------------*/
/* Processing for 2 parameters; validate ordering value. */
/*---------------------------------------------------------------------*/
if (equal((char *)"ascending",word[1],1)
|| equal((char *)"descending",word[1],1))
{
order = word[1][0];
if (islower(order))
order = toupper(order);
sort_fields[0].order = order;
break;
}
/*---------------------------------------------------------------------*/
/* If the parameter is not Ascending or Descending, display error. */
/*---------------------------------------------------------------------*/
display_error(1,(char *)word[1]);
#ifdef TRACE
trace_return();
#endif
return(RC_INVALID_OPERAND);
break;
/*---------------------------------------------------------------------*/
/* More than 2 parameters; sort field(s) are being specified. */
/*---------------------------------------------------------------------*/
default:
i = 1;
num_fields = 0;
state = SF_START;
while(1)
{
switch(state)
{
case SF_START:
if (equal((char *)"ascending",word[i],1)
|| equal((char *)"descending",word[i],1))
{
order = word[i][0];
if (islower(order))
order = toupper(order);
sort_fields[num_fields].order = order;
state = SF_ORDER;
i++;
break;
}
left_col = atoi(word[i]);
if (left_col == 0)
{
state = SF_ERROR;
break;
}
sort_fields[num_fields].order = order;
sort_fields[num_fields].left_col = left_col;
state = SF_LEFT;
i++;
break;
case SF_ORDER:
left_col = atoi(word[i]);
if (left_col < 1)
{
state = SF_ERROR;
break;
}
sort_fields[num_fields].left_col = left_col;
state = SF_LEFT;
i++;
break;
case SF_LEFT:
right_col = atoi(word[i]);
if (right_col < 1)
{
state = SF_ERROR;
break;
}
sort_fields[num_fields].right_col = right_col;
if (right_col < left_col)
{
state = SF_ERROR;
break;
}
state = SF_START;
i++;
num_fields++;
break;
default:
state = SF_ERROR;
break;
}
/*---------------------------------------------------------------------*/
/* If we have an error, display a message... */
/*---------------------------------------------------------------------*/
if (state == SF_ERROR)
{
display_error(1,(char *)word[i]);
#ifdef TRACE
trace_return();
#endif
return(RC_INVALID_OPERAND);
}
/*---------------------------------------------------------------------*/
/* If we have run out of parameters... */
/*---------------------------------------------------------------------*/
if (i == num_params)
{
/*---------------------------------------------------------------------*/
/* ...then if we have the correct number of parameters, OK. */
/*---------------------------------------------------------------------*/
if (state == SF_START)
break;
else
/*---------------------------------------------------------------------*/
/* ...otherwise display an error. */
/*---------------------------------------------------------------------*/
{
display_error(1,(char *)params);
#ifdef TRACE
trace_return();
#endif
return(RC_INVALID_OPERAND);
}
}
}
break;
}
/*---------------------------------------------------------------------*/
/* Determine the maximum length of a sort field. */
/*---------------------------------------------------------------------*/
for (i=0;i<num_fields;i++)
max_column_width = max(max_column_width,
sort_fields[i].right_col - sort_fields[i].left_col + 1);
/*---------------------------------------------------------------------*/
/* Allocate memory for num_lines of LINE pointers. */
/*---------------------------------------------------------------------*/
if ((lfirst = (LINE **)calloc(num_lines,sizeof(LINE *))) == NULL)
{
display_error(30,(char *)"");
#ifdef TRACE
trace_return();
#endif
return(RC_OUT_OF_MEMORY);
}
/*---------------------------------------------------------------------*/
/* Allocate memory for each of the temporary sort fields to the length */
/* of the maximum field width. */
/*---------------------------------------------------------------------*/
if ((sort_field_1 = (char *)malloc(max_column_width)) == NULL)
{
display_error(30,(char *)"");
#ifdef TRACE
trace_return();
#endif
return(RC_OUT_OF_MEMORY);
}
if ((sort_field_2 = (char *)malloc(max_column_width)) == NULL)
{
display_error(30,(char *)"");
#ifdef TRACE
trace_return();
#endif
return(RC_OUT_OF_MEMORY);
}
/*---------------------------------------------------------------------*/
/* Assign the values of the newly allocated array to the LINE pointers */
/* for the target lines. */
/*---------------------------------------------------------------------*/
#ifdef USE_VOID
first = curr = (LINE *)ll_find((void *)CURRENT_FILE->first_line,true_line);
#else
first = curr = lll_find(CURRENT_FILE->first_line,true_line);
#endif
lp = lfirst;
for (i=0;i<num_lines;i++)
{
lp[i] = curr;
curr = curr->next;
}
last = curr;
/*---------------------------------------------------------------------*/
/* Sort the target array... */
/*---------------------------------------------------------------------*/
qsort(lfirst,num_lines,sizeof(LINE *),cmp);
/*---------------------------------------------------------------------*/
/* Merge the sorted array pointers into the linked list... */
/*---------------------------------------------------------------------*/
lp = lfirst;
first->prev->next = lp[0];
for (i=0;i<num_lines;i++)
{
if (i == 0)
{
lp[0]->prev = first->prev;
lp[0]->next = lp[1];
}
else
if (i == num_lines - 1)
{
lp[num_lines-1]->next = last;
lp[num_lines-1]->prev = lp[num_lines-2];
last->prev = lp[num_lines-1];
}
else
{
lp[i]->next = lp[i+1];
lp[i]->prev = lp[i-1];
}
}
#if 0
lp->prev = first->prev;
lp->next = lp[1];
for (i=1;i<num_lines-1;i++)
{
lp[i]->next = lp[i+1];
lp[i]->prev = lp[i-1];
}
lp[num_lines-1]->next = last;
lp[num_lines-1]->prev = lp[num_lines-2];
last->prev = lp[num_lines];
#endif
/*---------------------------------------------------------------------*/
/* Free up the memory used for the sort fields and the target array. */
/*---------------------------------------------------------------------*/
free(sort_field_1);
free(sort_field_2);
free(lfirst);
/*---------------------------------------------------------------------*/
/* Increment alteration count... */
/*---------------------------------------------------------------------*/
if ((rc = increment_alt(CURRENT_FILE)) != RC_OK)
{
#ifdef TRACE
trace_return();
#endif
return(rc);
}
sprintf(temp_cmd,"%d line(s) sorted",num_lines);
display_error(0,temp_cmd);
#ifdef TRACE
trace_return();
#endif
return(RC_OK);
}
/***********************************************************************/
#ifdef PROTO
static int cmp(LINE **first,LINE **second)
#else
static int cmp(first,second)
LINE **first,**second;
#endif
/***********************************************************************/
{
/*--------------------------- local data ------------------------------*/
register int i,j;
int len,rc,right_col,left_col;
LINE *one = *first;
LINE *two = *second;
/*--------------------------- processing ------------------------------*/
/*---------------------------------------------------------------------*/
/* For each sort field defined in the array sort_fields, compare the */
/* value of both lines for the specified column range. */
/*---------------------------------------------------------------------*/
for (i=0;i<num_fields;i++)
{
/*---------------------------------------------------------------------*/
/* Calculate the length of the sort field. */
/*---------------------------------------------------------------------*/
len = sort_fields[i].right_col - sort_fields[i].left_col + 1;
/*---------------------------------------------------------------------*/
/* Set the two temporary fields to blanks. */
/*---------------------------------------------------------------------*/
memset(sort_field_1,' ',len);
memset(sort_field_2,' ',len);
/*---------------------------------------------------------------------*/
/* For the first line to be compared, extract the portion of the line */
/* that corresponds with the current sort column... */
/*---------------------------------------------------------------------*/
right_col = min(sort_fields[i].right_col,one->length);
left_col = min(sort_fields[i].left_col,one->length);
/*---------------------------------------------------------------------*/
/* If the sort column lies after the end of the line, leave the */
/* contents of the sort field blank. */
/*---------------------------------------------------------------------*/
if (sort_fields[i].left_col <= one->length)
memcpy(sort_field_1,one->line+left_col-1,right_col-left_col+1);
/*---------------------------------------------------------------------*/
/* For the next line to be compared, extract the portion of the line */
/* that corresponds with the current sort column... */
/*---------------------------------------------------------------------*/
right_col = min(sort_fields[i].right_col,two->length);
left_col = min(sort_fields[i].left_col,two->length);
/*---------------------------------------------------------------------*/
/* If the sort column lies after the end of the line, leave the */
/* contents of the sort field blank. */
/*---------------------------------------------------------------------*/
if (sort_fields[i].left_col <= two->length)
memcpy(sort_field_2,two->line+left_col-1,right_col-left_col+1);
/*---------------------------------------------------------------------*/
/* If CASE IGNORE is on for the current view, set both sort fields to */
/* uppercase for the comparison. */
/*---------------------------------------------------------------------*/
if (CURRENT_VIEW->case_sort == CASE_IGNORE)
{
for (j=0;j<len;j++)
{
if (islower(sort_field_1[j]))
sort_field_1[j] = toupper(sort_field_1[j]);
if (islower(sort_field_2[j]))
sort_field_2[j] = toupper(sort_field_2[j]);
}
}
/*---------------------------------------------------------------------*/
/* If the two sort fields are equal, continue the sort with the next */
/* sort field value. If the sort fields are different, return with the */
/* the comparison value (if ASCENDING) or the comparison value negated */
/* (if DESCENDING). */
/*---------------------------------------------------------------------*/
if ((rc = strncmp(sort_field_1,sort_field_2,len)) != 0)
return((sort_fields[i].order == 'A') ? rc : -rc);
}
/*---------------------------------------------------------------------*/
/* To get to here, the result of sorting on all fields has resulted in */
/* both lines being equal. Return with 0 to indicate this. */
/*---------------------------------------------------------------------*/
return(0);
}