/* isp.c - Internal Signaling Protocol test generator */

/* Written 1997 by Werner Almesberger, EPFL-ICA */


#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>

#include <atm.h>
#include <linux/atmsvc.h>

#include "isp.h"


extern int yyparse(void);


static int in,out;


void send_msg(const struct atmsvc_msg *msg)
{
    int wrote;

    wrote = write(out,msg,sizeof(*msg));
    if (wrote == sizeof(*msg)) return;
    if (wrote < 0) perror("write");
    else fprintf(stderr,"bad write: %d != %d\n",wrote,sizeof(*msg));
    exit(1);
}


void recv_msg(struct atmsvc_msg *msg)
{
    int got;

    got = read(in,msg,sizeof(*msg));
    if (got == sizeof(*msg)) return;
    if (got < 0) perror("read");
    else fprintf(stderr,"bad read: %d != %d\n",got,sizeof(*msg));
    exit(1);
}


static struct errno_table {
    const char *name;
    int value;
} table[] = {
#include "errnos.inc"
    { NULL, 0 }
};


int str2errno(const char *str)
{
    const struct errno_table *walk;

    for (walk = table; walk->name; walk++)
	if (!strcmp(walk->name,str)) break;
    return walk->value;
}


static const char *errno2str(int code)
{
    static char buf[30]; /* probably large enough :) */
    const struct errno_table *walk;

    for (walk = table; walk->name; walk++) {
	if (walk->value == code) return walk->name;
	if (walk->value == -code) {
	    sprintf(buf,"-%s",walk->name);
	    return buf;
	}
    }
    sprintf(buf,"%d (0x%x)",code,code);
    return buf;
}


#define F_VCC		0x00000001
#define F_LISTEN	0x00000002
#define F_REPLY		0x00000004
#define F_PVC		0x00000008
#define F_LOCAL		0x00000010
#define F_QOS		0x00000020
#define F_SVC		0x00000040
#define F_SAP		0x00000080


void dump_msg(const struct atmsvc_msg *msg)
{
    static struct {
	enum atmsvc_msg_type type;
	const char *name;
	int fields;
    } *type, types[] = {
	{ as_bind,	"bind",		F_VCC | F_SVC | F_SAP },
	{ as_okay,	"okay",		F_VCC | F_PVC | F_LOCAL | F_QOS |
					F_SVC | F_SAP },
	{ as_connect,	"connect",	F_VCC | F_LOCAL | F_QOS | F_SVC |
				        F_SAP },
	{ as_indicate,	"indicate",	F_LISTEN | F_QOS | F_SVC | F_SAP },
	{ as_accept,	"accept",	F_VCC | F_LISTEN },
	{ as_reject,	"reject",	F_LISTEN | F_REPLY },
	{ as_error,	"error",	F_VCC | F_REPLY },
	{ as_close,	"close",	F_VCC | F_REPLY },
	{ as_itf_notify,"itf_notify",	F_PVC },
	{ as_modify,	"modify",	F_VCC | F_REPLY | F_QOS },
	{ as_identify,	"identify",	F_VCC | F_LISTEN },
	{ 0,		NULL,		0 }
    };
    char buf[1000]; /* bigger than any MAX_ATM_*_LEN */

    for (type = types; type->name; type++)
	if (type->type == msg->type) break;
    if (!type->name) {
	printf("Unknown type %d\n",(int) msg->type);
	return;
    }
    printf("Type=%s",type->name);
    if (type->fields & F_VCC)
	printf(", vcc=%ld (0x%lx)",msg->vcc,msg->vcc);
    if (type->fields & F_LISTEN)
	printf(", listen=%ld (0x%lx)",msg->listen_vcc,msg->listen_vcc);
    if (type->fields & F_REPLY)
	printf(", reply=%s",errno2str(msg->reply));
    if (type->fields & F_PVC) {
	if (atm2text(buf,sizeof(buf),(struct sockaddr *) &msg->pvc,
	  A2T_PRETTY | A2T_NAME) < 0) strcpy(buf,"<invalid>");
	printf(",\n  pvc=%s",buf);
    }
    if (type->fields & F_LOCAL) {
	if (atm2text(buf,sizeof(buf),(struct sockaddr *) &msg->local,
	  A2T_PRETTY | A2T_NAME) < 0) strcpy(buf,"<invalid>");
	printf(",\n  local=%s",buf);
    }
    if (type->fields & F_QOS) {
	if (qos2text(buf,sizeof(buf),&msg->qos,0) < 0) strcpy(buf,"<invalid>");
	printf(",\n  qos=%s",buf);
    }
    if (type->fields & F_SVC) {
	if (atm2text(buf,sizeof(buf),(struct sockaddr *) &msg->svc,
	  A2T_PRETTY | A2T_NAME) < 0) strcpy(buf,"<invalid>");
	printf(",\n  svc=%s",buf);
    }
    if (type->fields & F_SAP) {
	if (sap2text(buf,sizeof(buf),&msg->sap,S2T_NAME) < 0)
	    strcpy(buf,"<invalid>");
	printf(",\n  sap=%s",buf);
    }
    putchar('\n');
}


int main(int argc,char **argv)
{
    if (argc != 3) {
	fprintf(stderr,"usage: %s input output\n",*argv);
	return 1;
    }
    if ((in = open(argv[1],O_RDWR)) < 0) { /* actually read-only */
	perror(argv[1]);
	return 1;
    }
    if ((out = open(argv[2],O_RDWR)) < 0) { /* actually write-only */
	perror(argv[2]);
	return 1;
    }
    return yyparse();
}
