#include <stdarg.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include "structfill.h"

TableEntry::TableEntry(const char *ostr,Tags thetype,unsigned int off,
		       TableEntry *nxt):oidstr(ostr),type(thetype),
  offset(off),next(nxt){
  assert(type==INT_TAG || type==TIME_TICK_TAG || type==OID_TAG || 
	 type==STRING_TAG || type==IPADDR_TAG || type==COUNTER_TAG);
}

void SNMP_structFiller::append(const char *oidstr,Tags tag,
			       unsigned int offset){
  assert(oidstr);
  assert(tabdef=new TableEntry(oidstr,tag,offset,tabdef));
  if(oidseq)
    oidseq->append(oidstr);
  else
    oidseq=new OidSeq(1,oidstr);
}

void SNMP_structFiller::remove(const char *oidstr){
  //delete the TableEntry
  assert(tabdef); /* if this fails you are trying to delete 
		     something from an empty structfiller. */
  if(*tabdef==oidstr){
    TableEntry *nextone=tabdef->next;
    delete tabdef;
    tabdef=nextone;
  } else { // search for it amongst the rest of the entries
    char test=0;
    for(TableEntry *cur=tabdef;cur!=NULL;cur=cur->next){
      if(*cur->next==oidstr){
	TableEntry *doomed=cur->next;
	cur->next=doomed->next;
	delete doomed;
	test=1;
	break;
      }
    }
    assert(test); /* if this fails you are trying to delete 
		     something from a structfiller that doesn't 
		     exist there. */
  }

  //delete it off of the oidseq
  assert(oidseq); /* this should never fail. It means that there is 
		     something seriously wrong. the oidseq is out 
		     of sync with the table */
  oidseq->remove(oidstr);
}

SNMP_structFiller::~SNMP_structFiller(){
  if(oidseq) delete oidseq;
  if(retseq) delete retseq;
  while(tabdef){
    TableEntry *tmp=tabdef->next;
    delete tabdef;
    tabdef=tmp;
  }
}

int SNMP_structFiller::fillStruct(OidSeq *data,
				  unsigned char *curstruct){
  int retval=1;
  for(TableEntry *cur=tabdef;cur!=NULL;cur=cur->next){
    BerBase *curber;
    if(!(curber=data->child(cur->oidstr))){
      retval=0;
      continue;
    }

    if(curber->type()!=cur->type){
      if(curber->type()==INT_TAG && cur->type==COUNTER_TAG)
	fprintf(stderr,"Warning: Counter returned when Integer expected for "
		"%s.Buggy firmware?\n",cur->oidstr);
      else if(curber->type()==COUNTER_TAG && cur->type==INT_TAG)
	fprintf(stderr,"Warning: Integer returned when Counter expected for "
		"%s. Buggy firmware?\n",cur->oidstr);
      else{
	fprintf(stderr,"Warning: Printer returned a value of type 0x%x when a "
		"value of 0x%x was expected for %s. Buggy firmware?"
		"\n",curber->type(),cur->type,cur->oidstr);
	throw SNMP_error_oid(cur->oidstr);
      }
    }
    
    unsigned char *inside=curstruct+cur->offset;
    switch(cur->type){
    case INT_TAG:
    case COUNTER_TAG:
      *((long*)inside)=((BerInt*)curber)->value();
      break;
    case TIME_TICK_TAG:
      *((unsigned long*)inside)=((BerTimeTick*)curber)->value();
      break;
    case IPADDR_TAG:
      assert(((BerIPAddr*)curber)->Len()==4);
      assert(*((unsigned char**)inside)=new unsigned char[4]);
      memcpy(*((unsigned char**)inside),((BerIPAddr*)curber)->IPaddr(),4);
      break;
    case OID_TAG:
    case STRING_TAG:
      assert(*((char**)inside)=new char[256]);
      int len=((BerString*)curber)->copy(*((char**)inside),256);
      (*(char**)inside)[len]=0;
      *(char**)inside=(char*)realloc(*(char**)inside,len+1);
      break;
    }
  }
  return retval;
}

void *SNMP_structFiller::get(void *tobefilled){
  retseq=session.get(oidseq);
  if(retseq==NULL)
    throw SNMPERR_NoResponse;

  if( !fillStruct(retseq,(unsigned char*)tobefilled)){
    fprintf(stderr,"Warning: printer did not respond with a value for one of the OIDs. Buggy firmware?\n");
    return NULL;
  }

  return tobefilled;
}

void *SNMP_structFiller::get_next(void *tobefilled){
  if(retseq){
    delete oidseq;
    oidseq=retseq;
    retseq=NULL;
  }
  retseq=session.get_next(oidseq);
  if(retseq==NULL)
    throw SNMPERR_NoResponse;

  if(!fillStruct(retseq,(unsigned char*)tobefilled))
    return NULL;

  return tobefilled;
}

void *SNMP_table::get(unsigned int &idx){
  idx=0; // sort of a second return value
  unsigned int maxlen=10;
  void *retval;

  assert(retval=malloc(structlen*maxlen));
  /* this isn't strictly necessary but it does make debugging 
     easier notice I don't do it below when the array is resized.
   */
  memset(retval,0,structlen*maxlen); 

  // while we are still in the table
  while(get_next(((unsigned char*)retval)+idx*structlen)){ 
    idx++;
    if(idx==maxlen){ //this could be really slow with big structures.
      maxlen+=10;
      assert(retval=realloc(retval,structlen*maxlen));
    }
  }

  assert(retval=realloc(retval,structlen*(idx+1)));
  return retval;
}





syntax highlighted by Code2HTML, v. 0.9.1