/* Common routines for ip4 addresses. */

#include "ip4addr.h"

#define digit(c) ((c) >= '0' && (c) <= '9')
#define d2n(c) ((c) - '0')

static unsigned ip4mask(const char *s, ip4addr_t a) {
  /* helper routine for ip4cidr() */
  unsigned b = 0;
  if (!digit(*s)) return 0;
  do b = b * 10 + d2n(*s++);
  while(digit(*s));
  if (*s != '\0') return 0;
  if (!b || b > 32) return 0;
  if (a & ((((ip4addr_t)1) << (32 - b)) - 1)) return 0;
  return b;
}

/* parse ip4 CIDR range in `s', store base in *ap and
 * return number of bits */

unsigned ip4cidr(const char *s, ip4addr_t *ap) {
  ip4addr_t o;
  if (!digit(*s)) return 0;
  o = d2n(*s++);
  if (digit(*s)) {
    o = o * 10 + d2n(*s++);
    if (digit(*s) && (o = o * 10 + d2n(*s++)) > 255) return 0;
  }
  *ap = o << 24;
  if (!*s) return 8;
  if (*s == '/') return ip4mask(s+1, *ap);
  if (*s++ != '.' || !digit(*s)) return 0;
  o = d2n(*s++);
  if (digit(*s)) {
    o = o * 10 + d2n(*s++);
    if (digit(*s) && (o = o * 10 + d2n(*s++)) > 255) return 0;
  }
  *ap |= o << 16;
  if (!*s) return 16;
  if (*s == '/') return ip4mask(s+1, *ap);
  if (*s++ != '.' || !digit(*s)) return 0;
  o = d2n(*s++);
  if (digit(*s)) {
    o = o * 10 + d2n(*s++);
    if (digit(*s) && (o = o * 10 + d2n(*s++)) > 255) return 0;
  }
  *ap |= o << 8;
  if (!*s) return 24;
  if (*s == '/') return ip4mask(s+1, *ap);
  if (*s++ != '.' || !digit(*s)) return 0;
  o = d2n(*s++);
  if (digit(*s)) {
    o = o * 10 + d2n(*s++);
    if (digit(*s) && (o = o * 10 + d2n(*s++)) > 255) return 0;
  }
  *ap |= o;
  if (!*s) return 32;
  if (*s == '/') return ip4mask(s+1, *ap);
  return 0;
}
