Jump to: navigation, search

The C Programming Language, 2nd Edition, by Kernighan and Ritchie
Exercise 5.15 on page 121

Add the option -f to fold upper and lower case together, so that case distinctions are not made during sorting; for example, a and A compare equal.



Solution by Franz Fritsche

/* K&R Exercise 5-15 */

#include <stdio.h>
#include <string.h>
#include <ctype.h>

#define MAXLINES 5000     /* max #lines to be sorted */
char *lineptr[MAXLINES];  /* pointers to text lines */

int readlines(char *lineptr[], int nlines);
void writelines(char *lineptr[], int nlines);

void my_qsort(void *lineptr[], int left, int right,
            int (*comp)(void *, void *), int order);
            
int numcmp(char *, char *);
int strcmp_f(char *, char *);

/* sort input lines */
int main(int argc, char *argv[])
{
    int nlines;        /* number of input lines read */
    int numeric = 0;   /* 1 if numeric sort */
    int reverse = 0;   /* 1 if sorting in reverse order */
    int foldcase = 0;  /* 1 if sorting case insensitive */

    while (--argc > 0)
    {
        if (strcmp(*++argv, "-n") == 0)
            numeric = 1;
        else if (strcmp(*argv, "-r") == 0)
            reverse = 1;
        else if (strcmp(*argv, "-f") == 0)
            foldcase = 1;
    }

    if ((nlines = readlines(lineptr, MAXLINES)) >= 0)
    {
        my_qsort((void **) lineptr, 0, nlines-1,
            (int (*)(void *, void *))(numeric ? numcmp : (foldcase ? strcmp_f : strcmp)), 
            reverse ? -1 : 1);
        writelines(lineptr, nlines);
        return 0;
    }
    else
    {
        printf("input too big to sort\n");
        return 1;
    }
    
    return 0;
}

#define MAXLEN 1000  /* max length of any input line */

int getline(char *, int);
char *alloc(int);

/* readlines:  read input lines */
int readlines(char *lineptr[], int maxlines)
{
    int len, nlines;
    char *p, line[MAXLEN];

    nlines = 0;
    while ((len = getline(line, MAXLEN)) > 0)
        if (nlines >= maxlines || (p = alloc(len)) == NULL)
            return -1;
        else
        {
            line[len-1] = '\0';  /* delete newline */
            strcpy(p, line);
            lineptr[nlines++] = p;
        }

    return nlines;
}

/* writelines:  write output lines */
void writelines(char *lineptr[], int nlines)
{
    int i;

    for (i = 0; i < nlines; i++)
        printf("%s\n", lineptr[i]);
}

/* getline:  read a line into s, return length  */
int getline(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;
    s[i] = '\0';

    return i;
}

#define ALLOCSIZE 10000 /* size of available space */

static char allocbuf[ALLOCSIZE]; /* storage for alloc */
static char *allocp = allocbuf;  /* next free position */
   
char *alloc(int n)    /* return pointer to n characters */
{
    if (allocbuf + ALLOCSIZE - allocp >= n)  /* it fits */
    {
        allocp += n;
        return allocp - n;  /* old p */
    }
    else    /* not enough room */
        return 0;
}

#include <stdlib.h>

/* numcmp:  compare s1 and s2 numerically */
int numcmp(char *s1, char *s2)
{
    double v1, v2;

    v1 = atof(s1);
    v2 = atof(s2);
    if (v1 < v2)
        return -1;
    else if (v1 > v2)
        return 1;
    else
        return 0;
}

/* strcmp_f */
int strcmp_f(char *s, char *t)
{
    for ( ; toupper(*s) == toupper(*t); s++, t++)
        if (*s == '\0')
            return 0;

    return toupper(*s) - toupper(*t);
}
  
/* my_qsort:  sort v[left]...v[right] */
void my_qsort(void *v[], int left, int right,
            int (*comp)(void *, void *), int order)
{
    int i, last;

    void swap(void *v[], int, int);

    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 (order * (*comp)(v[i], v[left]) < 0)
            swap(v, ++last, i);
    swap(v, left, last);
    my_qsort(v, left, last-1, comp, order);
    my_qsort(v, last+1, right, comp, order);
}

void swap(void *v[], int i, int j)
{
    void *temp;

    temp = v[i];
    v[i] = v[j];
    v[j] = temp;
}


Solution by codybartfast (Category 0)

This uses a different approach, it creates a 'key' from the line and then sorts on that key. So if folding, the key will be an upper case copy of the line. The orignal line, not the key, is printed.

char *fold(char *value);
char *makekey(char *value, int dofold);
char **keyvaluepair(char *key, char *value);
char *key(char *pair[]);
char *value(char *pair[]);
int keycompare(void *a, void *b);
int reverse(void *a, void *b);
void freestuff(char ***kvms, int nlines);

int (*basecompare)(void *, void *);
int (*compare)(void *, void *);

int main(int argc, char *argv[])
{
	int i, nlines;
	int numeric = 0;
	int doreverse = 0;
	int dofold = 0;

	char *buff, **lines, ***kvps;

	for (i = 1; i < argc; i++) {
		if (strcmp(argv[i], "-n") == 0)
			numeric = 1;
		else if (strcmp(argv[i], "-r") == 0)
			doreverse = 1;
		else if (strcmp(argv[i], "-f") == 0)
			dofold = 1;
	}

	basecompare = numeric ? ((int (*)(void *, void *))numcmp) :
				((int (*)(void *, void *))strcmp);
	compare = doreverse ? reverse : basecompare;

	if ((nlines = readlines(&buff, &lines)) == LNS_ERROR) {
		printf("input too big to sort\n");
		return 0;
	}
	kvps = malloc(nlines * sizeof(char **));
	for (i = 0; i < nlines; i++) {
		char *key = makekey(lines[i], dofold);
		kvps[i] = keyvaluepair(key, lines[i]);
	}

	quicksort((void **)kvps, 0, nlines - 1, keycompare);

	for (i = 0; i < nlines; i++)
		lines[i] = value(kvps[i]);

	writelines(lines, nlines);

	freelines(buff, lines);
	freestuff(kvps, nlines);
	return 0;
}

char *fold(char *value)
{
	char *ptr = value;
	while ((*ptr = toupper(*ptr)))
		ptr++;
	return value;
}

char *makekey(char *value, int dofold)
{
	char *key = malloc((strlen(value) + 1) * sizeof(char));
	strcpy(key, value);
	if (dofold)
		fold(key);
	return key;
}

char **keyvaluepair(char key[], char value[])
{
	char **pair = malloc(2 * sizeof(char *));
	pair[0] = key;
	pair[1] = value;
	return pair;
}

char *key(char *pair[])
{
	return pair[0];
}

char *value(char *pair[])
{
	return pair[1];
}

int keycompare(void *a, void *b)
{
	char *v1 = key(a);
	char *v2 = key(b);
	return (*compare)(v1, v2);
}

int reverse(void *a, void *b)
{
	return (*basecompare)(b, a);
}

void freestuff(char ***kvps, int nlines)
{
	int i;
	for (i = 0; i < nlines; i++) {
		free(key(kvps[i]));
		free(kvps[i]);
	}
	free(kvps);
}

Full code on github

Solution by anonymous

This is an extension from my exercise 5.14 solution. I simply added strcmpi in the pointer functions part of the code and -f handling in the argument code.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

/*
    Exercise 5-15. Add the option -f to fold upper and lower case together, so that case distinctions are not made during sorting; for example, a and A compare equal.
*/

#define MAXLINES 5000 // max #lines to be sorted
#define MAXLEN 1000 // max length of any input line
#define ALLOCSIZE 10000 // size of available space

char *lineptr[MAXLINES]; // pointers to text lines
static char allocbuf[ALLOCSIZE]; // storage for alloc
static char *allocp = allocbuf; // next free position

int readlines(char *lineptr[], int nlines);
void writelines(char *lineptr[], int nlines);
void swap(void *v[], int, int);
int readlines(char *lineptr[], int maxlines);
void writelines(char *lineptr[], int nlines);
int getline(char *s, int lim);
char *alloc(int n);
void myqsort(void *lineptr[], int left, int right, int (*comp)(void *, void *)); // qsort had to be renamed to myqsort since stdlib.h is included.
int reverseCompare(void *, void *); // used to call baseCompare with the arguments in reverse order
int numcmp(const char *, const char *);

enum booelan { FALSE, TRUE }; // FALSE == 0, TRUE == 1, this creates booleans in a language that that doesn't natively support them unless you import stdbool.h in c99 or newer

int (*baseCompare)(void *, void *); // function pointer called baseCompare which points to either numcmp or cmpstr depending on the sorting type
int (*compare)(void *, void *); // function pointer called compare that will either point to the regular compare function baseCompare, or a new function called reverseCompare

// sort input lines
int main(int argc, char *argv[])
{   // nlines = number of input lines read, numeric, reverse, and caseInsensitive are flags for features turned on in the arguments
    int nlines, numeric = FALSE, reverse = FALSE, caseInsensitive = FALSE;

    while (--argc > 0)
        if (strcmp(*++argv, "-n") == 0)
            numeric = TRUE;
        else if (strcmp(*argv, "-r") == 0)
            reverse = TRUE;
        else if (strcmp(*argv, "-f") == 0)
            caseInsensitive = TRUE;
        else
        {
            printf("Usage: sort [-n] [-r] [-f]\n");
            return 1;
        }

    baseCompare = (int (*)(void *, void *))(numeric ? numcmp : caseInsensitive ? strcmpi : strcmp); // if numeric, use numcmp otherwise use strcmp/strcmpi
    compare = reverse ? reverseCompare : baseCompare; // if reverse, use reverseCompare, otherwise use baseCompare

    if ((nlines = readlines(lineptr, MAXLINES)) >= 0)
    {
        myqsort((void **) lineptr, 0, nlines - 1, compare);
        writelines(lineptr, nlines);
        return 0;
    }
    else
    {
        printf("input too big to sort\n");
        return 1;
    }
}

// sort v[left]...v[right] into increasing order
void myqsort(void *v[], int left, int right, int (*comp)(void *, void *))
{
    int i, last;
    if (left >= right) // do nothing if array contains fewer than two elements
        return;
    swap(v, left, (left + right) / 2);
    last = left;
    for (i = left + 1; i <= right; i++)
        if ((*comp)(v[i], v[left]) < 0)
            swap(v, ++last, i);
    swap(v, left, last);
    myqsort(v, left, last - 1, comp);
    myqsort(v, last + 1, right, comp);
}

// compare s1 and s2 numerically
int numcmp(const char *s1, const char *s2)
{
    double v1 = atof(s1), v2 = atof(s2);
    if (v1 < v2)
        return -1;
    else if (v1 > v2)
        return 1;
    return 0;
}

// swaps int i and j
void swap(void *v[], int i, int j)
{
    void *temp = v[i];
    v[i] = v[j];
    v[j] = temp;
}

// read input lines
int readlines(char *lineptr[], int maxlines)
{
    int len, nlines;
    char *p, line[MAXLEN];

    nlines = 0;
    while ((len = getline(line, MAXLEN)) > 0)
        if (nlines >= maxlines || (p = alloc(len)) == NULL)
            return -1;
        else
        {
            line[len - 1] = '\0'; // delete newline
            strcpy(p, line);
            lineptr[nlines++] = p;
        }
    return nlines;
}

// write output lines
void writelines(char *lineptr[], int nlines)
{
    while (nlines-- > 0)
        printf("%s\n", *lineptr++);
}

// get line into s, return length
int getline(char *s, int lim)
{
    int c;
    char *original = s;
    while (--lim > 0 && (c = getchar()) != EOF && c != '\n')
        *s++ = c;
    if (c == '\n')
        *s++ = c;
    *s = '\0';
    return s - original; // returns length
}

// return pointer to n characters
char *alloc(int n)
{
    if (allocbuf + ALLOCSIZE - allocp >= n) // it fits
    {
        allocp += n; // update marker to note where the next unallocated memory starts
        return allocp - n; // old pointer address
    }
    else
        return 0; // not enough room
}

int reverseCompare(void *left, void *right)
{
    return (*baseCompare)(right, left); // reverses the order that items are compared and which causes it to be sorted in reverse (descending) order
}
Personal tools