123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 |
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- /** 2022.10.04 this includes exercize 5-15/5-16 working on 5-17 **/
- /**
- errata
- undefined behaviour when you use -n on non numeric data
- */
- #define MAXLINES 20000 /* max #lines to be sorted */
- // int readlines(char *lineptr[], int nlines);
- // void writelines(char *lineptr[], int nlines);
- #define MAXLEN 1000 /* max length of any input line */
- #define ERR_TOOBIG 1 /** too big */
- #define ARGUMENT_PROBLEM 2 /** something wrong in commandline arguments given */
- int getline1(char *, int);
- char *lineptr[MAXLINES]; /* pointers to text lines */
- int readlines(char *lineptr[], int nlines);
- void writelines(char *lineptr[], int nlines);
- void swap(void *v[], int, int);
- void qsort1(void *v[], int left, int right, int noSort, int caseInsensitive, int directory,
- int (*comp)(void *, void *, int, int));
- int numcmp(char *, char *, int, int);
- int isAlphaNumeric(char);
- //helper function for zero()
- void zerome(int*);
- //initializes an array of ints
- void zero (int*, int);
- //helper function to reverse an array v
- // why hasn't this got a function prototype? @TODO
- void reverse(void *v[], int left, int right)
- {
- int i, last;
- if (left >= right)
- return;
- swap(v,left,right);
- reverse(v,left+1,right-1);
- }
- void zerome(int* c)
- {
- *c = 0;
- }
- void zero (int* a, int len)
- {
- for (int i =0; i<len; i++)
- zerome( a+i );
- // apparently this doesn't work
- // zerome( a+sizeof(i)*i );
- }
- /* sort input lines */
- int main(int argc, char *argv[])
- {
- int strcmp1(char *s, char *t);
- int strcmp2(char *s, char *t, int u, int v);
- int fieldcount = 1; // cardinal. we always start with 1
- // step 1 - figure out how many fields we're talking about here
- for (int i=0; i< argc; i++)
- if ( strcmp1(argv[i], "-l") ==0 ) fieldcount++;
- int help = 0; // only one help will get us help.
- // ok now we've got a number of fields we have to deal with
- // we need their positions, as to skip them
- //sets field[0] as 0
- //turns out we were one too high at fieldcount+1
- int field [fieldcount] ;
- zero(field,fieldcount);
-
- // length of fields, they can be different
- int fieldlen [fieldcount] ;
- zero(fieldlen,fieldcount);
- for (int i=0; i< argc; i++)
- {
- if ( strcmp1(argv[i], "-l") ==0 )
- {
- field[i+1] = i; //we need this position
- // @TODO needs sanity checking
- if ((i+1) > argc)
- {
- printf ("-l out of bounds");
- return ARGUMENT_PROBLEM;
- }
- fieldlen[i+1]=atoi(argv[i+1]); //is this the right format?
- /*
- -l l1 -l l2 -l l3 -l l4 -l l5
- -------|-------------|-|-|-------|
- l1 l2 l3l4 l5
- */
- }
- }
- /* flags*/
- int numeric[fieldcount];
- int backwards[fieldcount];
- int caseInsensitive[fieldcount];
- int directory[fieldcount];
- int noSort[fieldcount];
- //initialize
- zero(numeric,fieldcount);
- zero(backwards,fieldcount);
- zero(caseInsensitive,fieldcount) ;
- zero(directory,fieldcount);
- zero(noSort,fieldcount);
- /** WIP */
- for (int i=0, j=0; i < fieldcount; i++) //we're iterating fields here. we start with 0.
- {
- int nextfield = field[i]; //we go from j to nextfield
-
- for (; j<argc; j++) //we only iterate to the end of either argv OR the field
- //we are iterating on argv so argv[j] is the thing we are using
- {
- if (j==nextfield && nextfield != 0)
- i++;
- // printf("*** argc %d %s \n",argc,argv[i]);
- noSort[i] |= (strcmp1(argv[j], "-x") == 0);
- numeric[i] |= (strcmp1(argv[j], "-n") == 0);
- caseInsensitive[i] |= (( strcmp1(argv[j], "-f") == 0 ) || ( strcmp1(argv[j], "-df") ==0 ));
- backwards[i] |= ( strcmp1(argv[j], "-r") ==0 ) ;
- directory[i] |= (( strcmp1(argv[j], "-d") ==0 ) || ( strcmp1(argv[j], "-df") ==0 ));
- // we don't have to check for -l because we already did that
-
- // only one of these
- help |= (( strcmp1(argv[j], "-h") ==0 ) || ( strcmp1(argv[j], "--help") ==0 ));
- }
- // j++; //do we need this??
- }
-
- if (help)
- {
- printf ("./qsort [-l OPTIONS]* \n");
- printf (" OPTIONS: \n");
- printf (" -x : don't actually sort fields \n");
- printf (" -n : sort by number \n");
- printf (" -r : reverse (5-14) \n");
- printf (" -f : fold lower/upper case together (5-15) \n");
- printf (" -d : sort as if directory structure (5-16) \n");
- printf (" -fd : -f and -d (5-16) \n");
- printf (" -l N demarcates the next field by N \n");
- return(0); //bail
- }
- printf("fieldcount %d\n",fieldcount);
-
- //shotgun debugging flags
- for (int i = 0; i < fieldcount; i++)
- {
- printf ("numeric, %d \ncaseInsensitive %d\nbackwards %d\nnosort %d\ni %d\ndirectory %d\n\n",numeric[i],caseInsensitive[i],backwards[i],noSort[i],i,directory[i]);
- }
- int nlines = -1; /* number of input lines read */
-
- int first = 0;
- int last = -1;
- if ((nlines = readlines(lineptr, MAXLINES)) >= 0) //read lines
- {
- for (int i =0 ; i < fieldcount; i++) //is fieldcount 0 or 1 here if there's none?
- {
- first = field[i];
- //what happens if someone uses -l 0?
- if (fieldlen[0]==0) //we didn't get a -l
- {
- last = nlines-1;
- }
- else
- last = first+fieldlen[i];
- if (last == 0)
- last = nlines-1;
-
- //do the sort
- //something's wrong here on the normal sort
- qsort1( (void**) lineptr, first, last, noSort[i], caseInsensitive[i], directory[i],
- (int (*)(void*,void*,int,int)) (numeric ? numcmp : strcmp2 ) );
- //this part seems to work
- if(backwards[i])
- reverse((void**)lineptr, first, last);
- }
-
- //output
- writelines(lineptr, nlines);
-
- return 0;
- } else {
- printf("input %d too big to sort, contact devs with this error message \n",nlines);
- return ERR_TOOBIG;
- }
- }
- /* qsort: sort v[left]...v[right] into increasing order */
- void qsort1(void *v[], int left, int right, int noSort, int caseInsensitive, int directory,
- int (*comp)(void *, void *, int, int))
- {
- if (noSort) return; // do nothing on no sort
- int i, last;
- if (left >= right) /* do nothing if array contains */
- return; /* fewer than two elements */
- swap(v, left, (left + right)/2);
- last = left;
- for (i = left+1; i <= right; i++)
- if ((*comp)(v[i], v[left], caseInsensitive, directory) < 0)
- swap(v, ++last, i);
- swap(v, left, last);
- qsort1(v, left, last-1, noSort, caseInsensitive, directory, comp);
- qsort1(v, last+1, right, noSort, caseInsensitive, directory, comp);
- }
- /** includes blanks */
- int isAlphaNumeric(char a)
- {
- return
- (
- ( (a >= 'A') && (a <= 'Z') ) ||
- ( (a >= '0') && (a <= '9') ) ||
- ( (a >= 'a') && (a <= 'z') ) ||
- (a == ' ')
- );
- }
- /* strcmp: return <0 if s<t, 0 if s==t, >0 if s>t */
- int strcmp2(char *s, char *t, int caseInsensitive, int dirmode)
- {
- for ( ; *s == *t; s++, t++)
- if (*s == '\0')
- return 0;
- int ci= caseInsensitive;
- char str = *s;
- //if c is lowercase
- char stt = *t;
- // 4 cases
- // shouldn't affect dirmode since both str and stt *are both* alphanumeric here
- if (ci)
- {
- if (((str >= 'a') && (str <= 'z'))
- && ((stt >= 'a') && (stt <= 'z'))) //s and t are both lowercase
- return *s - *t; //compare as normal
- if ((str >= 'a') && (str <= 'z'))
- return *s - *t -32; // s is but t isn't... drop s down
- if ((stt >= 'a') && (stt <= 'z'))
- return *s - *t +32; // t is but s isn't... drop t up
- }
- if (dirmode)
- //check to make sure we're dealing with
- // assumption: treat all non alphanumeric, non space, non numbers as the same, ie we're not checking them
- // for 5-16
- {
- // what if there's only one? This isn't specified by the question.
- if (isAlphaNumeric(str) || isAlphaNumeric(stt))
- return *s - *t;
- // all other characters are equal
- return (char)0;
- }
- return *s - *t; //neither? then it doesn't matter
- }
- int strcmp1(char *s, char *t)
- {
- int u=1; //case insensitive desirable here?
- int v=0; //dirmode not needed here
- return strcmp2(s,t,u,v);
- }
- /* writelines: write output lines */
- void writelines(char *lineptr[], int nlines)
- {
- while (nlines-- > 0)
- printf("%s\n", *lineptr++);
- }
- /* numcmp: compare s1 and s2 numerically */
- int numcmp(char *s1, char *s2, int dud, int dud2)
- {
- //dud, dud2 is not used
- double v1, v2;
- v1 = atof(s1);
- v2 = atof(s2);
- if (v1 < v2)
- return -1;
- else if (v1 > v2)
- return 1;
- else
- return 0;
- }
- int getline1(char s[],int lim)
- {
- int c, i;
- for (i=0; i < lim-1 && (c=getchar())!=EOF && c!='\n'; ++i)
- s[i] = c;
- if (c == '\n') {
- s[i] = c;
- ++i;
- }
- s[i] = '\0';
- return i;
- }
- /* readlines: read input lines */
- int readlines(char *lineptr[], int maxlines)
- {
- int len, nlines;
- char *p, line[MAXLEN];
- nlines = 0;
- while ((len = getline1(line, MAXLEN)) > 0)
- {
- p = malloc(sizeof(char)*len);
- if (nlines >= maxlines || p == NULL)
- return -1;
- else {
- line[len-1] = '\0'; /* delete newline */
- strcpy(p, line);
- lineptr[nlines++] = p;
- }
- }
- return nlines;
- }
- /* swap: interchange v[i] and v[j] */
- void swap(void *v[], int i, int j)
- {
- void *temp;
- temp = v[i];
- v[i] = v[j];
- v[j] = temp;
- }
|