
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>

struct mystruct {
  char *label;
  unsigned a, b, c;
};


struct mystruct data[] = 
{ { "First ", 1, 2, 3},
  { "Second", 2, 3, 1},
  { "Third ", 3, 1, 2}
};

static ptrdiff_t comparison_offset = 0;
static int order = 1;           /* ascending */
int compare_structs(const void *, const void *);


int
main(int argc, char **argv) {
  unsigned field;
  char *field_addr;
  unsigned i;

  if (argc != 3) return 1;
  order = argv[1][0] == '-' ? -1 : 1;
  field = argv[2][0];           /* should be a, b, or c */
  

  switch (field) {
  case 'a':
    field_addr = (char *)& data[0].a;
    break;
  case 'b':
    field_addr = (char *)& data[0].b;
    break;
  case 'c':
    field_addr = (char *)& data[0].c;
    break;
  default:
    printf("Unknown field name %s.\n", argv[1]);
    return 1;
  }
  printf("I will sort %s by field '%c'.\n", 
         order >= 0 ? "ascending" : "descending",
         field);

  /* The field of interest is *this* many bytes from the beginning
   * of the structure.  Alternative technique: use 'offsetof() macro
   * if you have one. */
  comparison_offset = field_addr - (char *) &data[0];
  printf ("offset: %u\n", comparison_offset);

  qsort(data,                   /* base */
        sizeof(data)/sizeof(data[0]), /* nmemb */
        sizeof(data[0]),        /* size */
        compare_structs);

  for (i=0; i < sizeof(data)/sizeof(data[0]); i++) {
    struct mystruct d = data[i];
    printf("%u: { %s, %u, %u, %u }\n", i,
           d.label, d.a, d.b, d.c);
  }
  return 0;
}

int
compare_structs(const void *a, const void *b)
{
  /* a and b point to structs */
  int ai = *(unsigned *)(((char *)a)+comparison_offset);
  int bi = *(unsigned *)(((char *)b)+comparison_offset);
  return order * (ai - bi);
}

