1 |
296 |
ahitrov |
package IIR_Parser; |
2 |
|
|
use strict; |
3 |
|
|
use locale; |
4 |
|
|
|
5 |
|
|
use vars qw ($VERSION); $VERSION=0.1; |
6 |
|
|
|
7 |
|
|
#use LogPool; |
8 |
|
|
|
9 |
|
|
my %default_params = ( |
10 |
|
|
'field_name_length' => 132 , |
11 |
|
|
'debug' => 0, |
12 |
|
|
'save_path' => undef, |
13 |
|
|
'err' => undef, |
14 |
|
|
|
15 |
|
|
); |
16 |
|
|
|
17 |
|
|
|
18 |
|
|
##################### СЧИТЫВАНИЕ СЛЕДУЮЩЕГО СИМВОЛА |
19 |
|
|
sub getchar { |
20 |
|
|
my $self = shift; |
21 |
|
|
$self -> {_CHAR} = getc ($self->{'_FH'}) ; |
22 |
|
|
## $self -> {err}->handle('debug', "Got char =$self->{_CHAR}=\n"); |
23 |
|
|
return $self -> {_CHAR} ; |
24 |
|
|
} |
25 |
|
|
###################### КОНСТРУКТОР |
26 |
|
|
|
27 |
|
|
sub new { |
28 |
|
|
my ($class, %params ) = @_; |
29 |
|
|
my $self = { }; |
30 |
|
|
|
31 |
|
|
foreach ( keys %default_params ) { |
32 |
|
|
$self->{ $_} = exists $params {$_} ? $params {$_} : $default_params {$_} ; |
33 |
|
|
} |
34 |
|
|
|
35 |
|
|
bless ($self,$class); |
36 |
|
|
|
37 |
|
|
my $input_path = $params{'input_path' }; |
38 |
|
|
|
39 |
|
|
if( defined $input_path ) { # Откроем файл |
40 |
|
|
( -d $input_path ) || $self -> {err}->handle('fatal', "IIR ($input_path) : is not a directory\n"); |
41 |
|
|
my $input_file_name = $input_path."/.IIR"; |
42 |
|
|
$self->{_FH} = IO::File -> new ("< $input_file_name") || $self -> {err}->handle('fatal', "Unable to open $input_file_name : $! \n" ); |
43 |
|
|
|
44 |
|
|
} else { # Приготовимся читать из STDIN |
45 |
|
|
$self->{_FH} = \*STDIN; |
46 |
|
|
|
47 |
|
|
} |
48 |
|
|
|
49 |
|
|
my $char = $self->getchar ; # Прочитаем и запомним первый символ |
50 |
|
|
defined ( $char ) || $self -> {err}->handle('syntax',"Message is empty: cannot read first char\n"); |
51 |
|
|
|
52 |
|
|
$self -> ClearSpaces; # Пропустим пробелы в начале |
53 |
|
|
return $self; |
54 |
|
|
} |
55 |
|
|
|
56 |
|
|
######################## ПРОПУСК ПРОБЕЛОВ |
57 |
|
|
sub ClearSpaces { |
58 |
|
|
my $self = shift; |
59 |
|
|
my $char = $self -> {_CHAR} ; |
60 |
|
|
while ( defined($char) && $char =~ /[\s\r\n]/ ) { |
61 |
|
|
$char = $self->getchar ; |
62 |
|
|
} |
63 |
|
|
} |
64 |
|
|
|
65 |
|
|
########################## ЧТЕНИЕ ИМЕНИ ПОЛЯ |
66 |
|
|
|
67 |
|
|
sub GetFieldName { # Прочитаем имя поля IIR |
68 |
|
|
my $self = shift; |
69 |
|
|
$self->{debug} && $self->{err}->handle('debug',"GetFieldName\n"); |
70 |
|
|
|
71 |
|
|
my $char = $self -> {'_CHAR'} ; |
72 |
|
|
my $ret = ""; |
73 |
|
|
while ($char =~ /\w/) { # Имя содержит только алфавитно-цифровые символы и подчеркивание |
74 |
|
|
(length($ret) > $self -> {'field_name_length'} ) && $self->{err}->handle('syntax', "Field too long : ".length($ret)."\n"); |
75 |
|
|
$ret .= $char; |
76 |
|
|
$char = $self->getchar; |
77 |
|
|
defined($char) || $self->{err}->handle('syntax', "File ended while scanning for field name \n" ); |
78 |
|
|
} |
79 |
|
|
|
80 |
|
|
defined($char) || $self->{err}->handle('syntax', "File ended between field name and expected colon\n"); |
81 |
|
|
( $char ne ':' ) && $self->{err}->handle('syntax', "Field name ".$ret.$char." does not end with colon\n"); |
82 |
|
|
# После имени должно идти двоеточие |
83 |
|
|
$char = $self->getchar; |
84 |
|
|
return $self -> {'_FIELD_NAME'} |
85 |
|
|
= $ret ; # Запомним название поля |
86 |
|
|
} |
87 |
|
|
|
88 |
|
|
########################## ЧТЕНИЕ ЗНАЧЕНИЯ ПОЛЯ |
89 |
|
|
|
90 |
|
|
sub GetFieldValue { |
91 |
|
|
my ($self, %args) = @_; |
92 |
|
|
$self->{debug} && $self->{err}->handle('debug',"GetFieldValue $self->{_FIELD_NAME}\n"); |
93 |
|
|
|
94 |
|
|
my $char = $self -> {'_CHAR'} ; |
95 |
|
|
my $mode = $args {'mode'}; |
96 |
|
|
my $name = $args {'name'}; |
97 |
|
|
|
98 |
|
|
my $save = ($mode =~ /save/ ); # Режим записи значений полей в отдельные файлы |
99 |
|
|
my $return = ($mode =~ /return/); # Режим возврата значений полей |
100 |
|
|
my $check = ($mode =~ /check/); # Режим возврата значений полей |
101 |
|
|
my $ret = ""; |
102 |
|
|
my $firstline = 1; |
103 |
|
|
my $line_begin=""; |
104 |
|
|
my $type; |
105 |
|
|
my $binary=0; |
106 |
|
|
|
107 |
|
|
my $save_file = undef; |
108 |
|
|
if ($save) { # Откроем файл для сохранения значения поля, если это надо. |
109 |
|
|
defined ($self -> {'save_path'} ) || $self->{err}->handle('developer', "GetFieldValue with save option called without a defined save_path\n"); |
110 |
|
|
my $save_file_name; |
111 |
|
|
my $instance_number; |
112 |
|
|
do { # На случай множественных значений полей - если такой файл уже есть, припишем к его названию номер |
113 |
|
|
$save_file_name = "$self->{save_path}/.$self->{_FIELD_NAME}$instance_number"; |
114 |
|
|
$instance_number++; |
115 |
|
|
} while -f $save_file_name ; |
116 |
|
|
$save_file = MP::WriteFile -> open ("$save_file_name") || $self->{err}->handle('fatal' , "Cannot open >$save_file_name\n" ); |
117 |
|
|
$ret = $save_file; # Возвращаемым значением будет сохраненный файл (объект класса MP::WriteFile) |
118 |
|
|
} |
119 |
|
|
|
120 |
|
|
return "" unless defined ($char) ; # Если тут EOF, делать больше нечего - вернем пустую строку |
121 |
|
|
|
122 |
|
|
while ( $char =~/[ \t]/ ) { # Пропустим пробелы вначале (но не переводы строки!) |
123 |
|
|
$char = $self->getchar; |
124 |
|
|
return "" unless defined ($char); |
125 |
|
|
} |
126 |
|
|
if ($char eq "\n") { # Если в первой же строке пусто, |
127 |
|
|
$char = $self->getchar; # Посмотрим, что дальше |
128 |
|
|
return $ret if($char ne ' '); # Если там непробел -- это уже начало следующего поля, а данное - пустое. Возвращаемся. |
129 |
|
|
$char = $self->getchar; |
130 |
|
|
} |
131 |
|
|
|
132 |
|
|
while (1) { |
133 |
|
|
unless (defined ($char)) { # EOF: Заканчиваем читать. |
134 |
|
|
$save_file->close if $save; |
135 |
|
|
return $ret; |
136 |
|
|
} |
137 |
|
|
$ret = 1 if $check; |
138 |
|
|
$ret .= $char if $return; # Припишем новый символ к значению поля |
139 |
|
|
|
140 |
|
|
if ( $save && !($binary && $line_begin eq '' && $char eq ' ' ) ) { |
141 |
|
|
$save_file->print($char) || $self ->{err}->handle('fatal',"Cannot write to $save_file->{path}\n") |
142 |
|
|
} # Запишем новый символ в файл данного поля |
143 |
|
|
# в двоичном поле если вдруг в начале строки окажется пробел, не пишем его |
144 |
|
|
$line_begin .= $char if $char ne "\n" && ($binary || $firstline) && length($line_begin) < 8; |
145 |
|
|
# Начало строки понадобится для определения типа поля и конца двоичного поля |
146 |
|
|
|
147 |
|
|
$char = $self->getchar; |
148 |
|
|
|
149 |
|
|
unless (defined ($char)) { # EOF: Заканчиваем читать. |
150 |
|
|
$save_file->close if $save; |
151 |
|
|
return $ret; |
152 |
|
|
} |
153 |
|
|
|
154 |
|
|
# Нас интересует тип поля. Если первая строчка начинается с begin, то это |
155 |
|
|
# двоичный тип (uuencode), в противном случае - текст. В конце разбора первой строки мы |
156 |
|
|
# уже должны знать тип, чтобы правильно разобрать следующую. |
157 |
|
|
if ($char eq "\n" && $firstline ) { # Поэтому в конце первой строки определяем тип |
158 |
|
|
$firstline = 0; |
159 |
|
|
$binary = ($line_begin =~ /^begin\s/ ); |
160 |
|
|
} |
161 |
|
|
# Во всех строках, кроме первой определяем, является ли данная строка продолжением |
162 |
|
|
# поля или уже нет |
163 |
|
|
if ($char eq "\n" ) { # Строка закончлась |
164 |
|
|
if ($binary) { # Двоичное поле кончается словом end |
165 |
|
|
if ($line_begin =~ /^end/ ) { |
166 |
|
|
do { # После end пропустим все до первой буквы (названия следующего поля) |
167 |
|
|
$char = $self->getchar; |
168 |
|
|
} until !defined($char) || $char =~/\w/; |
169 |
|
|
$save_file->print("\n") if $save; |
170 |
|
|
$save_file->close if $save; |
171 |
|
|
return $ret; |
172 |
|
|
} else { |
173 |
|
|
$line_begin =""; |
174 |
|
|
next; |
175 |
|
|
} |
176 |
|
|
} else { # Для текстового поля признаком продолжения является пробел в начале строки |
177 |
|
|
my $nextchar = $self->getchar; |
178 |
|
|
if ( $nextchar && $nextchar eq ' ' ) { |
179 |
|
|
$char = "\n"; |
180 |
|
|
next; |
181 |
|
|
} else { |
182 |
|
|
$save_file->close if $save; |
183 |
|
|
while ( defined $nextchar && $nextchar eq "\n" ) { $nextchar =$self->getchar ; } # Пропустим пустые строки, если они там есть |
184 |
|
|
return $ret; |
185 |
|
|
} |
186 |
|
|
} |
187 |
|
|
} |
188 |
|
|
} |
189 |
|
|
} |
190 |
|
|
|
191 |
|
|
############# Сброс остатка входного потока в файл |
192 |
|
|
sub DumpTail { |
193 |
|
|
my ($self,$file) = @_; |
194 |
|
|
my $char = $self->{'_CHAR'}; |
195 |
|
|
while(defined($char)) { |
196 |
|
|
(print {$file} $char ) || return undef; |
197 |
|
|
$char = $self->getchar; |
198 |
|
|
} |
199 |
|
|
return 1; |
200 |
|
|
} |
201 |
|
|
|
202 |
|
|
|
203 |
|
|
########### Проверка конца входного потока |
204 |
|
|
sub eof { |
205 |
|
|
my $self = shift; |
206 |
|
|
! defined $self->{'_CHAR'} |
207 |
|
|
} |
208 |
|
|
|
209 |
|
|
|
210 |
|
|
|
211 |
|
|
1; |
212 |
|
|
|