#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <malloc.h>

typedef struct record_tag
{
  int uid;
  char uname[32];
  unsigned int seq;
} record;

int compare_record (const void * a, const void * b)
{
  const record *ra,*rb;
  int diff;
  ra = a;
  rb = b;
  
  diff = ra->uid - rb->uid;
  if (diff == 0) 
    diff = ra->seq - rb->seq;
  return diff;
}

record *parse(FILE* passwd, /*out*/ size_t *size)
{
  char* line = NULL;
  size_t linesize = 0;
  size_t mem_size = 256;
  size_t elements = 0;
  record* base = malloc( sizeof(record) * mem_size );
  record* r = base;
  unsigned int seq = 0;
  size_t i;
  
  while (getline(&line, &linesize, passwd) != -1) {
    char *lname, *p, *luids;
    size_t name_len;
    
    lname = line;
    p = strchr(line, ':');
    *p = '\0'; /* terminate user name*/
    
    name_len = p - lname;
    strncpy( r->uname, lname, name_len);
    r->uname[name_len] = '\0';
    
    p = strchr(p + 1, ':'); /* passwd field */
    
    luids = p + 1;
    p = strchr(p + 1, ':'); /* passwd field */
    *p = '\0'; /* terminate uid */
    
    
    r->uid = strtol(luids, NULL, 10);

    r->seq = seq++;

    ++ r; ++ elements;
    
    
    if (mem_size == elements) {
      mem_size *= 2;
      base = realloc(base, sizeof(record) * mem_size );
      r = &base[elements];
    }
  }

  *size = elements;
  
  qsort(base, elements, sizeof(record), compare_record);

  for (i = 1; i < elements; ++i) {
    if (base[i].uid == base[i-1].uid) {/* duplicate */
      base[i].seq = base[i-1].seq + 1;
      base[i].uname[0] = '/';
      /* fprintf(stderr, "Skipping %d\n", base[i].uid); */
    }
    else {
      base[i].seq = 0;
    }
  }

  return base;
}

char *uid2name(int uid, record* r, size_t r_size)
/* returns the account name corresponding to uid; the name is valid
   until the next call to uid2name */
{
  record *found, key;
  key.uname[0] = '\0';
  key.uid = uid;
  key.seq = 0;

  found = bsearch(&key, r, r_size, sizeof(record), compare_record);

  if (found)
    return found->uname;

  return "uid not found";
}

int main()
{
  char* line = NULL;
  size_t linesize = 0;
  struct mallinfo m;
  record* r;
  size_t r_size;
  
  
  FILE *passwd = fopen("./passwd","r");
  if (passwd == NULL) {
    fprintf(stderr, "could not open passwd\n");
    exit(1);
  }
  
  r = parse(passwd, &r_size);
  
  fclose(passwd);
  
  while (getline(&line, &linesize, stdin) != -1) {
    puts(uid2name(strtol(line, NULL, 10), r, r_size));
  }
  
  /* Folgendes gibt nur den von malloc() verbrauchten Speicher aus.
     Wenn Ihr Programm nennenswerte Mengen an statisch allokiertem
     Speicher verwendet oder z.B. mit mmap einen Speicherbereich
     allokiert, in den es hineinschreibt (z.B. bei Verwendung von
     strtok), zaehlen Sie die entsprechenden Groessen bitte dazu und
     geben Sie die Summe aus. */
  m = mallinfo();
  fprintf(stderr,"%d\n",m.uordblks + m.hblkhd);
  
  exit(0);
}
