#if HAVE_CONFIG_H
#include <config.h>
#endif

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include <stdarg.h>

#if HAVE_STRINGS_H
#include <strings.h>
#endif

#include "irrdi.h"
#include "ro.h"
#include "trace.h"
#include "utils.h"

int route2ro(struct ro* queue,char* text, struct ro* ra, char* as);

/* struct ro* ro_head=NULL; */
extern int debug, allroutes, quiet, no_expand_as;

static int ases=0, asmcs=0, routes=0;

struct ro*
expansion_enq(struct ro* ro_head,struct ro* ra)
{ 
	struct ro* rb;
	
	if(!ro_head) { 
		ro_head=ra;
		return ra;
	};
	rb=ro_head;

	/* check for duplicate entries */
	do { 
		if(ra->type==rb->type && ra->text && rb->text && 
			!strcasecmp(ra->text,rb->text)) {
			if(debug) 
				SX_TRACE2("%s - duplicate", ra->text);
			return NULL; 
		};
		if(!rb->next) break;
		rb=rb->next;
	} while(1);

	rb->next=ra;
	if(ra->type==RO_AS) { 
		ases++;
	} else if(ra->type==RO_MC) { 
		asmcs++;
	} else if(ra->type==RO_RO) { 
		routes++;
	};
		
	return ra;
};

struct ro* 
ro_init()
{ 
	struct ro* ro=malloc(sizeof(struct ro));
	if(!ro) { 
		perror("malloc");
		exit(1);
	};
	bzero(ro,sizeof(struct ro));
	return ro;
};

int
ro_minus() 
{ 
	SX_TRACE(SX_TRACE_NOTICE,"ro_minus is still stub... \n");
	return 1;
};

int
ro_exists(struct ro* ro, char* text)
{ 
	struct rpsl_ip* rp=NULL;
	
	if(isdigit(text[0])) { 
		rp=rpsl_translate(text);
		if(!rp) return 0;
	};
	
	while(ro && ro->text) { 
		if(!strcasecmp(ro->text,text)) return 1;
		if(rp && ro->type==RO_RPSL && rpsl_filters(ro->rpsl,rp->addr.s_addr,
			rp->len)){
			if(debug) 
				SX_TRACE2("%s filtered by %s",text,ro->text);
			free(rp);
			return 1; 
		};
		ro=ro->next;
	};
	free(rp);
	return 0;
};

int
ro_free(struct ro* ra)
{ 
	if(!ra) return 0;
	if(ra->text) free(ra->text);
	if(ra->ovl) free(ra->ovl);
	if(ra->as) free(ra->as);
	if(ra->rpsl) rpsl_free(ra->rpsl);
	free(ra);
	return 0;
};

int
ro_queue_free(struct ro* ra)
{ 
	struct ro* rb;

	while(ra) { 
		rb=ra->next;
		ro_free(ra);
		ra=rb;
	};
	return 1;
};

struct ro* 
ro_fixup(struct ro* ro) 
{ 
	struct ro* rn;
nextone:
	if(!ro) return NULL;
	rn=ro->next;
	if(ro->text) return ro;
	ro_free(ro);
	ro=rn;
	goto nextone;
};

int
ro_sort(struct ro* ra, struct ro* rb)
{ 
	if(ra->type!=RO_RO) return -1;

	if(rb->type==RO_RPSL && rpsl_filters(rb->rpsl,ra->addr,ra->masklen)) {
		if(debug) { 
			SX_TRACE2("%s masks %s/%i",rb->text,
				inet_ntoa(*(struct in_addr*)&ra->addr),ra->masklen);
		};
		return 0;
	};

	if(rb->type!=RO_RO) return 1;

	if(ntohl(ra->addr)==ntohl(rb->addr) && ra->mask==rb->mask) return 0;

	if(ntohl(ra->lastaddr) < ntohl(rb->addr)) return -1;
	if(ntohl(ra->addr) > ntohl(rb->lastaddr)) return 1;

	if(ntohl(ra->addr) <= ntohl(rb->addr) && 
		ntohl(ra->lastaddr) >= ntohl(rb->lastaddr)) { 
		rb->flag|=RO_OVL;
		if(ra->as && rb->as && !strcasecmp(ra->as,rb->as)) 
			 return -2;
		return -1;
	};

	if(ntohl(ra->addr) >= ntohl(rb->addr) && 
		ntohl(ra->lastaddr) <= ntohl(rb->lastaddr)) {
		ra->flag|=RO_OVL;
		if(ra->as && rb->as && !strcasecmp(ra->as,rb->as)) 
			return 2;
		return 1;
	};

	SX_TRACE(SX_TRACE_ERROR,"unbeliveable... can't check %s and %s....\n",
		ra->text, rb->text);
	return 0;
};

int
expansion_add(struct ro* ro_head,char* text, int type)
{ 
	struct ro* ra;

	if(type==RO_RO) 
		return route2ro(ro_head,text,NULL,NULL);
	
	ra=malloc(sizeof(struct ro));
	if(!ra) return 0;
	bzero(ra,sizeof(struct ro));
	ra->text=strdup(text);
	ra->type=type;

	if(type==RO_RPSL) { 
		ra->rpsl=rpsl_translate(text);
		if(!ra->rpsl) { 
			fprintf(stderr,"!Untranslatable object: %s\n",text);
			ro_free(ra);
			return 0;
		};
		ra->addr=ra->rpsl->addr.s_addr;
		ra->mask=ra->rpsl->len==32?0xffffffff:
			htonl(0xffffffff<<(32-ra->rpsl->len));
		ra->masklen=ra->rpsl->len;
	};
		
	if(debug) { 
		SX_TRACE2("!added to future expansion: %s %s\n", 
			ra->type==RO_RO?"route":
			(ra->type==RO_AS?"AS":(ra->type==RO_RPSL?"RPSL FILTER":"AS-MACRO")),
			ra->text);
	};
	expansion_enq(ro_head,ra);

	return 1;
};

int
route2ro(struct ro* ro_head,char* text, struct ro* ra, char* as)
{ 
	char* c;
	int mle;
	int needenq=0;
	struct ro* rb, *rc;
	char buffer[512];

	if(!text) return 0;
	if(!ra) { 
		ra=malloc(sizeof(struct ro));
		if(!ra) return 0;
		bzero(ra,sizeof(struct ro));
		ra->text=strdup(text);
		ra->type=RO_RO;
		if(as) ra->as=strdup(as);
		needenq=1;
	};
	c=text;
	while(isdigit(*c)||*c=='.') c++;
	if(*c=='/') { 
		*c=0;
		ra->addr=inet_addr(text);
		c++;
		mle=atoi(c);
		*(--c)='/';
	} else { 
		/* there is not valid a.a.a.a/b ... */
		printf("!incorrect syntax with %s, assuming masklen /24\n",
			text); 
		*c=0;
		ra->addr=inet_addr(text);
		mle=24;
	};
		
	if(mle<1 || mle>32) { 
		SX_TRACE(SX_TRACE_NOTICE,"Incorrect masklen %i in %s (assuming /24)\n",mle,text);
		ra->mask=0xffffff00;
		ra->mask=htonl(ra->mask);
		ra->masklen=24;
	} else { 
		ra->mask=0xffffffff;
		ra->mask<<=32-mle;
		ra->mask=htonl(ra->mask);
		ra->masklen=mle;
	};

	ra->lastaddr=ra->addr|(~ra->mask);

/*
	if(!needenq) return 1;
*/
	
	rb=ro_head;
	rc=rb;

	while(rb) { 

		switch(ro_sort(ra,rb)) { 
			case 0: 
				ro_free(ra);
				return 0;
			case 2:
				if(((ra->as && rb->as && !strcasecmp(ra->as, rb->as)) || (!ra->as && !rb->as))
					&& !ra->ovl) { 
					snprintf(buffer,sizeof(buffer)-1,"overlayed by %s in the same %s", rb->text, rb->as?rb->as:"NONE");
					ra->ovl=strdup(buffer);
				};
				ra->flag|=RO_OVL;

				ra->next=rb->next;
				rb->next=ra;
				return 1;
			case -2: 
				
				if(((ra->as && rb->as && !strcasecmp(ra->as, rb->as)) || (!ra->as && !rb->as))
					&& !rb->ovl) { 
					snprintf(buffer,sizeof(buffer)-1,"overlayed by %s in the same %s", ra->text, ra->as?ra->as:"NONE");
					rb->ovl=strdup(buffer);
				};
				rb->flag|=RO_OVL;

				ra->next=rb;
				if(rc==rb) { 
					ro_head=ra;
				} else { 
					rc->next=ra;
				};

				rc=rb->next;

				while(rc) { 
					if(ro_sort(rc,ra)!=2) return 1;
					if(((ra->as && rc->as && !strcasecmp(ra->as, rc->as)) || (!ra->as && !rc->as))
                                        	&& !rc->ovl) { 
						snprintf(buffer,sizeof(buffer)-1,"overlayed by %s in the same %s", ra->text, ra->as?ra->as:"NONE");
                                        	rc->ovl=strdup(buffer);
                                	};
                                	rc->flag|=RO_OVL;
					rc=rc->next;
				};
                                
				return 1;
			case -1: 
				ra->next=rb;
				if(rc==rb) { 
					ro_head=ra;
				} else { 
					rc->next=ra;
				};
				return 1;
			case 1: 
				rc=rb;
				rb=rb->next;
				continue;

			};
		rb=rb->next;
	};

	if(needenq) expansion_enq(ro_head,ra);
	return 1;
};

struct irrdc*
ir_init(char* host, int port, char* source)
{ 
	struct irrdc* ir;

	if(!(ir=irrd_init(host,port))) { 
		perror("!irrd_init");
		return NULL;
	};
	if(!irrd_connect(ir)) { 
		perror("!irrd_connect");
		return NULL;
	};

	if(source) { 
		if(!irrd_question(ir,"!s%s",source)) { 
			perror("!Can't set source");
			return NULL;
		};
	};
	return ir;
};

int 
expand(struct ro* ro_head,char* host, int port, struct ro* excep, char* source)
{ 
	struct irrdc* ir=NULL;
	char* c,*d;
	struct ro* ra;

	if(!ro_head) { 
		if(debug) SX_TRACE2("Nothing to expand...\n");
		return 0;
	};


	ra=ro_head;

	while(ra) { 
		if(ra->type==RO_RO || ra->type==RO_RPSL) { 
/*			route2ro(ra->text,ra,NULL); */
		} else if(ra->type==RO_AS) { 
			if(no_expand_as) 
				goto nextra;
			if(!ir) { 
				ir=ir_init(host,port,source);
				if(!ir) { 
					SX_TRACE(SX_TRACE_ERROR,"!Fatal: Can't connect %s:%i: %s\n",
						host, port, strerror(errno));
					return 0;
				};
			}; 
			if(!irrd_question(ir,"!g%s",ra->text)) { 
				SX_TRACE(SX_TRACE_ERROR,"!Fatal: unparseable answer or fatal error \n"
					"!(may be RIPE/RPSL Tool query language not supported by this server)");
				return 0;
			};
			if(ir->answer) { 
				c=ir->answer;
nextro:
				while(isspace(*c)) c++;
				d=c;
				while(!isspace(*c)) c++;
				*c=0;
				if(!excep || !ro_exists(excep,d)) 
					route2ro(ro_head,d,NULL,ra->text);
				if(*(c+1)!=0) { c++; goto nextro; };
			};
		} else if(ra->type==RO_MC) { 
			if(!ir) { 
				ir=ir_init(host,port,source);
				if(!ir) { 
					SX_TRACE(SX_TRACE_ERROR,"!Fatal: Cant connect %s:%i: %s\n", 
						host, port, strerror(errno));
					return 0;
				};
			}; 
			if(!irrd_question(ir,"!i%s,1",ra->text)) { 
				SX_TRACE(SX_TRACE_ERROR,"!Fatal: unparseable answer or fatal error\n"
					"!(may be RIPE/RPSL Tool query language not supported by this server)");
				return 0;
			};
			if(ir->answer) { 
				c=ir->answer;
nextas:
				while(isspace(*c)) c++;
				d=c;
				while(!isspace(*c)) c++;
				*c=0;
				if(!excep || ! ro_exists(excep,d)) 
					expansion_add(ro_head,d,RO_AS);
				if(*(c+1)!=0) { c++; goto nextas; };
			};
		} 
nextra:
		ra=ra->next;
	};
	irrd_disc(ir);
	irrd_free(ir);
	ir=NULL;
	return 1;
};

static unsigned 
is_numbered(char* string,int extrange,FILE* f)
{ 
	char* ep;
	unsigned long retval;

	if(!string) return 0;

	retval=strtoul(string,&ep,10);
	
	if(ep && *ep) return 0;
	if(errno==ERANGE) return 0;
	if(retval>65535) return 0;
	if(extrange) {
		if(retval<100 || (retval>199 && retval<2000) || retval>2699) {
			fprintf(f,"\
!=============================================================================
! WARNING ! %s is out of extended access-list ranges (100-199 and 2000-2699)
!=============================================================================
",string); 
			exit(1);
		};
	} else {
		if((retval>100 && retval<1300) || retval>1999) {
			fprintf(f,"\
!=============================================================================
! WARNING ! %s is out of standard access-list ranges (1-99 and 1300-1999)
!=============================================================================
",string); 

			exit(1);
		};
	};

	return (unsigned)retval;
};

int 
print_eacl(struct ro* ro_head,FILE* f,char* name, int headers)
{ 
	struct ro* ra=ro_head;
	unsigned numbered=is_numbered(name,1,f);

	if(headers) {
		if(numbered) { 
			fprintf(f,"no access-list %s\n",name);
		} else { 
			fprintf(f,"no ip access-list extended %s\n",name?name:"UNKNOWN");
			fprintf(f,"ip access-list extended %s\n",name?name:"UNKNOWN");
		};
	};
	
	while(ra) { 
		if(!quiet || !ra->ovl) { 
			if(ra->ovl && !(ra->flag&RO_AGGR)) { 
				fprintf(f,"! %s\n",ra->ovl);
				if(!allroutes) fprintf(f,"! - ");
			};
			if(ra->type==RO_RO && !(ra->flag&RO_AGGR)) {
				if(numbered) 
					fprintf(f,"access-list %s",name);
				fprintf(f," permit ip host %s host ",
					inet_ntoa(*(struct in_addr*)&ra->addr));
				fprintf(f,"%s\n",inet_ntoa(*(struct in_addr*)&ra->mask));
			} else if (ra->type==RO_RPSL && !(ra->flag&RO_AGGR)) { 
				if(numbered) 
					fprintf(f,"access-list %s",name);
				fprintf(f," permit ip ");
				rpsl_print_filter(f,ra->rpsl,2);
				fprintf(f,"\n");
			};
		};
		ra=ra->next;
	};
	if(headers) { 
		if(numbered) 
			fprintf(f,"access-list %s",name);
		fprintf(f," deny ip any any\n");
	};
	return 1;
};

int 
print_stacl(struct ro* ro_head,FILE* f,char* name, int headers)
{ 
	struct ro* ra=ro_head;
	unsigned numbered=is_numbered(name,0,f);

	if(headers) { 
		if(numbered) { 
			fprintf(f,"no access-list %s\n",name);
		} else { 
			fprintf(f,"no ip access-list standard %s\n",name?name:"UNKNOWN");
			fprintf(f,"ip access-list standard %s\n",name?name:"UNKNOWN");
		};
	};
	
	while(ra) { 
		if((!quiet || !ra->ovl) && !(ra->flag&RO_AGGR)) { 
			if(ra->ovl) { 
				fprintf(f,"! %s\n",ra->ovl);
				if(!allroutes) fprintf(f,"! - ");
			};
			if(ra->type==RO_RO) { 
				if(numbered) 
					fprintf(f,"access-list %s",name);
				fprintf(f," permit ip %s ",
					inet_ntoa(*(struct in_addr*)&ra->addr));
				ra->mask=~ra->mask;
				fprintf(f,"%s\n",inet_ntoa(*(struct in_addr*)&ra->mask));
			} else if (ra->type==RO_RPSL) { 
				if(numbered)
					fprintf(f," permit ip ");
				rpsl_print_filter(f,ra->rpsl,2);
				fprintf(f,"\n");
			};
		};
		ra=ra->next;
	};
	return 1;
};
int 
print_prefixlist(struct ro* ro_head,FILE* f, char* name, int headers)
{
	struct ro* ra=ro_head;

	if(headers) 
		fprintf(f,"no ip prefix-list %s\n", name?name:"UNKNOWN");
	
	while(ra) { 
		if((!quiet || !ra->ovl) && !(ra->flag&RO_AGGR)) {
			if(ra->ovl) { 
				fprintf(f,"! %s\n",ra->ovl);
				if(!allroutes) fprintf(f,"! - ");
			};
			if(ra->type==RO_RO) { 
				fprintf(f,"ip prefix-list %s permit %s/%i\n", 
					name?name:"UNKNOWN",inet_ntoa(*(struct in_addr*)&ra->addr),
					ra->masklen);
			} else if(ra->type==RO_RPSL) { 
				fprintf(f,"ip prefix-list %s permit ",name?name:"UNKNOWN");
				rpsl_print_filter(f,ra->rpsl,1);
				fprintf(f,"\n");
			};
		};
		ra=ra->next;
	};
	return 1;
};

int 
print_iacl(struct ro* ro_head,FILE* f,char* name, int headers)
{ 
	struct ro* ra=ro_head;
	unsigned nmas;
	unsigned numbered=is_numbered(name,1,f);

	if(headers) { 
		if(numbered) { 
			fprintf(f,"no access-list %s\n",name);
		} else { 
			fprintf(f,"no ip access-list extended %s\n",name?name:"UNKNOWN");
			fprintf(f,"ip access-list extended %s\n",name?name:"UNKNOWN");
		};
	};
	
	while(ra) { 
		if(ra->flag&RO_AGGR) goto next;
		if((ra->type==RO_RO || ra->type==RO_RPSL) && 
			(!quiet || !ra->flag&RO_OVL)) {
			if(ra->flag&RO_OVL) { 
				if(!allroutes) fprintf(f,"! - ");
			};
			if(numbered) 
				fprintf(f,"access-list %s",name);
			fprintf(f," permit ip %s ",inet_ntoa(*(struct in_addr*)&ra->addr));
			nmas=~ra->mask;
			fprintf(f,"%s any\n",inet_ntoa(*(struct in_addr*)&nmas));
		};
next:
		ra=ra->next;
	};
	if(headers) { 
		if(numbered) 
			fprintf(f,"access-list %s",name);
		fprintf(f," deny ip any any\n");
	};
	return 1;
};

int 
print_oacl(struct ro* ro_head,FILE* f,char* name, int headers)
{ 
	struct ro* ra=ro_head;
	unsigned nmas;
	unsigned numbered=is_numbered(name,1,f);

	if(headers) { 
		if(numbered) {
			fprintf(f,"no access-list %s\n",name);
		} else { 
			fprintf(f,"no ip access-list extended %s\n",name?name:"UNKNOWN");
			fprintf(f,"ip access-list extended %s\n",name?name:"UNKNOWN");
		};
	};
	
	while(ra) { 
		if(ra->flag&RO_AGGR) goto next;
		if((ra->type==RO_RO || ra->type==RO_RPSL) && 
			(!quiet || !ra->flag&RO_OVL)) {

			if(ra->flag&RO_OVL) { 
				if(!allroutes) fprintf(f,"! - ");
			};
			if(numbered) 
				fprintf(f,"access-list %s",name);

			fprintf(f," permit ip any %s ",
				inet_ntoa(*(struct in_addr*)&ra->addr));

			nmas=~ra->mask;
			fprintf(f,"%s\n",inet_ntoa(*(struct in_addr*)&nmas));
		};
next:
		ra=ra->next;
	};
	if(headers) {
		if(numbered)
			fprintf(f,"access-list %s",name);
		fprintf(f," deny ip any any\n");
	};
	return 1;
};

int
print_statistics(FILE* f)
{ 
	fprintf(f,"as-macros: %i\n",asmcs);
	fprintf(f,"as       : %i\n",ases);
	fprintf(f,"routes   : %i\n",routes);
	return 1;
};

int 
print_gated(struct ro* ro_head,FILE* f, char* name, int headers)
{
	struct ro* ra=ro_head;
	
	while(ra) { 
		if((!quiet || !ra->ovl) && !(ra->flag&RO_AGGR)) {
			if(ra->ovl) { 
				fprintf(f,"# %s\n",ra->ovl);
				if(!allroutes) fprintf(f,"# - ");
			};
			if(ra->type==RO_RO) { 
				fprintf(f,"%s masklen %i exact ;\n",
					inet_ntoa(*(struct in_addr*)&ra->addr), ra->masklen);
			} else if(ra->type==RO_RPSL) { 
				rpsl_print_filter(f,ra->rpsl,3);
				fprintf(f,"\n");
			};
		};
		ra=ra->next;
	};
	return 1;
};

static int as_path_sort(struct ro* head);

int
print_aspath(struct ro* head, FILE* f, char* name, int headers, int number)
{ 
	struct ro* ra=head;
	int nc=0;

	as_path_sort(head);

	if(headers) 
		fprintf(f,"no ip as-path access-list %s\n",name?name:"NN");

	/* is the 'starting as' is in list ? */
	while(ra) { 
		if(atoi(ra->text+2)==number) 
			fprintf(f,"ip as-path access-list %s permit ^%i(_%i)*$\n",
				name?name:"NN",number,number);
		ra=ra->next;
	};
	
	ra=head;

	while(ra) { 
		if(ra->type==RO_AS) { 
			if(atoi(ra->text+2)==number) goto noprint;
			if(!nc) { 
				fprintf(f,"ip as-path access-list %s permit ^%i(_[0-9]+)*_(%s",
					name?name:"NN",number,ra->text+2);
			} else { 
				fprintf(f,"|%s",ra->text+2);
			};
			nc++;
			if(nc==4) { 
				fprintf(f,")$\n");
				nc=0;
			};
		};
noprint:
		ra=ra->next;
	};
	if(nc) 
		fprintf(f,")$\n");
	return 1;
};

struct rpsl_ip*
ro2rpsl(struct ro* ro)
{ 
	struct rpsl_ip* rp;
	if(!ro) return NULL;
	rp=malloc(sizeof(struct rpsl_ip));
	if(!rp) return NULL;

	memset(rp,0,sizeof(struct rpsl_ip));

	rp->addr.s_addr=ro->addr;
	rp->len=ro->masklen;
	return rp;
};


struct ro* 
rpsl_ip_new(unsigned long addr, int len, int len1, int len2)
{ 
	struct ro* ro=malloc(sizeof(struct ro));
	struct rpsl_ip* rpsl=malloc(sizeof(struct rpsl_ip));

	if(!ro || !rpsl) return NULL;

	memset(ro,0,sizeof(struct ro));
	memset(rpsl,0,sizeof(struct rpsl_ip));

	ro->addr=rpsl->addr.s_addr=addr;
	ro->masklen=rpsl->len=len;
	ro->mask=(rpsl->len==32?0xffffffff:
		htonl(0xffffffff<<(32-rpsl->len)));
	rpsl->len=len;
	rpsl->len1=len1;
	rpsl->len2=len2;
	ro->rpsl=rpsl;
	ro->text=strdup("Autocreated");
	ro->type=RO_RPSL;

	return ro;
};

struct ro*
can_aggregate(struct ro* ra, struct ro* rb)
{ 
	unsigned long wildaddr;
	
	if(ra->masklen!=rb->masklen) return 0;

	wildaddr=0xffffffffl<<(32-ra->masklen+1); 
	wildaddr=htonl(wildaddr);

	if((ra->addr&wildaddr)^(rb->addr&wildaddr)) {
		return NULL;
	};
	/* theoretically can.. at least addresses are eq.. */

	/* easiest case - both are non-rpsl yet :) */
	if(!ra->rpsl->len1 && !rb->rpsl->len1)
		return rpsl_ip_new(ra->addr&wildaddr,ra->masklen-1,ra->masklen,
			ra->masklen);

	/* another easy case */
	if(ra->rpsl->len1==rb->rpsl->len1 && 
		ra->rpsl->len2==rb->rpsl->len2) { 
		return rpsl_ip_new(ra->addr&wildaddr,ra->masklen-1,
			ra->rpsl->len1,rb->rpsl->len2);
	};

	/* all other cases are not-so-easy , so do not disturb now :) */
	return NULL;
};
		
	
int
ro_aggregate(struct ro* ro_head)
{ 
	struct ro* ra, *rb, *rnew; 
	int do_progon=1;

	/* how can we got here ? :) */
	if(!ro_head) return 0;

	ra=ro_head;
	while(ra) { 
		if(ra->type==RO_RO && !ra->rpsl) { 
			ra->rpsl=ro2rpsl(ra);
		};
		ra=ra->next;
	};

nextprogon:
	if(!do_progon) return 1;
	do_progon=0;

	ra=ro_head;

	/* have no need to aggregate empty, or one-element lists.. */
	if(!ra) return 0;
	rb=ra->next;
	if(!rb) return 0;

nextra: /* (this label violates Copyright (C) of nextra company */
	while(ra && (ra->flag&RO_OVL || !(ra->type==RO_RO || ra->type==RO_RPSL)))
		ra=ra->next; 
	if(!ra) goto nextprogon;

nextrb:
	rb=ra->next;
	if(!rb) goto nextprogon;
	while(rb && (rb->flag&RO_OVL || !(rb->type==RO_RO || rb->type==RO_RPSL))) 
		rb=rb->next;
	if(!rb) goto nextprogon;

	if((rnew=can_aggregate(ra,rb))) { 
		static char buffer[128];
		rnew->next=ra->next;
		ra->next=rnew;
		ra->flag|=RO_OVL|RO_AGGR;
		rb->flag|=RO_OVL|RO_AGGR;
		do_progon=1;
		snprintf(buffer,sizeof(buffer),"Aggregated into %s/%i^%i-%i",
			inet_ntoa(rnew->rpsl->addr),rnew->rpsl->len,rnew->rpsl->len1,
			rnew->rpsl->len2);
		ra->ovl=strdup(buffer);
		rb->ovl=strdup(buffer);
		ra=rb;
		goto nextra;
	} else {
		ra=rb;
		goto nextrb;
	};
	goto nextprogon;	
};

int 
print_formatted(struct ro* ro_head,FILE* f,char* name, char* format)
{ 
	struct ro* ra=ro_head;
	
	while(ra) { 
		if(!quiet || !ra->ovl) { 
			if(ra->ovl) { 
				fprintf(f,"! %s\n",ra->ovl);
				if(!allroutes) fprintf(f,"! - ");
			};
			if(ra->type==RO_RO || ra->type==RO_RPSL) {
				char* c=format;
				while(*c) { 
					if(*c=='%') {
						switch(*(c+1)) { 
							case 'r': 
							case 'n': 
								fprintf(f,"%s",
									inet_ntoa(*(struct in_addr*)&ra->addr));
								break;
							case 'm':
								fprintf(f,"%s",
									inet_ntoa(*(struct in_addr*)&ra->mask));
								break;
							case 'l':
								fprintf(f,"%i",ra->masklen);
								break;
							default: 
								fprintf(stderr,"Unknown format char %c\n",
									*(c+1));
								exit(-1);
						};
						c++;
					} else if (*c=='\\') { 
						if(*(c+1)=='n') { 
							fwrite("\n",1,1,f);
							c++;
						} else { 
							fwrite(c,1,1,f);
						};
					} else { 
						fwrite(c,1,1,f);
					};
					c++;
				};
			};
		};
		ra=ra->next;
	};
	return 1;
};

int
ro_refinery(struct ro* ro, int ref)
{ 
	while(ro) { 
		if(ro->type==RO_RO && ro->masklen<ref) { 
			ro->rpsl=malloc(sizeof(struct rpsl_ip));
			memset(ro->rpsl,0,sizeof(struct rpsl_ip));
			ro->rpsl->addr.s_addr=ro->addr;
			ro->rpsl->len=ro->rpsl->len1=ro->masklen;
			ro->rpsl->len2=ref;
			ro->type=RO_RPSL;
		} else if(ro->type==RO_RPSL && ro->rpsl->len2<ref) { 
			ro->rpsl->len2=ref;
		};
		ro=ro->next;
	};
	return 1;
};

static int
as_path_sort(struct ro* head)
{ 
	struct ro* ra, *rb;
	int changed;

nextstep:
	changed=0;
	ra=rb=head;
	while(ra && rb) { 
		if(ra->type!=RO_AS) { 
			ra=ra->next;
			if(ra) rb=ra->next;
			continue;
		};
		if(rb->type!=RO_AS) { 
			rb=rb->next;
			continue;
		};

		if(atoi(ra->text+2)>atoi(rb->text+2)) { 
			char* text=rb->text;
			rb->text=ra->text;
			ra->text=text;
			changed++;
		};
		ra=ra->next;
		if(ra) rb=ra->next;
	}; 
	if(changed) goto nextstep;
	return 1;
};


int
print_oaspath(struct ro* head, FILE* f, char* name, int headers, int number)
{ 
	struct ro* ra=head;
	int nc=0;

	as_path_sort(head);

	if(headers) 
		fprintf(f,"no ip as-path access-list %s\n",name?name:"NN");

	/* is the 'starting as' is in list ? */
	while(ra) { 
		if(atoi(ra->text+2)==number) {
			fprintf(f,"ip as-path access-list %s permit ^(_%i)*$\n",
				name?name:"NN",number);
			break;
		};
		ra=ra->next;
	};
	
	ra=head;

	while(ra) { 
		if(ra->type==RO_AS) { 
			if(atoi(ra->text+2)==number) goto noprint;
			if(!nc) { 
				fprintf(f,"ip as-path access-list %s permit ^(_[0-9]+)*_(%s",
					name?name:"NN",ra->text+2);
			} else { 
				fprintf(f,"|%s",ra->text+2);
			};
			nc++;
			if(nc==5) { 
				fprintf(f,")+$\n");
				nc=0;
			};
		};
noprint:
		ra=ra->next;
	};
	if(nc) 
		fprintf(f,")+$\n");
	return 1;
};

