K&R C Programs Exercise 5-16

Exercise 5-16. Add the -d (directory order) option, which makes comparisons only on letters, numbers and blanks. Make sure it works in conjunction with -f.

Directory order ignores punctuation and control characters — only alphanumeric characters and spaces are considered. The comparison function skips non-directory characters in both strings simultaneously, then compares the next significant characters. Combine with -f (fold case) for case-insensitive directory order.

Key Addition to the Sort from Exercise 5-15

/* K&R Exercise 5-16 — sort with -d (directory order), builds on Ex 5-15
 * Compile: gcc -ansi -Wall ex5-16.c -o mysort
 * Usage:   ./mysort [-n] [-r] [-f] [-d] */
#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, dir = 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 *a, const char *b)
{
    double va = atof(a), vb = atof(b);
    return (va < vb) ? -1 : (va > vb) ? 1 : 0;
}

/* Skip non-directory characters (keep alnum + space) */
static int dircmp(const char *s1, const char *s2)
{
    int c1, c2;
    do {
        while (*s1 && !isalnum((unsigned char)*s1) && *s1 != ' ') s1++;
        while (*s2 && !isalnum((unsigned char)*s2) && *s2 != ' ') s2++;
        c1 = fold ? tolower((unsigned char)*s1) : (unsigned char)*s1;
        c2 = fold ? tolower((unsigned char)*s2) : (unsigned char)*s2;
        if (c1 == c2) { if (*s1) s1++, s2++; }
        else break;
    } while (*s1 && *s2);
    return c1 - c2;
}

static int foldcmp(const char *s1, const char *s2)
{
    for ( ; tolower((unsigned char)*s1) == tolower((unsigned char)*s2); s1++, s2++)
        if (!*s1) 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 (dir)  res = dircmp(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 (*p == 'd') dir     = 1;
            }
        }
    if ((n = readlines()) > 0) {
        qsort(lineptr, n, sizeof(char *), compare);
        writelines(lineptr, n);
    }
    return 0;
}

Test It

printf "the cat\nthe!cat\nThe cat\n" | ./mysort -d    # ignores !, case-sensitive
printf "the cat\nthe!cat\nThe cat\n" | ./mysort -d -f # ignores ! and case

What This Exercise Teaches

  • Character filtering in comparators — advance both pointers past irrelevant characters simultaneously; the next significant characters are then compared
  • Flag interaction-d and -f interact: dircmp checks fold internally to apply case folding during directory comparison
  • Directory order in practice — this is how Unix sort implements -d; useful for sorting index entries that may contain punctuation

Set Up Your C Environment

← Exercise 5-15  | 
Chapter 5 Solutions  | 
Exercise 5-17 →

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>