K&R C Programs Exercise 5-15

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.

Add a fold flag. When set, the comparison function converts both characters to lowercase before comparing. The key: don’t modify the strings themselves — do the case conversion only inside the comparison function, so the original strings are printed unchanged.

Key Addition to the Sort from Exercise 5-14

/* K&R Exercise 5-15 — sort with -f (fold case), builds on Ex 5-14
 * Compile: gcc -ansi -Wall ex5-15.c -o mysort
 * Usage:   ./mysort [-n] [-r] [-f] */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAXLINES  5000
#define MAXSTORE  100000

static char  store[MAXSTORE];
static char *lineptr[MAXLINES];
static int   numeric = 0, reverse = 0, fold = 0;

int my_getline(char *s, int lim)
{
    char *p = s; int c;
    while (--lim > 0 && (c = getchar()) != EOF && c != '\n') *p++ = c;
    if (c == '\n') *p++ = c;
    *p = '\0';
    return (int)(p - s);
}

int readlines(void)
{
    int len, n = 0;
    char *sp = store, line[1024];
    while ((len = my_getline(line, sizeof line)) > 0 && n < MAXLINES
           && sp + len < store + MAXSTORE) {
        line[len-1] = '\0';
        strcpy(sp, line);
        lineptr[n++] = sp;
        sp += len;
    }
    return n;
}

void writelines(char *lp[], int n) { while (n-- > 0) printf("%s\n", *lp++); }

int numcmp(const char *s1, const char *s2)
{
    double v1 = atof(s1), v2 = atof(s2);
    return (v1 < v2) ? -1 : (v1 > v2) ? 1 : 0;
}

/* Case-folding string comparison */
int foldcmp(const char *s1, const char *s2)
{
    for ( ; tolower((unsigned char)*s1) == tolower((unsigned char)*s2); s1++, s2++)
        if (*s1 == '\0')
            return 0;
    return tolower((unsigned char)*s1) - tolower((unsigned char)*s2);
}

int compare(const void *a, const void *b)
{
    const char *s1 = *(const char **)a, *s2 = *(const char **)b;
    int res;
    if (numeric)      res = numcmp(s1, s2);
    else if (fold)    res = foldcmp(s1, s2);
    else              res = strcmp(s1, s2);
    return reverse ? -res : res;
}

int main(int argc, char *argv[])
{
    int n, i;
    for (i = 1; i < argc; i++)
        if (argv[i][0] == '-') {
            char *p;
            for (p = argv[i]+1; *p; p++) {
                if (*p == 'n') numeric = 1;
                if (*p == 'r') reverse = 1;
                if (*p == 'f') fold    = 1;
            }
        }
    if ((n = readlines()) > 0) {
        qsort(lineptr, n, sizeof(char *), compare);
        writelines(lineptr, n);
    }
    return 0;
}

Test It

printf "Banana\napple\nCherry\n" | ./mysort       # B before a (ASCII order)
printf "Banana\napple\nCherry\n" | ./mysort -f    # apple Banana Cherry
printf "Banana\napple\nCherry\n" | ./mysort -f -r # Cherry Banana apple

What This Exercise Teaches

  • Non-destructive transformation in comparators — apply tolower only during comparison; the original strings are preserved for output
  • tolower with casttolower((unsigned char)c) avoids undefined behaviour when char is signed and c is negative (e.g., an accented character)
  • Flag combinations-f and -r compose correctly because the compare wrapper applies both transformations sequentially

Set Up Your C Environment

← Exercise 5-14  | 
Chapter 5 Solutions  | 
Exercise 5-16 →

Book: The C Programming Language, 2nd Ed — Kernighan & Ritchie

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>