/*      $NetBSD: irpmarshall.c,v 1.1.1.2 2012/09/09 16:07:57 christos Exp $     */

/*
* Copyright(c) 1989, 1993, 1995
*      The Regents of the University of California.  All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
*    must display the following acknowledgement:
*      This product includes software developed by the University of
*      California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
*    may be used to endorse or promote products derived from this software
*    without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/

/*
* Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
* Portions Copyright (c) 1996 by Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#if defined(LIBC_SCCS) && !defined(lint)
static const char rcsid[] = "Id: irpmarshall.c,v 1.7 2006/03/09 23:57:56 marka Exp ";
#endif /* LIBC_SCCS and not lint */

#if 0

Check values are in approrpriate endian order.

Double check memory allocations on unmarhsalling

#endif


/* Extern */

#include "port_before.h"

#include <sys/types.h>
#include <sys/socket.h>

#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>

#include <stdio.h>
#include <ctype.h>
#include <pwd.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <utmp.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>

#include <irs.h>
#include <isc/memcluster.h>
#include <isc/irpmarshall.h>

#include "port_after.h"


#ifndef HAVE_STRNDUP
static char    *strndup(const char *str, size_t len);
#endif

static char   **splitarray(const char *buffer, const char *buffend, char delim);
static int      joinarray(char * const * argv, char *buffer, char delim);
static char    *getfield(char **res, size_t reslen, char **buffer, char delim);
static size_t   joinlength(char * const *argv);
static void     free_array(char **argv, size_t entries);

#define ADDR_T_STR(x) (x == AF_INET ? "AF_INET" :\
                      (x == AF_INET6 ? "AF_INET6" : "UNKNOWN"))

#define MAXPADDRSIZE (sizeof "255.255.255.255" + 1)

static char COMMA = ',';

static const char *COMMASTR = ",";
static const char *COLONSTR = ":";



/* See big comment at bottom of irpmarshall.h for description. */


#ifdef WANT_IRS_PW
/* +++++++++++++++++++++++++ struct passwd +++++++++++++++++++++++++ */

/*%
* int irp_marshall_pw(const struct passwd *pw, char **buffer, size_t *len)
*
* notes: \li
*
*      See irpmarshall.h
*
* return: \li
*
*      0 on sucess, -1 on failure.
*
*/

int
irp_marshall_pw(const struct passwd *pw, char **buffer, size_t *len) {
       size_t need = 1 ;               /*%< for null byte */
       char pwUid[24];
       char pwGid[24];
       char pwChange[24];
       char pwExpire[24];
       const char *pwClass;
       const char *fieldsep = COLONSTR;

       if (pw == NULL || len == NULL) {
               errno = EINVAL;
               return (-1);
       }

       sprintf(pwUid, "%ld", (long)pw->pw_uid);
       sprintf(pwGid, "%ld", (long)pw->pw_gid);

#ifdef HAVE_PW_CHANGE
       sprintf(pwChange, "%ld", (long)pw->pw_change);
#else
       pwChange[0] = '0';
       pwChange[1] = '\0';
#endif

#ifdef HAVE_PW_EXPIRE
       sprintf(pwExpire, "%ld", (long)pw->pw_expire);
#else
       pwExpire[0] = '0';
       pwExpire[1] = '\0';
#endif

#ifdef HAVE_PW_CLASS
       pwClass = pw->pw_class;
#else
       pwClass = "";
#endif

       need += strlen(pw->pw_name)     + 1; /*%< one for fieldsep */
       need += strlen(pw->pw_passwd)   + 1;
       need += strlen(pwUid)           + 1;
       need += strlen(pwGid)           + 1;
       need += strlen(pwClass)         + 1;
       need += strlen(pwChange)        + 1;
       need += strlen(pwExpire)        + 1;
       need += strlen(pw->pw_gecos)    + 1;
       need += strlen(pw->pw_dir)      + 1;
       need += strlen(pw->pw_shell)    + 1;

       if (buffer == NULL) {
               *len = need;
               return (0);
       }

       if (*buffer != NULL && need > *len) {
               errno = EINVAL;
               return (-1);
       }

       if (*buffer == NULL) {
               need += 2;              /*%< for CRLF */
               *buffer = memget(need);
               if (*buffer == NULL) {
                       errno = ENOMEM;
                       return (-1);
               }

               *len = need;
       }

       strcpy(*buffer, pw->pw_name);           strcat(*buffer, fieldsep);
       strcat(*buffer, pw->pw_passwd);         strcat(*buffer, fieldsep);
       strcat(*buffer, pwUid);                 strcat(*buffer, fieldsep);
       strcat(*buffer, pwGid);                 strcat(*buffer, fieldsep);
       strcat(*buffer, pwClass);               strcat(*buffer, fieldsep);
       strcat(*buffer, pwChange);              strcat(*buffer, fieldsep);
       strcat(*buffer, pwExpire);              strcat(*buffer, fieldsep);
       strcat(*buffer, pw->pw_gecos);          strcat(*buffer, fieldsep);
       strcat(*buffer, pw->pw_dir);            strcat(*buffer, fieldsep);
       strcat(*buffer, pw->pw_shell);          strcat(*buffer, fieldsep);

       return (0);
}

/*%
* int irp_unmarshall_pw(struct passwd *pw, char *buffer)
*
* notes: \li
*
*      See irpmarshall.h
*
* return: \li
*
*      0 on success, -1 on failure
*
*/

int
irp_unmarshall_pw(struct passwd *pw, char *buffer) {
       char *name, *pass, *class, *gecos, *dir, *shell;
       uid_t pwuid;
       gid_t pwgid;
       time_t pwchange;
       time_t pwexpire;
       char *p;
       long t;
       char tmpbuf[24];
       char *tb = &tmpbuf[0];
       char fieldsep = ':';
       int myerrno = EINVAL;

       name = pass = class = gecos = dir = shell = NULL;
       p = buffer;

       /* pw_name field */
       name = NULL;
       if (getfield(&name, 0, &p, fieldsep) == NULL || strlen(name) == 0) {
               goto error;
       }

       /* pw_passwd field */
       pass = NULL;
       if (getfield(&pass, 0, &p, fieldsep) == NULL) { /*%< field can be empty */
               goto error;
       }


       /* pw_uid field */
       tb = tmpbuf;
       if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL ||
           strlen(tb) == 0) {
               goto error;
       }
       t = strtol(tmpbuf, &tb, 10);
       if (*tb) {
               goto error;     /*%< junk in value */
       }
       pwuid = (uid_t)t;
       if ((long) pwuid != t) {        /*%< value must have been too big. */
               goto error;
       }



       /* pw_gid field */
       tb = tmpbuf;
       if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL ||
           strlen(tb) == 0) {
               goto error;
       }
       t = strtol(tmpbuf, &tb, 10);
       if (*tb) {
               goto error;     /*%< junk in value */
       }
       pwgid = (gid_t)t;
       if ((long)pwgid != t) { /*%< value must have been too big. */
               goto error;
       }



       /* pw_class field */
       class = NULL;
       if (getfield(&class, 0, &p, fieldsep) == NULL) {
               goto error;
       }



       /* pw_change field */
       tb = tmpbuf;
       if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL ||
           strlen(tb) == 0) {
               goto error;
       }
       t = strtol(tmpbuf, &tb, 10);
       if (*tb) {
               goto error;     /*%< junk in value */
       }
       pwchange = (time_t)t;
       if ((long)pwchange != t) {      /*%< value must have been too big. */
               goto error;
       }



       /* pw_expire field */
       tb = tmpbuf;
       if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL ||
           strlen(tb) == 0) {
               goto error;
       }
       t = strtol(tmpbuf, &tb, 10);
       if (*tb) {
               goto error;     /*%< junk in value */
       }
       pwexpire = (time_t)t;
       if ((long) pwexpire != t) {     /*%< value must have been too big. */
               goto error;
       }



       /* pw_gecos field */
       gecos = NULL;
       if (getfield(&gecos, 0, &p, fieldsep) == NULL) {
               goto error;
       }



       /* pw_dir field */
       dir = NULL;
       if (getfield(&dir, 0, &p, fieldsep) == NULL) {
               goto error;
       }



       /* pw_shell field */
       shell = NULL;
       if (getfield(&shell, 0, &p, fieldsep) == NULL) {
               goto error;
       }



       pw->pw_name = name;
       pw->pw_passwd = pass;
       pw->pw_uid = pwuid;
       pw->pw_gid = pwgid;
       pw->pw_gecos = gecos;
       pw->pw_dir = dir;
       pw->pw_shell = shell;

#ifdef HAVE_PW_CHANGE
       pw->pw_change = pwchange;
#endif
#ifdef HAVE_PW_CLASS
       pw->pw_class = class;
#endif
#ifdef HAVE_PW_EXPIRE
       pw->pw_expire = pwexpire;
#endif

       return (0);

error:
       errno = myerrno;

       if (name != NULL) free(name);
       if (pass != NULL) free(pass);
       if (gecos != NULL) free(gecos);
       if (dir != NULL) free(dir);
       if (shell != NULL) free(shell);

       return (-1);
}

/* ------------------------- struct passwd ------------------------- */
#endif /* WANT_IRS_PW */
/* +++++++++++++++++++++++++ struct group +++++++++++++++++++++++++ */

/*%
* int irp_marshall_gr(const struct group *gr, char **buffer, size_t *len)
*
* notes: \li
*
*      See irpmarshall.h.
*
* return: \li
*
*      0 on success, -1 on failure
*/

int
irp_marshall_gr(const struct group *gr, char **buffer, size_t *len) {
       size_t need = 1;        /*%< for null byte */
       char grGid[24];
       const char *fieldsep = COLONSTR;

       if (gr == NULL || len == NULL) {
               errno = EINVAL;
               return (-1);
       }

       sprintf(grGid, "%ld", (long)gr->gr_gid);

       need += strlen(gr->gr_name) + 1;
#ifndef MISSING_GR_PASSWD
       need += strlen(gr->gr_passwd) + 1;
#else
       need++;
#endif
       need += strlen(grGid) + 1;
       need += joinlength(gr->gr_mem) + 1;

       if (buffer == NULL) {
               *len = need;
               return (0);
       }

       if (*buffer != NULL && need > *len) {
               errno = EINVAL;
               return (-1);
       }

       if (*buffer == NULL) {
               need += 2;              /*%< for CRLF */
               *buffer = memget(need);
               if (*buffer == NULL) {
                       errno = ENOMEM;
                       return (-1);
               }

               *len = need;
       }

       strcpy(*buffer, gr->gr_name);           strcat(*buffer, fieldsep);
#ifndef MISSING_GR_PASSWD
       strcat(*buffer, gr->gr_passwd);
#endif
       strcat(*buffer, fieldsep);
       strcat(*buffer, grGid);                 strcat(*buffer, fieldsep);
       joinarray(gr->gr_mem, *buffer, COMMA) ; strcat(*buffer, fieldsep);

       return (0);
}

/*%
* int irp_unmarshall_gr(struct group *gr, char *buffer)
*
* notes: \li
*
*      See irpmarshall.h
*
* return: \li
*
*      0 on success and -1 on failure.
*
*/

int
irp_unmarshall_gr(struct group *gr, char *buffer) {
       char *p, *q;
       gid_t grgid;
       long t;
       char *name = NULL;
       char *pass = NULL;
       char **members = NULL;
       char tmpbuf[24];
       char *tb;
       char fieldsep = ':';
       int myerrno = EINVAL;

       if (gr == NULL || buffer == NULL) {
               errno = EINVAL;
               return (-1);
       }

       p = buffer;

       /* gr_name field */
       name = NULL;
       if (getfield(&name, 0, &p, fieldsep) == NULL || strlen(name) == 0U) {
               goto error;
       }


       /* gr_passwd field */
       pass = NULL;
       if (getfield(&pass, 0, &p, fieldsep) == NULL) {
               goto error;
       }


       /* gr_gid field */
       tb = tmpbuf;
       if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL ||
           strlen(tb) == 0U) {
               goto error;
       }
       t = strtol(tmpbuf, &tb, 10);
       if (*tb) {
               goto error;     /*%< junk in value */
       }
       grgid = (gid_t)t;
       if ((long) grgid != t) {        /*%< value must have been too big. */
               goto error;
       }


       /* gr_mem field. Member names are separated by commas */
       q = strchr(p, fieldsep);
       if (q == NULL) {
               goto error;
       }
       members = splitarray(p, q, COMMA);
       if (members == NULL) {
               myerrno = errno;
               goto error;
       }
       p = q + 1;


       gr->gr_name = name;
#ifndef MISSING_GR_PASSWD
       gr->gr_passwd = pass;
#endif
       gr->gr_gid = grgid;
       gr->gr_mem = members;

       return (0);

error:
       errno = myerrno;

       if (name != NULL) free(name);
       if (pass != NULL) free(pass);

       return (-1);
}


/* ------------------------- struct group ------------------------- */




/* +++++++++++++++++++++++++ struct servent +++++++++++++++++++++++++ */

/*%
* int irp_marshall_sv(const struct servent *sv, char **buffer, size_t *len)
*
* notes: \li
*
*      See irpmarshall.h
*
* return: \li
*
*      0 on success, -1 on failure.
*
*/

int
irp_marshall_sv(const struct servent *sv, char **buffer, size_t *len) {
       size_t need = 1;        /*%< for null byte */
       char svPort[24];
       const char *fieldsep = COLONSTR;
       short realport;

       if (sv == NULL || len == NULL) {
               errno = EINVAL;
               return (-1);
       }

       /* the int s_port field is actually a short in network order. We
          want host order to make the marshalled data look correct */
       realport = ntohs((short)sv->s_port);
       sprintf(svPort, "%d", realport);

       need += strlen(sv->s_name) + 1;
       need += joinlength(sv->s_aliases) + 1;
       need += strlen(svPort) + 1;
       need += strlen(sv->s_proto) + 1;

       if (buffer == NULL) {
               *len = need;
               return (0);
       }

       if (*buffer != NULL && need > *len) {
               errno = EINVAL;
               return (-1);
       }

       if (*buffer == NULL) {
               need += 2;              /*%< for CRLF */
               *buffer = memget(need);
               if (*buffer == NULL) {
                       errno = ENOMEM;
                       return (-1);
               }

               *len = need;
       }

       strcpy(*buffer, sv->s_name);            strcat(*buffer, fieldsep);
       joinarray(sv->s_aliases, *buffer, COMMA); strcat(*buffer, fieldsep);
       strcat(*buffer, svPort);                strcat(*buffer, fieldsep);
       strcat(*buffer, sv->s_proto);           strcat(*buffer, fieldsep);

       return (0);
}

/*%
* int irp_unmarshall_sv(struct servent *sv, char *buffer)
*
* notes: \li
*
*      See irpmarshall.h
*
* return: \li
*
*      0 on success, -1 on failure.
*
*/

int
irp_unmarshall_sv(struct servent *sv, char *buffer) {
       char *p, *q;
       short svport;
       long t;
       char *name = NULL;
       char *proto = NULL;
       char **aliases = NULL;
       char tmpbuf[24];
       char *tb;
       char fieldsep = ':';
       int myerrno = EINVAL;

       if (sv == NULL || buffer == NULL)
               return (-1);

       p = buffer;


       /* s_name field */
       name = NULL;
       if (getfield(&name, 0, &p, fieldsep) == NULL || strlen(name) == 0U) {
               goto error;
       }


       /* s_aliases field */
       q = strchr(p, fieldsep);
       if (q == NULL) {
               goto error;
       }
       aliases = splitarray(p, q, COMMA);
       if (aliases == NULL) {
               myerrno = errno;
               goto error;
       }
       p = q + 1;


       /* s_port field */
       tb = tmpbuf;
       if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL ||
           strlen(tb) == 0U) {
               goto error;
       }
       t = strtol(tmpbuf, &tb, 10);
       if (*tb) {
               goto error;     /*%< junk in value */
       }
       svport = (short)t;
       if ((long) svport != t) {       /*%< value must have been too big. */
               goto error;
       }
       svport = htons(svport);

       /* s_proto field */
       proto = NULL;
       if (getfield(&proto, 0, &p, fieldsep) == NULL) {
               goto error;
       }

       sv->s_name = name;
       sv->s_aliases = aliases;
       sv->s_port = svport;
       sv->s_proto = proto;

       return (0);

error:
       errno = myerrno;

       if (name != NULL) free(name);
       if (proto != NULL) free(proto);
       free_array(aliases, 0);

       return (-1);
}


/* ------------------------- struct servent ------------------------- */

/* +++++++++++++++++++++++++ struct protoent +++++++++++++++++++++++++ */

/*%
* int irp_marshall_pr(struct protoent *pr, char **buffer, size_t *len)
*
* notes: \li
*
*      See irpmarshall.h
*
* return: \li
*
*      0 on success and -1 on failure.
*
*/

int
irp_marshall_pr(struct protoent *pr, char **buffer, size_t *len) {
       size_t need = 1;        /*%< for null byte */
       char prProto[24];
       const char *fieldsep = COLONSTR;

       if (pr == NULL || len == NULL) {
               errno = EINVAL;
               return (-1);
       }

       sprintf(prProto, "%d", (int)pr->p_proto);

       need += strlen(pr->p_name) + 1;
       need += joinlength(pr->p_aliases) + 1;
       need += strlen(prProto) + 1;

       if (buffer == NULL) {
               *len = need;
               return (0);
       }

       if (*buffer != NULL && need > *len) {
               errno = EINVAL;
               return (-1);
       }

       if (*buffer == NULL) {
               need += 2;              /*%< for CRLF */
               *buffer = memget(need);
               if (*buffer == NULL) {
                       errno = ENOMEM;
                       return (-1);
               }

               *len = need;
       }

       strcpy(*buffer, pr->p_name);            strcat(*buffer, fieldsep);
       joinarray(pr->p_aliases, *buffer, COMMA); strcat(*buffer, fieldsep);
       strcat(*buffer, prProto);               strcat(*buffer, fieldsep);

       return (0);

}

/*%
* int irp_unmarshall_pr(struct protoent *pr, char *buffer)
*
* notes: \li
*
*      See irpmarshall.h
*
* return: \li
*
*      0 on success, -1 on failure
*
*/

int irp_unmarshall_pr(struct protoent *pr, char *buffer) {
       char *p, *q;
       int prproto;
       long t;
       char *name = NULL;
       char **aliases = NULL;
       char tmpbuf[24];
       char *tb;
       char fieldsep = ':';
       int myerrno = EINVAL;

       if (pr == NULL || buffer == NULL) {
               errno = EINVAL;
               return (-1);
       }

       p = buffer;

       /* p_name field */
       name = NULL;
       if (getfield(&name, 0, &p, fieldsep) == NULL || strlen(name) == 0U) {
               goto error;
       }


       /* p_aliases field */
       q = strchr(p, fieldsep);
       if (q == NULL) {
               goto error;
       }
       aliases = splitarray(p, q, COMMA);
       if (aliases == NULL) {
               myerrno = errno;
               goto error;
       }
       p = q + 1;


       /* p_proto field */
       tb = tmpbuf;
       if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL ||
           strlen(tb) == 0U) {
               goto error;
       }
       t = strtol(tmpbuf, &tb, 10);
       if (*tb) {
               goto error;     /*%< junk in value */
       }
       prproto = (int)t;
       if ((long) prproto != t) {      /*%< value must have been too big. */
               goto error;
       }

       pr->p_name = name;
       pr->p_aliases = aliases;
       pr->p_proto = prproto;

       return (0);

error:
       errno = myerrno;

       if (name != NULL) free(name);
       free_array(aliases, 0);

       return (-1);
}

/* ------------------------- struct protoent ------------------------- */



/* +++++++++++++++++++++++++ struct hostent +++++++++++++++++++++++++ */

/*%
* int irp_marshall_ho(struct hostent *ho, char **buffer, size_t *len)
*
* notes: \li
*
*      See irpmarshall.h.
*
* return: \li
*
*      0 on success, -1 on failure.
*
*/

int
irp_marshall_ho(struct hostent *ho, char **buffer, size_t *len) {
       size_t need = 1;        /*%< for null byte */
       char hoaddrtype[24];
       char holength[24];
       char **av;
       char *p;
       int addrlen;
       int malloced = 0;
       size_t remlen;
       const char *fieldsep = "@";

       if (ho == NULL || len == NULL) {
               errno = EINVAL;
               return (-1);
       }

       switch(ho->h_addrtype) {
       case AF_INET:
               strcpy(hoaddrtype, "AF_INET");
               break;

       case AF_INET6:
               strcpy(hoaddrtype, "AF_INET6");
               break;

       default:
               errno = EINVAL;
               return (-1);
       }

       sprintf(holength, "%d", ho->h_length);

       need += strlen(ho->h_name) + 1;
       need += joinlength(ho->h_aliases) + 1;
       need += strlen(hoaddrtype) + 1;
       need += strlen(holength) + 1;

       /* we determine an upper bound on the string length needed, not an
          exact length. */
       addrlen = (ho->h_addrtype == AF_INET ? 16 : 46) ; /*%< XX other AF's?? */
       for (av = ho->h_addr_list; av != NULL && *av != NULL ; av++)
               need += addrlen;

       if (buffer == NULL) {
               *len = need;
               return (0);
       }

       if (*buffer != NULL && need > *len) {
               errno = EINVAL;
               return (-1);
       }

       if (*buffer == NULL) {
               need += 2;              /*%< for CRLF */
               *buffer = memget(need);
               if (*buffer == NULL) {
                       errno = ENOMEM;
                       return (-1);
               }

               *len = need;
               malloced = 1;
       }

       strcpy(*buffer, ho->h_name);            strcat(*buffer, fieldsep);
       joinarray(ho->h_aliases, *buffer, COMMA); strcat(*buffer, fieldsep);
       strcat(*buffer, hoaddrtype);            strcat(*buffer, fieldsep);
       strcat(*buffer, holength);              strcat(*buffer, fieldsep);

       p = *buffer + strlen(*buffer);
       remlen = need - strlen(*buffer);
       for (av = ho->h_addr_list ; av != NULL && *av != NULL ; av++) {
               if (inet_ntop(ho->h_addrtype, *av, p, remlen) == NULL) {
                       goto error;
               }
               if (*(av + 1) != NULL)
                       strcat(p, COMMASTR);
               remlen -= strlen(p);
               p += strlen(p);
       }
       strcat(*buffer, fieldsep);

       return (0);

error:
       if (malloced) {
               memput(*buffer, need);
       }

       return (-1);
}

/*%
* int irp_unmarshall_ho(struct hostent *ho, char *buffer)
*
* notes: \li
*
*      See irpmarshall.h.
*
* return: \li
*
*      0 on success, -1 on failure.
*
*/

int
irp_unmarshall_ho(struct hostent *ho, char *buffer) {
       char *p, *q, *r;
       int hoaddrtype;
       int holength;
       long t;
       char *name;
       char **aliases = NULL;
       char **hohaddrlist = NULL;
       size_t hoaddrsize;
       char tmpbuf[24];
       char *tb;
       char **alist;
       int addrcount;
       char fieldsep = '@';
       int myerrno = EINVAL;

       if (ho == NULL || buffer == NULL) {
               errno = EINVAL;
               return (-1);
       }

       p = buffer;

       /* h_name field */
       name = NULL;
       if (getfield(&name, 0, &p, fieldsep) == NULL || strlen(name) == 0U) {
               goto error;
       }


       /* h_aliases field */
       q = strchr(p, fieldsep);
       if (q == NULL) {
               goto error;
       }
       aliases = splitarray(p, q, COMMA);
       if (aliases == NULL) {
               myerrno = errno;
               goto error;
       }
       p = q + 1;


       /* h_addrtype field */
       tb = tmpbuf;
       if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL ||
           strlen(tb) == 0U) {
               goto error;
       }
       if (strcmp(tmpbuf, "AF_INET") == 0)
               hoaddrtype = AF_INET;
       else if (strcmp(tmpbuf, "AF_INET6") == 0)
               hoaddrtype = AF_INET6;
       else
               goto error;


       /* h_length field */
       tb = tmpbuf;
       if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL ||
           strlen(tb) == 0U) {
               goto error;
       }
       t = strtol(tmpbuf, &tb, 10);
       if (*tb) {
               goto error;     /*%< junk in value */
       }
       holength = (int)t;
       if ((long) holength != t) {     /*%< value must have been too big. */
               goto error;
       }


       /* h_addr_list field */
       q = strchr(p, fieldsep);
       if (q == NULL)
               goto error;

       /* count how many addresss are in there */
       if (q > p + 1) {
               for (addrcount = 1, r = p ; r != q ; r++) {
                       if (*r == COMMA)
                               addrcount++;
               }
       } else {
               addrcount = 0;
       }

       hoaddrsize = (addrcount + 1) * sizeof (char *);
       hohaddrlist = malloc(hoaddrsize);
       if (hohaddrlist == NULL) {
               myerrno = ENOMEM;
               goto error;
       }

       memset(hohaddrlist, 0x0, hoaddrsize);

       alist = hohaddrlist;
       for (t = 0, r = p ; r != q ; p = r + 1, t++) {
               char saved;
               while (r != q && *r != COMMA) r++;
               saved = *r;
               *r = 0x0;

               alist[t] = malloc(hoaddrtype == AF_INET ? 4 : 16);
               if (alist[t] == NULL) {
                       myerrno = ENOMEM;
                       goto error;
               }

               if (inet_pton(hoaddrtype, p, alist[t]) == -1)
                       goto error;
               *r = saved;
       }
       alist[t] = NULL;

       ho->h_name = name;
       ho->h_aliases = aliases;
       ho->h_addrtype = hoaddrtype;
       ho->h_length = holength;
       ho->h_addr_list = hohaddrlist;

       return (0);

error:
       errno = myerrno;

       if (name != NULL) free(name);
       free_array(hohaddrlist, 0);
       free_array(aliases, 0);

       return (-1);
}

/* ------------------------- struct hostent------------------------- */



/* +++++++++++++++++++++++++ struct netgrp +++++++++++++++++++++++++ */

/*%
* int irp_marshall_ng(const char *host, const char *user,
*                     const char *domain, char *buffer, size_t *len)
*
* notes: \li
*
*      See note for irp_marshall_ng_start
*
* return: \li
*
*      0 on success, 0 on failure.
*
*/

int
irp_marshall_ng(const char *host, const char *user, const char *domain,
               char **buffer, size_t *len) {
       size_t need = 1; /*%< for nul byte */
       const char *fieldsep = ",";

       if (len == NULL) {
               errno = EINVAL;
               return (-1);
       }

       need += 4;                     /*%< two parens and two commas */
       need += (host == NULL ? 0 : strlen(host));
       need += (user == NULL ? 0 : strlen(user));
       need += (domain == NULL ? 0 : strlen(domain));

       if (buffer == NULL) {
               *len = need;
               return (0);
       } else if (*buffer != NULL && need > *len) {
               errno = EINVAL;
               return (-1);
       }

       if (*buffer == NULL) {
               need += 2;              /*%< for CRLF */
               *buffer = memget(need);
               if (*buffer == NULL) {
                       errno = ENOMEM;
                       return (-1);
               }

               *len = need;
       }

       (*buffer)[0] = '(';
       (*buffer)[1] = '\0';

       if (host != NULL)
               strcat(*buffer, host);
       strcat(*buffer, fieldsep);

       if (user != NULL)
               strcat(*buffer, user);
       strcat(*buffer, fieldsep);

       if (domain != NULL)
               strcat(*buffer, domain);
       strcat(*buffer, ")");

       return (0);
}



/* ---------- */

/*%
* int irp_unmarshall_ng(const char **host, const char **user,
*                       const char **domain, char *buffer)
*
* notes: \li
*
*      Unpacks the BUFFER into 3 character arrays it allocates and assigns
*      to *HOST, *USER and *DOMAIN. If any field of the value is empty,
*      then the corresponding paramater value will be set to NULL.
*
* return: \li
*
*      0 on success and -1 on failure.
*/

int
irp_unmarshall_ng(const char **hostp, const char **userp, const char **domainp,
                 char *buffer)
{
       char *p, *q;
       char fieldsep = ',';
       int myerrno = EINVAL;
       char *host, *user, *domain;

       if (userp == NULL || hostp == NULL ||
           domainp == NULL || buffer == NULL) {
               errno = EINVAL;
               return (-1);
       }

       host = user = domain = NULL;

       p = buffer;
       while (isspace((unsigned char)*p)) {
               p++;
       }
       if (*p != '(') {
               goto error;
       }

       q = p + 1;
       while (*q && *q != fieldsep)
               q++;
       if (!*q) {
               goto error;
       } else if (q > p + 1) {
               host = strndup(p, q - p);
       }

       p = q + 1;
       if (!*p) {
               goto error;
       } else if (*p != fieldsep) {
               q = p + 1;
               while (*q && *q != fieldsep)
                       q++;
               if (!*q) {
                       goto error;
               }
               user = strndup(p, q - p);
       } else {
               p++;
       }

       if (!*p) {
               goto error;
       } else if (*p != ')') {
               q = p + 1;
               while (*q && *q != ')')
                       q++;
               if (!*q) {
                       goto error;
               }
               domain = strndup(p, q - p);
       }
       *hostp = host;
       *userp = user;
       *domainp = domain;

       return (0);

error:
       errno = myerrno;

       if (host != NULL) free(host);
       if (user != NULL) free(user);

       return (-1);
}

/* ------------------------- struct netgrp ------------------------- */




/* +++++++++++++++++++++++++ struct nwent +++++++++++++++++++++++++ */

/*%
* int irp_marshall_nw(struct nwent *ne, char **buffer, size_t *len)
*
* notes: \li
*
*      See at top.
*
* return: \li
*
*      0 on success and -1 on failure.
*
*/

int
irp_marshall_nw(struct nwent *ne, char **buffer, size_t *len) {
       size_t need = 1;        /*%< for null byte */
       char nAddrType[24];
       char nNet[MAXPADDRSIZE];
       const char *fieldsep = COLONSTR;

       if (ne == NULL || len == NULL) {
               return (-1);
       }

       strcpy(nAddrType, ADDR_T_STR(ne->n_addrtype));

       if (inet_net_ntop(ne->n_addrtype, ne->n_addr, ne->n_length,
                         nNet, sizeof nNet) == NULL) {
               return (-1);
       }


       need += strlen(ne->n_name) + 1;
       need += joinlength(ne->n_aliases) + 1;
       need += strlen(nAddrType) + 1;
       need += strlen(nNet) + 1;

       if (buffer == NULL) {
               *len = need;
               return (0);
       }

       if (*buffer != NULL && need > *len) {
               errno = EINVAL;
               return (-1);
       }

       if (*buffer == NULL) {
               need += 2;              /*%< for CRLF */
               *buffer = memget(need);
               if (*buffer == NULL) {
                       errno = ENOMEM;
                       return (-1);
               }

               *len = need;
       }

       strcpy(*buffer, ne->n_name);            strcat(*buffer, fieldsep);
       joinarray(ne->n_aliases, *buffer, COMMA) ; strcat(*buffer, fieldsep);
       strcat(*buffer, nAddrType);             strcat(*buffer, fieldsep);
       strcat(*buffer, nNet);                  strcat(*buffer, fieldsep);

       return (0);
}

/*%
* int irp_unmarshall_nw(struct nwent *ne, char *buffer)
*
* notes: \li
*
*      See note up top.
*
* return: \li
*
*      0 on success and -1 on failure.
*
*/

int
irp_unmarshall_nw(struct nwent *ne, char *buffer) {
       char *p, *q;
       int naddrtype;
       long nnet;
       int bits;
       char *name = NULL;
       char **aliases = NULL;
       char tmpbuf[24];
       char *tb;
       char fieldsep = ':';
       int myerrno = EINVAL;

       if (ne == NULL || buffer == NULL) {
               goto error;
       }

       p = buffer;

       /* n_name field */
       name = NULL;
       if (getfield(&name, 0, &p, fieldsep) == NULL || strlen(name) == 0U) {
               goto error;
       }


       /* n_aliases field. Aliases are separated by commas */
       q = strchr(p, fieldsep);
       if (q == NULL) {
               goto error;
       }
       aliases = splitarray(p, q, COMMA);
       if (aliases == NULL) {
               myerrno = errno;
               goto error;
       }
       p = q + 1;


       /* h_addrtype field */
       tb = tmpbuf;
       if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL ||
           strlen(tb) == 0U) {
               goto error;
       }
       if (strcmp(tmpbuf, "AF_INET") == 0)
               naddrtype = AF_INET;
       else if (strcmp(tmpbuf, "AF_INET6") == 0)
               naddrtype = AF_INET6;
       else
               goto error;


       /* n_net field */
       tb = tmpbuf;
       if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL ||
           strlen(tb) == 0U) {
               goto error;
       }
       nnet = 0;
       bits = inet_net_pton(naddrtype, tmpbuf, &nnet, sizeof nnet);
       if (bits < 0) {
               goto error;
       }

       /* nnet = ntohl(nnet); */ /* keep in network order for nwent */

       ne->n_name = name;
       ne->n_aliases = aliases;
       ne->n_addrtype = naddrtype;
       ne->n_length = bits;
       ne->n_addr = malloc(sizeof nnet);
       if (ne->n_addr == NULL) {
               goto error;
       }

       memcpy(ne->n_addr, &nnet, sizeof nnet);

       return (0);

error:
       errno = myerrno;

       if (name != NULL) free(name);
       free_array(aliases, 0);

       return (-1);
}


/* ------------------------- struct nwent ------------------------- */


/* +++++++++++++++++++++++++ struct netent +++++++++++++++++++++++++ */

/*%
* int irp_marshall_ne(struct netent *ne, char **buffer, size_t *len)
*
* notes: \li
*
*      See at top.
*
* return: \li
*
*      0 on success and -1 on failure.
*
*/

int
irp_marshall_ne(struct netent *ne, char **buffer, size_t *len) {
       size_t need = 1;        /*%< for null byte */
       char nAddrType[24];
       char nNet[MAXPADDRSIZE];
       const char *fieldsep = COLONSTR;
       long nval;

       if (ne == NULL || len == NULL) {
               return (-1);
       }

       strcpy(nAddrType, ADDR_T_STR(ne->n_addrtype));

       nval = htonl(ne->n_net);
       if (inet_ntop(ne->n_addrtype, &nval, nNet, sizeof nNet) == NULL) {
               return (-1);
       }

       need += strlen(ne->n_name) + 1;
       need += joinlength(ne->n_aliases) + 1;
       need += strlen(nAddrType) + 1;
       need += strlen(nNet) + 1;

       if (buffer == NULL) {
               *len = need;
               return (0);
       }

       if (*buffer != NULL && need > *len) {
               errno = EINVAL;
               return (-1);
       }

       if (*buffer == NULL) {
               need += 2;              /*%< for CRLF */
               *buffer = memget(need);
               if (*buffer == NULL) {
                       errno = ENOMEM;
                       return (-1);
               }

               *len = need;
       }

       strcpy(*buffer, ne->n_name);            strcat(*buffer, fieldsep);
       joinarray(ne->n_aliases, *buffer, COMMA) ; strcat(*buffer, fieldsep);
       strcat(*buffer, nAddrType);             strcat(*buffer, fieldsep);
       strcat(*buffer, nNet);                  strcat(*buffer, fieldsep);

       return (0);
}

/*%
* int irp_unmarshall_ne(struct netent *ne, char *buffer)
*
* notes: \li
*
*      See note up top.
*
* return: \li
*
*      0 on success and -1 on failure.
*
*/

int
irp_unmarshall_ne(struct netent *ne, char *buffer) {
       char *p, *q;
       int naddrtype;
       long nnet;
       int bits;
       char *name = NULL;
       char **aliases = NULL;
       char tmpbuf[24];
       char *tb;
       char fieldsep = ':';
       int myerrno = EINVAL;

       if (ne == NULL || buffer == NULL) {
               goto error;
       }

       p = buffer;

       /* n_name field */
       name = NULL;
       if (getfield(&name, 0, &p, fieldsep) == NULL || strlen(name) == 0U) {
               goto error;
       }


       /* n_aliases field. Aliases are separated by commas */
       q = strchr(p, fieldsep);
       if (q == NULL) {
               goto error;
       }
       aliases = splitarray(p, q, COMMA);
       if (aliases == NULL) {
               myerrno = errno;
               goto error;
       }
       p = q + 1;


       /* h_addrtype field */
       tb = tmpbuf;
       if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL ||
           strlen(tb) == 0U) {
               goto error;
       }
       if (strcmp(tmpbuf, "AF_INET") == 0)
               naddrtype = AF_INET;
       else if (strcmp(tmpbuf, "AF_INET6") == 0)
               naddrtype = AF_INET6;
       else
               goto error;


       /* n_net field */
       tb = tmpbuf;
       if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL ||
           strlen(tb) == 0U) {
               goto error;
       }
       bits = inet_net_pton(naddrtype, tmpbuf, &nnet, sizeof nnet);
       if (bits < 0) {
               goto error;
       }
       nnet = ntohl(nnet);

       ne->n_name = name;
       ne->n_aliases = aliases;
       ne->n_addrtype = naddrtype;
       ne->n_net = nnet;

       return (0);

error:
       errno = myerrno;

       if (name != NULL) free(name);
       free_array(aliases, 0);

       return (-1);
}


/* ------------------------- struct netent ------------------------- */


/* =========================================================================== */

/*%
* static char ** splitarray(const char *buffer, const char *buffend, char delim)
*
* notes: \li
*
*      Split a delim separated astring. Not allowed
*      to have two delims next to each other. BUFFER points to begining of
*      string, BUFFEND points to one past the end of the string
*      (i.e. points at where the null byte would be if null
*      terminated).
*
* return: \li
*
*      Returns a malloced array of pointers, each pointer pointing to a
*      malloced string. If BUFEER is an empty string, then return values is
*      array of 1 pointer that is NULL. Returns NULL on failure.
*
*/

static char **
splitarray(const char *buffer, const char *buffend, char delim) {
       const char *p, *q;
       int count = 0;
       char **arr = NULL;
       char **aptr;

       if (buffend < buffer)
               return (NULL);
       else if (buffend > buffer && *buffer == delim)
               return (NULL);
       else if (buffend > buffer && *(buffend - 1) == delim)
               return (NULL);

       /* count the number of field and make sure none are empty */
       if (buffend > buffer + 1) {
               for (count = 1, q = buffer ; q != buffend ; q++) {
                       if (*q == delim) {
                               if (q > buffer && (*(q - 1) == delim)) {
                                       errno = EINVAL;
                                       return (NULL);
                               }
                               count++;
                       }
               }
       }

       if (count > 0) {
               count++ ;               /*%< for NULL at end */
               aptr = arr = malloc(count * sizeof (char *));
               if (aptr == NULL) {
                        errno = ENOMEM;
                        return (NULL);
                }

               memset(arr, 0x0, count * sizeof (char *));
               for (p = buffer ; p < buffend ; p++) {
                       for (q = p ; *q != delim && q != buffend ; q++)
                               /* nothing */;
                       *aptr = strndup(p, q - p);

                       p = q;
                       aptr++;
               }
               *aptr = NULL;
       } else {
               arr = malloc(sizeof (char *));
               if (arr == NULL) {
                       errno = ENOMEM;
                       return (NULL);
               }

               *arr = NULL;
       }

       return (arr);
}

/*%
* static size_t joinlength(char * const *argv)
*
* return: \li
*
*      the number of bytes in all the arrays pointed at
*      by argv, including their null bytes(which will usually be turned
*      into commas).
*
*
*/

static size_t
joinlength(char * const *argv) {
       int len = 0;

       while (argv && *argv) {
               len += (strlen(*argv) + 1);
               argv++;
       }

       return (len);
}

/*%
* int joinarray(char * const *argv, char *buffer, char delim)
*
* notes: \li
*
*      Copy all the ARGV strings into the end of BUFFER
*      separating them with DELIM.  BUFFER is assumed to have
*      enough space to hold everything and to be already null-terminated.
*
* return: \li
*
*      0 unless argv or buffer is NULL.
*
*
*/

static int
joinarray(char * const *argv, char *buffer, char delim) {
       char * const *p;
       char sep[2];

       if (argv == NULL || buffer == NULL) {
               errno = EINVAL;
               return (-1);
       }

       sep[0] = delim;
       sep[1] = 0x0;

       for (p = argv ; *p != NULL ; p++) {
               strcat(buffer, *p);
               if (*(p + 1) != NULL) {
                       strcat(buffer, sep);
               }
       }

       return (0);
}

/*%
* static char * getfield(char **res, size_t reslen, char **ptr, char delim)
*
* notes: \li
*
*      Stores in *RES, which is a buffer of length RESLEN, a
*      copy of the bytes from *PTR up to and including the first
*      instance of DELIM. If *RES is NULL, then it will be
*      assigned a malloced buffer to hold the copy. *PTR is
*      modified to point at the found delimiter.
*
* return: \li
*
*      If there was no delimiter, then NULL is returned,
*      otherewise *RES is returned.
*
*/

static char *
getfield(char **res, size_t reslen, char **ptr, char delim) {
       char *q;

       if (res == NULL || ptr == NULL || *ptr == NULL) {
               errno = EINVAL;
               return (NULL);
       }

       q = strchr(*ptr, delim);

       if (q == NULL) {
               errno = EINVAL;
               return (NULL);
       } else {
               if (*res == NULL) {
                       *res = strndup(*ptr, q - *ptr);
               } else {
                       if ((size_t)(q - *ptr + 1) > reslen) { /*%< to big for res */
                               errno = EINVAL;
                               return (NULL);
                       } else {
                               strncpy(*res, *ptr, q - *ptr);
                               (*res)[q - *ptr] = 0x0;
                       }
               }
               *ptr = q + 1;
       }

       return (*res);
}





#ifndef HAVE_STRNDUP
/*
* static char * strndup(const char *str, size_t len)
*
* notes: \li
*
*      like strdup, except do len bytes instead of the whole string. Always
*      null-terminates.
*
* return: \li
*
*      The newly malloced string.
*
*/

static char *
strndup(const char *str, size_t len) {
       char *p = malloc(len + 1);

       if (p == NULL)
               return (NULL);
       strncpy(p, str, len);
       p[len] = 0x0;
       return (p);
}
#endif

#if WANT_MAIN

/*%
* static int strcmp_nws(const char *a, const char *b)
*
* notes: \li
*
*      do a strcmp, except uneven lengths of whitespace compare the same
*
* return: \li
*
*/

static int
strcmp_nws(const char *a, const char *b) {
       while (*a && *b) {
               if (isspace(*a) && isspace(*b)) {
                       do {
                               a++;
                       } while (isspace(*a));
                       do {
                               b++;
                       } while (isspace(*b));
               }
               if (*a < *b)
                       return (-1);
               else if (*a > *b)
                       return (1);

               a++;
               b++;;
       }

       if (*a == *b)
               return (0);
       else if (*a > *b)
               return (1);
       else
               return (-1);
}

#endif

/*%
* static void free_array(char **argv, size_t entries)
*
* notes: \li
*
*      Free argv and each of the pointers inside it. The end of
*      the array is when a NULL pointer is found inside. If
*      entries is > 0, then NULL pointers inside the array do
*      not indicate the end of the array.
*
*/

static void
free_array(char **argv, size_t entries) {
       char **p = argv;
       int useEntries = (entries > 0U);

       if (argv == NULL)
               return;

       while ((useEntries && entries > 0U) || *p) {
               if (*p)
                       free(*p);
               p++;
               if (useEntries)
                       entries--;
       }
       free(argv);
}





/* ************************************************** */

#if WANT_MAIN

/*% takes an option to indicate what sort of marshalling(read the code) and
  an argument. If the argument looks like a marshalled buffer(has a ':'
  embedded) then it's unmarshalled and the remarshalled and the new string
  is compared to the old one.
*/

int
main(int argc, char **argv) {
       char buffer[1024];
       char *b = &buffer[0];
       size_t len = sizeof buffer;
       char option;

       if (argc < 2 || argv[1][0] != '-')
               exit(1);

       option = argv[1][1];
       argv++;
       argc--;


#if 0
       {
               char buff[10];
               char *p = argv[1], *q = &buff[0];

               while (getfield(&q, sizeof buff, &p, ':') != NULL) {
                       printf("field: \"%s\"\n", q);
                       p++;
               }
               printf("p is now \"%s\"\n", p);
       }
#endif

#if 0
       {
               char **x = splitarray(argv[1], argv[1] + strlen(argv[1]),
                                     argv[2][0]);
               char **p;

               if (x == NULL)
                       printf("split failed\n");

               for (p = x ; p != NULL && *p != NULL ; p++) {
                       printf("\"%s\"\n", *p);
               }
       }
#endif

#if 1
       switch(option) {
       case 'n': {
               struct nwent ne;
               int i;

               if (strchr(argv[1], ':') != NULL) {
                       if (irp_unmarshall_nw(&ne, argv[1]) != 0) {
                               printf("Unmarhsalling failed\n");
                               exit(1);
                       }

                       printf("Name: \"%s\"\n", ne.n_name);
                       printf("Aliases:");
                       for (i = 0 ; ne.n_aliases[i] != NULL ; i++)
                               printf("\n\t\"%s\"", ne.n_aliases[i]);
                       printf("\nAddrtype: %s\n", ADDR_T_STR(ne.n_addrtype));
                       inet_net_ntop(ne.n_addrtype, ne.n_addr, ne.n_length,
                                     buffer, sizeof buffer);
                       printf("Net: \"%s\"\n", buffer);
                       *((long*)ne.n_addr) = htonl(*((long*)ne.n_addr));
                       inet_net_ntop(ne.n_addrtype, ne.n_addr, ne.n_length,
                                     buffer, sizeof buffer);
                       printf("Corrected Net: \"%s\"\n", buffer);
               } else {
                       struct netent *np1 = getnetbyname(argv[1]);
                       ne.n_name = np1->n_name;
                       ne.n_aliases = np1->n_aliases;
                       ne.n_addrtype = np1->n_addrtype;
                       ne.n_addr = &np1->n_net;
                       ne.n_length = (IN_CLASSA(np1->n_net) ?
                                      8 :
                                      (IN_CLASSB(np1->n_net) ?
                                       16 :
                                       (IN_CLASSC(np1->n_net) ?
                                        24 : -1)));
                       np1->n_net = htonl(np1->n_net);
                       if (irp_marshall_nw(&ne, &b, &len) != 0) {
                               printf("Marshalling failed\n");
                       }
                       printf("%s\n", b);
               }
               break;
       }


       case 'r': {
               char **hosts, **users, **domains;
               size_t entries;
               int i;
               char *buff;
               size_t size;
               char *ngname;

               if (strchr(argv[1], '(') != NULL) {
                       if (irp_unmarshall_ng(&ngname, &entries,
                                             &hosts, &users, &domains,
                                             argv[1]) != 0) {
                               printf("unmarshall failed\n");
                               exit(1);
                       }

#define STRVAL(x) (x == NULL ? "*" : x)

                       printf("%s {\n", ngname);
                       for (i = 0 ; i < entries ; i++)
                               printf("\t\"%s\" : \"%s\" : \"%s\"\n",
                                      STRVAL(hosts[i]),
                                      STRVAL(users[i]),
                                      STRVAL(domains[i]));
                       printf("}\n\n\n");


                       irp_marshall_ng_start(ngname, NULL, &size);
                       for (i = 0 ; i < entries ; i++)
                               irp_marshall_ng_next(hosts[i], users[i],
                                                    domains[i], NULL, &size);
                       irp_marshall_ng_end(NULL, &size);

                       buff = malloc(size);

                       irp_marshall_ng_start(ngname, buff, &size);
                       for (i = 0 ; i < entries ; i++) {
                               if (irp_marshall_ng_next(hosts[i], users[i],
                                                        domains[i], buff,
                                                        &size) != 0)
                                       printf("next marshalling failed.\n");
                       }
                       irp_marshall_ng_end(buff, &size);

                       if (strcmp_nws(argv[1], buff) != 0) {
                               printf("compare failed:\n\t%s\n\t%s\n",
                                      buffer, argv[1]);
                       } else {
                               printf("compare ok\n");
                       }
               } else {
                       char *h, *u, *d, *buff;
                       size_t size;

                       /* run through two times. First to figure out how
                          much of a buffer we need. Second to do the
                          actual marshalling */

                       setnetgrent(argv[1]);
                       irp_marshall_ng_start(argv[1], NULL, &size);
                       while (getnetgrent(&h, &u, &d) == 1)
                               irp_marshall_ng_next(h, u, d, NULL, &size);
                       irp_marshall_ng_end(NULL, &size);
                       endnetgrent(argv[1]);

                       buff = malloc(size);

                       setnetgrent(argv[1]);
                       if (irp_marshall_ng_start(argv[1], buff, &size) != 0)
                               printf("Marshalling start failed\n");

                       while (getnetgrent(&h, &u, &d) == 1) {
                               if (irp_marshall_ng_next(h, u, d, buff, &size)
                                   != 0) {
                                       printf("Marshalling failed\n");
                               }
                       }

                       irp_marshall_ng_end(buff, &size);
                       endnetgrent();

                       printf("success: %s\n", buff);
               }
               break;
       }



       case 'h': {
               struct hostent he, *hp;
               int i;


               if (strchr(argv[1], '@') != NULL) {
                       if (irp_unmarshall_ho(&he, argv[1]) != 0) {
                               printf("unmarshall failed\n");
                               exit(1);
                       }

                       printf("Host: \"%s\"\nAliases:", he.h_name);
                       for (i = 0 ; he.h_aliases[i] != NULL ; i++)
                               printf("\n\t\t\"%s\"", he.h_aliases[i]);
                       printf("\nAddr Type: \"%s\"\n",
                              ADDR_T_STR(he.h_addrtype));
                       printf("Length: %d\nAddresses:", he.h_length);
                       for (i = 0 ; he.h_addr_list[i] != 0 ; i++) {
                               inet_ntop(he.h_addrtype, he.h_addr_list[i],
                                         buffer, sizeof buffer);
                               printf("\n\t\"%s\"\n", buffer);
                       }
                       printf("\n\n");

                       irp_marshall_ho(&he, &b, &len);
                       if (strcmp(argv[1], buffer) != 0) {
                               printf("compare failed:\n\t\"%s\"\n\t\"%s\"\n",
                                      buffer, argv[1]);
                       } else {
                               printf("compare ok\n");
                       }
               } else {
                       if ((hp = gethostbyname(argv[1])) == NULL) {
                               perror("gethostbyname");
                               printf("\"%s\"\n", argv[1]);
                               exit(1);
                       }

                       if (irp_marshall_ho(hp, &b, &len) != 0) {
                               printf("irp_marshall_ho failed\n");
                               exit(1);
                       }

                       printf("success: \"%s\"\n", buffer);
               }
               break;
       }


       case 's': {
               struct servent *sv;
               struct servent sv1;

               if (strchr(argv[1], ':') != NULL) {
                       sv = &sv1;
                       memset(sv, 0xef, sizeof (struct servent));
                       if (irp_unmarshall_sv(sv, argv[1]) != 0) {
                               printf("unmarshall failed\n");

                       }

                       irp_marshall_sv(sv, &b, &len);
                       if (strcmp(argv[1], buffer) != 0) {
                               printf("compare failed:\n\t\"%s\"\n\t\"%s\"\n",
                                      buffer, argv[1]);
                       } else {
                               printf("compare ok\n");
                       }
               } else {
                       if ((sv = getservbyname(argv[1], argv[2])) == NULL) {
                               perror("getservent");
                               exit(1);
                       }

                       if (irp_marshall_sv(sv, &b, &len) != 0) {
                               printf("irp_marshall_sv failed\n");
                               exit(1);
                       }

                       printf("success: \"%s\"\n", buffer);
               }
               break;
       }

       case 'g': {
               struct group *gr;
               struct group gr1;

               if (strchr(argv[1], ':') != NULL) {
                       gr = &gr1;
                       memset(gr, 0xef, sizeof (struct group));
                       if (irp_unmarshall_gr(gr, argv[1]) != 0) {
                               printf("unmarshall failed\n");

                       }

                       irp_marshall_gr(gr, &b, &len);
                       if (strcmp(argv[1], buffer) != 0) {
                               printf("compare failed:\n\t\"%s\"\n\t\"%s\"\n",
                                      buffer, argv[1]);
                       } else {
                               printf("compare ok\n");
                       }
               } else {
                       if ((gr = getgrnam(argv[1])) == NULL) {
                               perror("getgrnam");
                               exit(1);
                       }

                       if (irp_marshall_gr(gr, &b, &len) != 0) {
                               printf("irp_marshall_gr failed\n");
                               exit(1);
                       }

                       printf("success: \"%s\"\n", buffer);
               }
               break;
       }


       case 'p': {
               struct passwd *pw;
               struct passwd pw1;

               if (strchr(argv[1], ':') != NULL) {
                       pw = &pw1;
                       memset(pw, 0xef, sizeof (*pw));
                       if (irp_unmarshall_pw(pw, argv[1]) != 0) {
                               printf("unmarshall failed\n");
                               exit(1);
                       }

                       printf("User: \"%s\"\nPasswd: \"%s\"\nUid: %ld\nGid: %ld\n",
                              pw->pw_name, pw->pw_passwd, (long)pw->pw_uid,
                              (long)pw->pw_gid);
                       printf("Class: \"%s\"\nChange: %ld\nGecos: \"%s\"\n",
                              pw->pw_class, (long)pw->pw_change, pw->pw_gecos);
                       printf("Shell: \"%s\"\nDirectory: \"%s\"\n",
                              pw->pw_shell, pw->pw_dir);

                       pw = getpwnam(pw->pw_name);
                       irp_marshall_pw(pw, &b, &len);
                       if (strcmp(argv[1], buffer) != 0) {
                               printf("compare failed:\n\t\"%s\"\n\t\"%s\"\n",
                                      buffer, argv[1]);
                       } else {
                               printf("compare ok\n");
                       }
               } else {
                       if ((pw = getpwnam(argv[1])) == NULL) {
                               perror("getpwnam");
                               exit(1);
                       }

                       if (irp_marshall_pw(pw, &b, &len) != 0) {
                               printf("irp_marshall_pw failed\n");
                               exit(1);
                       }

                       printf("success: \"%s\"\n", buffer);
               }
               break;
       }

       default:
               printf("Wrong option: %c\n", option);
               break;
       }

#endif

       return (0);
}

#endif

/*! \file */