Revision 296 (by ahitrov, 2013/03/26 17:59:01) Promosite (anthill) project source
package IIR_Parser;
use strict;
use locale;

use vars qw ($VERSION); $VERSION=0.1; 

#use LogPool;

my %default_params = ( 
	'field_name_length' => 132 ,
	'debug'		    => 0,
	'save_path'	    => undef,
	'err'		    => undef,

);
 

#####################          СЧИТЫВАНИЕ СЛЕДУЮЩЕГО СИМВОЛА
sub getchar { 
  my $self = shift;
  $self -> {_CHAR} = getc ($self->{'_FH'}) ; 
##  $self -> {err}->handle('debug', "Got char =$self->{_CHAR}=\n");
  return $self -> {_CHAR} ;
}
######################		КОНСТРУКТОР

sub new { 
 my ($class,  %params ) = @_;
 my $self = { };

 foreach ( keys %default_params ) { 
    $self->{ $_} = exists $params {$_}  ? $params {$_} : $default_params {$_} ;
 }

 bless ($self,$class);

 my $input_path   = $params{'input_path'  };
 
 if( defined $input_path ) {                         # Откроем файл
     ( -d $input_path ) 			           || $self -> {err}->handle('fatal', "IIR ($input_path) : is not a directory\n");
     my $input_file_name = $input_path."/.IIR";
     $self->{_FH} = IO::File -> new ("< $input_file_name") || $self -> {err}->handle('fatal', "Unable to open $input_file_name : $! \n" );

  } else {					     # Приготовимся читать из STDIN  
     $self->{_FH} = \*STDIN;

  }

  my $char  = $self->getchar ;		             #   Прочитаем и запомним первый символ
  defined ( $char ) 					   || $self -> {err}->handle('syntax',"Message is empty: cannot read first char\n");
 
  $self -> ClearSpaces; 		             # Пропустим пробелы в начале
  return $self;
}

########################  ПРОПУСК ПРОБЕЛОВ
sub ClearSpaces { 
  my $self = shift;
  my $char = $self -> {_CHAR} ; 
  while ( defined($char) && $char =~ /[\s\r\n]/ ) { 
     $char = $self->getchar ;  
  }
}

########################## ЧТЕНИЕ ИМЕНИ ПОЛЯ 
 
sub GetFieldName { 			# Прочитаем имя поля IIR
  my $self = shift;
  $self->{debug} && $self->{err}->handle('debug',"GetFieldName\n");

  my $char = $self -> {'_CHAR'} ; 
  my $ret  = "";
  while ($char =~ /\w/) { 		# Имя содержит только алфавитно-цифровые символы и подчеркивание
        (length($ret) > $self -> {'field_name_length'} ) && $self->{err}->handle('syntax', "Field too long : ".length($ret)."\n");
	$ret .=  $char; 
        $char =  $self->getchar;
        defined($char)   				 || $self->{err}->handle('syntax', "File ended while scanning for field name \n" );
  }

  defined($char)                                         || $self->{err}->handle('syntax', "File ended between field name and expected colon\n");
  ( $char ne ':' ) 					 && $self->{err}->handle('syntax', "Field name ".$ret.$char." does not end with colon\n");
					# После имени должно идти двоеточие
  $char =  $self->getchar;
  return $self -> {'_FIELD_NAME'}       
	 = $ret ;       		# Запомним название поля 
}

########################## ЧТЕНИЕ ЗНАЧЕНИЯ ПОЛЯ

sub GetFieldValue { 
  my ($self, %args) = @_;              
  $self->{debug} && $self->{err}->handle('debug',"GetFieldValue $self->{_FIELD_NAME}\n");

  my $char = $self -> {'_CHAR'} ;
  my $mode = $args {'mode'};
  my $name = $args {'name'};

  my $save   = ($mode =~ /save/  );	      # Режим записи   значений полей в отдельные файлы
  my $return = ($mode =~ /return/);	      # Режим возврата значений полей
  my $check  = ($mode =~ /check/);	      # Режим возврата значений полей
  my $ret  = "";
  my $firstline = 1;
  my $line_begin="";
  my $type;
  my $binary=0;

  my $save_file = undef;
  if ($save) { 				      # Откроем файл для сохранения значения поля, если это надо.
      defined ($self -> {'save_path'} ) 		 || $self->{err}->handle('developer', "GetFieldValue with save option called without a defined save_path\n");
      my $save_file_name;
      my $instance_number;
      do {				      # На случай множественных значений полей - если такой файл уже есть, припишем к его названию номер 
	$save_file_name = "$self->{save_path}/.$self->{_FIELD_NAME}$instance_number";
        $instance_number++;
      } while  -f $save_file_name  ;
      $save_file = MP::WriteFile -> open ("$save_file_name")  || $self->{err}->handle('fatal'    , "Cannot open >$save_file_name\n" );     
      $ret = $save_file;		      #  Возвращаемым значением будет сохраненный файл (объект класса MP::WriteFile)
  }

  return "" unless defined ($char) ; 	      # Если тут EOF, делать больше нечего - вернем пустую строку

  while ( $char =~/[ \t]/ ) { 	 	      # Пропустим пробелы вначале (но не переводы строки!)
	$char =  $self->getchar;
	return "" unless defined ($char); 
  }
  if ($char eq "\n") {  	      	     # Если в первой  же строке пусто, 
     $char =  $self->getchar;		     # Посмотрим, что дальше
     return $ret if($char ne ' ');	     # Если там непробел -- это уже начало следующего поля, а данное - пустое. Возвращаемся.
     $char =  $self->getchar;   
  }
  
  while (1) { 
	unless (defined ($char)) {    # EOF: Заканчиваем читать.
		$save_file->close if $save;
		return $ret; 
 	}
	$ret  = 1		  if $check;
        $ret .=            $char  if $return; # Припишем новый символ к значению поля

	if ( $save  && !($binary && $line_begin eq '' && $char eq ' ' ) ) {   
	        $save_file->print($char)  			 || $self ->{err}->handle('fatal',"Cannot write to $save_file->{path}\n")
	}				      # Запишем  новый символ в файл данного поля
					      # в двоичном поле если вдруг в начале строки окажется пробел, не пишем его
	$line_begin .=     $char  if $char ne "\n" && ($binary || $firstline) && length($line_begin) < 8;
					      # Начало   строки понадобится для определения типа поля и конца двоичного поля
	
	$char =  $self->getchar;	

        unless (defined ($char)) {    # EOF: Заканчиваем читать.
                $save_file->close if $save;
                return $ret; 
        }
        
	# Нас интересует тип поля. Если первая строчка начинается с  begin, то это
	# двоичный тип (uuencode), в противном случае - текст. В конце разбора первой строки мы
	# уже должны знать тип, чтобы правильно разобрать следующую.
	if ($char eq "\n" && $firstline ) {  # Поэтому в конце первой строки определяем тип 
		$firstline = 0;
		$binary = ($line_begin =~ /^begin\s/ ); 
	}
	# Во всех строках, кроме первой определяем, является ли данная строка продолжением 
	# поля или уже нет
	if ($char eq "\n" ) {  		      # Строка закончлась  
		if ($binary) {      	      # Двоичное поле кончается словом end
			if ($line_begin =~ /^end/ ) { 
				do {  	      # После end пропустим все до первой буквы (названия следующего поля)
				$char = $self->getchar;
				} until !defined($char) || $char =~/\w/; 
                                $save_file->print("\n") if $save;
		                $save_file->close       if $save;
				return $ret; 
			} else { 
				$line_begin ="";
				next; 
			}
		} else  {      		      # Для текстового поля признаком продолжения является пробел в начале строки
		       my $nextchar = $self->getchar;
		       if ( $nextchar && $nextchar eq ' ' ) {  
				$char = "\n";
				next;
		       } else { 
				$save_file->close if $save;
				while ( defined $nextchar && $nextchar eq "\n" ) { $nextchar =$self->getchar ; }     # Пропустим пустые строки, если они там есть 
				return $ret;
		       }
		} 			
	}
  }  
}

############# Сброс остатка входного потока в файл
sub DumpTail { 
  my ($self,$file) = @_;
  my $char = $self->{'_CHAR'};
  while(defined($char)) { 
     (print {$file} $char )         || return undef;
     $char = $self->getchar;
  } 
  return 1;
}


###########  Проверка конца входного потока
sub eof {  
  my $self = shift;
  ! defined $self->{'_CHAR'}
}
  
      
  
1;