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 —
-dand-finteract:dircmpchecksfoldinternally to apply case folding during directory comparison - Directory order in practice — this is how Unix
sortimplements-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