1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2000-2020. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21%%----------------------------------------------------------------------
22%% Purpose : Scanner for text encoded Megaco/H.248 messages
23%%----------------------------------------------------------------------
24
25-module('megaco_text_scanner').
26
27-export([scan/1, skip_sep_chars/2]).
28
29-include_lib("megaco/include/megaco.hrl").
30-include_lib("megaco/src/engine/megaco_message_internal.hrl").
31-include("megaco_text_tokens.hrl").
32
33-define(LOWER1(Char),
34	if
35	    (Char >= $A) andalso (Char =< $Z) ->
36		Char - ($A - $a);
37	    true ->
38		Char
39	end).
40
41%% This is used when we _know_ it to be upper case
42-define(LOWER2(Char), Char - ($A - $a)).
43
44scan(Bin) when is_binary(Bin) ->
45    Chars = erlang:binary_to_list(Bin),
46    tokens1(Chars, 1, []);
47scan(Chars) when is_list(Chars) ->
48    tokens1(Chars, 1, []).
49
50%% As long as we dont know the version, we will loop in this function
51tokens1(Chars, Line, Acc) ->
52    case any_chars(Chars, Line, 1) of
53	{token, Token, [], LatestLine} ->
54	    %% We got to the end without actually getting a version token.
55	    Tokens = [{endOfMessage, LatestLine, endOfMessage}, Token | Acc],
56	    {error, no_version_found, lists:reverse(Tokens), Line};
57
58        %% -- Version token for version 1 --
59        {token, {'SafeChars',_,"!/1"} = Token, Rest, LatestLine} ->
60            tokens2(Rest, LatestLine, 1, [Token | Acc]);
61
62        {token, {'SafeChars',_,"megaco/1"} = Token, Rest, LatestLine} ->
63            tokens2(Rest, LatestLine, 1, [Token | Acc]);
64
65
66        %% -- Version token for version 2 --
67        {token, {'SafeChars',_,"!/2"} = Token, Rest, LatestLine} ->
68            tokens2(Rest, LatestLine, 2, [Token | Acc]);
69
70        {token, {'SafeChars',_,"megaco/2"} = Token, Rest, LatestLine} ->
71            tokens2(Rest, LatestLine, 2, [Token | Acc]);
72
73
74        %% -- Version token for version 3 --
75        {token, {'SafeChars',_,"!/3"} = Token, Rest, LatestLine} ->
76            tokens2(Rest, LatestLine, 3, [Token | Acc]);
77
78        {token, {'SafeChars',_,"megaco/3"} = Token, Rest, LatestLine} ->
79            tokens2(Rest, LatestLine, 3, [Token | Acc]);
80
81
82        %% -- Version token for version X --
83        {token, {'SafeChars',_,[$!,$/| Vstr]} = Token, Rest, LatestLine} ->
84	    case guess_version(Vstr) of
85		{ok, V} ->
86		    tokens2(Rest, LatestLine, V, [Token | Acc]);
87		{error, Reason} ->
88		    {error, Reason, LatestLine}
89	    end;
90
91        {token, {'SafeChars',_,[$m,$e,$g,$a,$c,$o,$/|Vstr]} = Token, Rest, LatestLine} ->
92	    case guess_version(Vstr) of
93		{ok, V} ->
94		    tokens2(Rest, LatestLine, V, [Token | Acc]);
95		{error, Reason} ->
96		    {error, Reason, LatestLine}
97	    end;
98
99	%% -- Other tokens --
100	{token, Token, Rest, LatestLine} ->
101	    tokens1(Rest, LatestLine, [Token | Acc]);
102
103	{bad_token, Token, _Rest, _LatestLine} ->
104	    {error, {bad_token, [Token, Acc]}, Line}
105    end.
106
107tokens2(Chars, Line0, Version, Tokens0) ->
108    case tokens3(Chars, Line0, Tokens0, Version) of
109	{ok, Tokens, Line} ->
110	    {ok, Tokens, Version, Line};
111	Error ->
112	    Error
113    end.
114
115tokens3(Chars, Line, Acc, Version) ->
116    %%     d("tokens2 -> entry with"
117    %%       "~n   Chars: ~s"
118    %%       "~n   Line:  ~p", [Chars, Line]),
119    case any_chars(Chars, Line, Version) of
120	{token, Token, [], LatestLine} ->
121	    %%  	    d("tokens2 -> Token: ~n~p", [Token]),
122	    Tokens = [{endOfMessage, LatestLine, endOfMessage}, Token | Acc],
123	    {ok, lists:reverse(Tokens), Line};
124
125	{token, Token, Rest, LatestLine} ->
126	    %%  	    d("tokens2 -> Token: ~n~p", [Token]),
127	    tokens3(Rest, LatestLine, [Token | Acc], Version);
128
129	{bad_token, Token, _Rest, _LatestLine} ->
130	    {error, {bad_token, [Token, Acc]}, Line}
131    end.
132
133
134guess_version([C]) when (48 =< C) and (C =< 57) ->
135    {ok, C-48};
136guess_version(Str) when is_list(Str) ->
137    case (catch list_to_integer(Str)) of
138	I when is_integer(I) ->
139	    {ok, I};
140	_ ->
141	    {error, {invalid_version, Str}}
142    end.
143
144
145%% Returns {token,     Token, Rest, LatestLine}
146%% Returns {bad_token, Token, Rest, LatestLine}
147any_chars([Char | Rest], Line, Version) ->
148    case ?classify_char(Char) of
149	safe_char_upper ->
150	    safe_chars(Rest, [Char], [?LOWER2(Char)], Line, Version);
151	safe_char ->
152	    safe_chars(Rest, [Char], [Char], Line, Version);
153	rest_char ->
154	    case Char of
155		?SemiColonToken ->
156		    comment_chars(Rest, Line);
157		_ ->
158		    rest_chars(Rest, [Char], Line)
159	    end;
160	double_quote ->
161	    quoted_chars(Rest, [], Line);
162	white_space ->
163	    sep_chars(Rest, Line);
164	end_of_line ->
165	    sep_chars(Rest, Line);
166	bad_char ->
167	    %% {bad_token, {'SEP', Line, Char}, Rest, Line}
168	    {bad_token, {'AnyChars', Line, Char}, Rest, Line}
169    end;
170any_chars([] = All, Line, _Version) ->
171    {token, {'SEP', Line, end_of_input}, All, Line}.
172
173comment_chars([Char | Rest], Line) ->
174    case ?classify_char(Char) of
175	safe_char_upper ->
176	    comment_chars(Rest, Line);
177	safe_char ->
178	    comment_chars(Rest, Line);
179	rest_char ->
180	    comment_chars(Rest, Line);
181	white_space ->
182	    comment_chars(Rest, Line);
183	end_of_line ->
184	    sep_chars(Rest, Line);
185	_ when Char =:= 22 ->
186	    comment_chars(Rest, Line);
187	_ ->
188	    %% {bad_token, {'SEP', Line, Char}, Rest, Line}
189	    {bad_token, {'CommentChars', Line, Char}, Rest, Line}
190    end;
191comment_chars([] = All, Line) ->
192    {token, {'SEP', Line}, All, Line}.
193
194sep_chars([Char | Rest] = All, Line) ->
195    case ?classify_char(Char) of
196	safe_char_upper ->
197	    {token, {'SEP', Line}, All, Line};
198	safe_char ->
199	    {token, {'SEP', Line}, All, Line};
200	rest_char when Char =:= ?SemiColonToken ->
201	    comment_chars(Rest, Line);
202	rest_char ->
203	    rest_chars(Rest, [Char], Line);
204	white_space ->
205	    sep_chars(Rest, Line);
206	end_of_line ->
207	    sep_chars(Rest, Line + 1);
208	_ ->
209	    %% {bad_token, {'SEP', Line, Char}, Rest, Line}
210	    {bad_token, {'SepChars', Line, Char}, Rest, Line}
211    end;
212sep_chars([] = All, Line) ->
213    {token, {'SEP', Line}, All, Line}.
214
215rest_chars(Rest, [?ColonToken], Line) ->
216    {token, {'COLON',   Line}, Rest, Line};
217rest_chars(Rest, [AccChar], Line) ->
218    TokenTag =
219	case AccChar of
220	    ?EqualToken   -> 'EQUAL';
221	    ?NequalToken  -> 'NEQUAL';
222	    ?LesserToken  -> 'LESSER';
223	    ?GreaterToken -> 'GREATER';
224	    ?LbrktToken   -> 'LBRKT';
225	    ?RbrktToken   -> 'RBRKT';
226	    ?LsbrktToken  -> 'LSBRKT';
227	    ?RsbrktToken  -> 'RSBRKT';
228	    ?LparToken    -> 'LPAR';
229	    ?RparToken    -> 'RPAR';
230	    ?VbarToken    -> 'VBAR';
231	    ?CommaToken   -> 'COMMA'
232	end,
233    {Rest2, Line2} = skip_sep_chars(Rest, Line),
234    {token, {TokenTag, Line}, Rest2, Line2}.
235
236skip_sep_chars([Char | Rest] = All, Line) ->
237    case ?classify_char2(Char) of
238	rest_char when Char =:= ?SemiColonToken ->
239	    skip_comment_chars(Rest, Line);
240	white_space ->
241	    skip_sep_chars(Rest, Line);
242	end_of_line ->
243	    skip_sep_chars(Rest, Line + 1);
244	_ ->
245	    {All, Line}
246    end;
247skip_sep_chars([] = All, Line) ->
248    {All, Line}.
249
250skip_comment_chars([Char | Rest] = All, Line) ->
251    case ?classify_char(Char) of
252	safe_char_upper ->
253	    skip_comment_chars(Rest, Line);
254	safe_char ->
255	    skip_comment_chars(Rest, Line);
256	rest_char ->
257	    skip_comment_chars(Rest, Line);
258	double_quote ->
259	    skip_comment_chars(Rest, Line);
260	white_space ->
261	    skip_comment_chars(Rest, Line);
262	end_of_line ->
263	    skip_sep_chars(Rest, Line + 1);
264	_ ->
265	    {All, Line}
266    end;
267skip_comment_chars([] = All, Line) ->
268    {All, Line}.
269
270quoted_chars([Char | Rest], Acc, Line) ->
271    case ?classify_char(Char) of
272	safe_char_upper ->
273	    quoted_chars(Rest, [Char | Acc], Line);
274	safe_char ->
275	    quoted_chars(Rest, [Char | Acc], Line);
276	rest_char ->
277	    quoted_chars(Rest, [Char | Acc], Line);
278	white_space ->
279	    quoted_chars(Rest, [Char | Acc], Line);
280	double_quote ->
281	    {token, {'QuotedChars', Line, lists:reverse(Acc)}, Rest, Line};
282	_ ->
283	    {bad_token, {'QuotedChars', Line, Char}, Rest, Line}
284    end;
285quoted_chars([] = All, _Acc, Line) ->
286    {bad_token, {'QuotedChars', Line, end_of_input}, All, Line}.
287
288safe_chars([Char | Rest] = All, Acc, LowerAcc, Line, Version) ->
289    %%     d("safe_chars -> entry with"
290    %%       "~n   Char:     ~p"
291    %%       "~n   LowerAcc: ~p", [Char, LowerAcc]),
292    case ?classify_char3(Char) of
293	safe_char_upper ->
294	    safe_chars(Rest, [Char | Acc],
295		       [?LOWER2(Char) | LowerAcc], Line, Version);
296	safe_char ->
297	    safe_chars(Rest, [Char | Acc], [Char | LowerAcc], Line, Version);
298	_ ->
299	    LowerSafeChars = lists:reverse(LowerAcc),
300	    TokenTag = select_token(LowerSafeChars, Version),
301	    SafeChars = lists:reverse(Acc),
302	    case TokenTag of
303		'MtpToken' ->
304		    %% 'MtpToken' 'LBRKT' OctetString 'RBRKT'
305		    special_chars(All, LowerSafeChars, Line, TokenTag);
306		'LocalToken' ->
307		    %% 'LocalToken' 'LBRKT' OctetString 'RBRKT'
308		    special_chars(All, SafeChars, Line, TokenTag);
309		'RemoteToken' ->
310		    %% 'RemoteToken' 'LBRKT' OctetString 'RBRKT'
311		    special_chars(All, SafeChars, Line, TokenTag);
312		'DigitMapToken' ->
313		    %% 'DigitMapToken'
314		    %% 'DigitMapToken' 'EQUAL' Name
315		    %% 'DigitMapToken' 'EQUAL' Name 'LBRKT' Value 'RBRKT'
316		    %% 'DigitMapToken' 'EQUAL' 'LBRKT' Value 'RBRKT'
317		    %% 'DigitMapToken' 'LBRKT' Value 'RBRKT'
318		    special_chars(All, LowerSafeChars, Line, TokenTag);
319		_ ->
320		    {token, {TokenTag, Line, LowerSafeChars}, All, Line}
321	    end
322    end;
323safe_chars([] = All, _Acc, LowerAcc, Line, Version) ->
324    LowerSafeChars = lists:reverse(LowerAcc),
325    TokenTag = select_token(LowerSafeChars, Version),
326    %%SafeChars = lists:reverse(Acc),
327    {token, {TokenTag, Line, LowerSafeChars}, All, Line}.
328
329collect_safe_chars([Char | Rest] = All, LowerAcc) ->
330    case ?classify_char3(Char) of
331	safe_char_upper ->
332	    collect_safe_chars(Rest, [?LOWER2(Char) | LowerAcc]);
333	safe_char ->
334	    collect_safe_chars(Rest, [Char | LowerAcc]);
335	_ ->
336	    {All, lists:reverse(LowerAcc)}
337    end;
338collect_safe_chars([] = Rest, LowerAcc) ->
339    {Rest, lists:reverse(LowerAcc)}.
340
341special_chars(All, SafeChars, Line, TokenTag) ->
342    {Rest, Line2} = skip_sep_chars(All, Line),
343    case Rest of
344	[?LbrktToken | Rest2] ->
345	    {token, {'OctetString', _, OctetString}, Rest4, Line4} =
346		octet_string(Rest2, Line2),
347	    case Rest4 of
348		[?RbrktToken | Rest6] ->
349		    Token =
350			case TokenTag of
351			    'MtpToken' ->
352				%% 'MtpToken' 'LBRKT' OctetString 'RBRKT'
353				{'MtpAddressToken', Line, OctetString};
354			    'LocalToken' ->
355				%% 'LocalToken' 'LBRKT' OctetString 'RBRKT'
356				PGs = property_groups(OctetString),
357				{'LocalDescriptorToken', Line, PGs};
358			    'RemoteToken' ->
359				%% 'RemoteToken' 'LBRKT' OctetString 'RBRKT'
360				PGs = property_groups(OctetString),
361				{'RemoteDescriptorToken', Line, PGs};
362			    'DigitMapToken' ->
363				%% 'DigitMapToken' 'LBRKT' OctetString 'RBRKT'
364				DMV = digit_map_value(OctetString),
365				DMD = #'DigitMapDescriptor'{digitMapValue = DMV},
366				{'DigitMapDescriptorToken', Line, DMD}
367			end,
368		    {token, Token, Rest6, Line4};
369		_ when TokenTag =:= 'DigitMapToken' ->
370		    %% 'DigitMapToken'
371		    {token, {'DigitMapToken', Line, SafeChars}, All, Line};
372		_ ->
373		    {token, {'SafeChars', Line, SafeChars}, All, Line}
374	    end;
375	[?EqualToken | Rest2] when TokenTag =:= 'DigitMapToken' ->
376	    {Rest3, Line3} = skip_sep_chars(Rest2, Line2),
377	    {Rest4, DigitMapName} = collect_safe_chars(Rest3, []),
378	    {Rest6, Line6, DMD} =
379		if
380		    DigitMapName =:= [] ->
381			{Rest3, Line3, #'DigitMapDescriptor'{}};
382		    true ->
383			{Rest5, Line5} = skip_sep_chars(Rest4, Line3),
384			{Rest5, Line5, #'DigitMapDescriptor'{digitMapName  = DigitMapName}}
385		end,
386	    case Rest6 of
387		[?LbrktToken | Rest7] ->
388		    {token, {'OctetString', _, OctetString}, Rest8, Line8} =
389			octet_string(Rest7, Line6),
390		    case Rest8 of
391			[?RbrktToken | Rest10] ->
392			    %% 'DigitMapToken' 'EQUAL' 'LBRKT' OctetString 'RBRKT'
393			    %% 'DigitMapToken' 'EQUAL' Name 'LBRKT' OctetString 'RBRKT'
394			    {Rest11, Line11} = skip_sep_chars(Rest10, Line8),
395			    DMV = digit_map_value(OctetString),
396			    DMD2 = DMD#'DigitMapDescriptor'{digitMapValue = DMV},
397			    {token, {'DigitMapDescriptorToken', Line, DMD2}, Rest11, Line11};
398			_ when DMD#'DigitMapDescriptor'.digitMapName /= asn1_NOVALUE ->
399			    %% 'DigitMapToken' 'EQUAL' Name
400			    {token, {'DigitMapDescriptorToken', Line, DMD}, Rest4, Line3};
401			_ ->
402			    %% 'DigitMapToken'
403			    {token, {'DigitMapToken', Line, SafeChars}, All, Line}
404		    end;
405		_ when DMD#'DigitMapDescriptor'.digitMapName /= asn1_NOVALUE ->
406		    %% 'DigitMapToken' 'EQUAL' Name
407		    {token, {'DigitMapDescriptorToken', Line, DMD}, Rest4, Line3};
408		_ ->
409		    %% 'DigitMapToken'
410		    {token, {'DigitMapToken', Line, SafeChars}, All, Line}
411	    end;
412	_  when TokenTag =:= 'DigitMapToken' ->
413	    %% 'DigitMapToken'
414	    {token, {'DigitMapToken', Line, SafeChars}, All, Line};
415	_ ->
416	    %% 'DigitMapToken'
417	    {token, {'SafeChars', Line, SafeChars}, All, Line}
418    end.
419
420octet_string(Chars, Line) ->
421    {Chars2, Line2} = skip_sep_chars(Chars, Line),
422    Acc = [],
423    {Rest, RevChars, RestLine} = octet_string(Chars2, Acc, Line2),
424    {RevChars2, _} = skip_sep_chars(RevChars, RestLine),
425    OctetString = lists:reverse(RevChars2),
426    {token, {'OctetString', Line, OctetString}, Rest, RestLine}.
427
428
429octet_string([Char | Rest] = All, Acc, Line) ->
430    if
431	(Char =:= ?CrToken) ->
432	    octet_string(Rest, [Char | Acc], Line + 1);
433	(Char =:= ?LfToken) ->
434	    octet_string(Rest, [Char | Acc], Line + 1);
435	(Char >= 8#1) andalso (Char =< 8#174) ->
436	    octet_string(Rest, [Char | Acc], Line);
437	(Char >= 8#176) andalso (Char =< 8#377) ->
438	    octet_string(Rest, [Char | Acc], Line);
439	(Char =:= ?BackslashToken) ->
440	    case Rest of
441		[?RbrktToken | _Rest2] ->
442		    %% OTP-4357
443		    octet_string(Rest,
444				 [?RbrktToken, ?BackslashToken | Acc], Line);
445		_ ->
446		    octet_string(Rest, [Char | Acc], Line)
447	    end;
448	true ->
449	    {All, Acc, Line}
450    end;
451octet_string([] = All, Acc, Line) ->
452    {All, Acc, Line}.
453
454
455%% digitMapValue      = ["T" COLON Timer COMMA]
456%%			["S" COLON Timer COMMA]
457%%			["L" COLON Timer COMMA]
458%%			["Z" COLON Timer COMMA] digitMap
459digit_map_value(Chars) ->
460    digit_map_value(Chars, #'DigitMapValue'{}).
461
462%% NOTE: The swap of the digitMapBody and the durationTimer is
463%% intentional. The reason is a problem with the flex scanner.
464%% Hopefully this is temporary...
465%% The values are swapped back later by the parser...
466digit_map_value([Char, ?ColonToken | Rest] = All, DMV) ->
467    case ?LOWER1(Char) of
468	$t -> digit_map_timer(All, Rest, #'DigitMapValue'.startTimer, DMV);
469	$s -> digit_map_timer(All, Rest, #'DigitMapValue'.shortTimer, DMV);
470	$l -> digit_map_timer(All, Rest, #'DigitMapValue'.longTimer, DMV);
471% 	$z -> digit_map_timer(All, Rest, #'DigitMapValue'.durationTimer, DMV);
472% 	_  -> DMV#'DigitMapValue'{digitMapBody = All}
473	$z -> digit_map_timer(All, Rest, #'DigitMapValue'.digitMapBody, DMV);
474	_  -> DMV#'DigitMapValue'{durationTimer = All}
475    end;
476digit_map_value(Chars, DMV) ->
477    DMV#'DigitMapValue'{durationTimer = Chars}.
478
479digit_map_timer(All, Chars, TimerPos, DMV) ->
480    {Rest, Digits} = collect_safe_chars(Chars, []),
481    {Rest2, _} = skip_sep_chars(Rest, 0),
482   case {Rest2, catch list_to_integer(Digits)} of
483       {[?CommaToken | Rest3], Int} when is_integer(Int) andalso
484                                         (Int >= 0) andalso
485					 (element(TimerPos, DMV) =:= asn1_NOVALUE) ->
486	   {Rest4, _} = skip_sep_chars(Rest3, 0),
487	   DMV2 = setelement(TimerPos, DMV, Int),
488	   digit_map_value(Rest4, DMV2);
489       _ ->
490	   DMV#'DigitMapValue'{digitMapBody = All}
491   end.
492
493%% ============================================================================
494%% <prev-parser-stuff>
495%%
496%% This stuff was originally in the parser(s), but was,
497%% for performance reasons, moved to the scanner(s). This
498%% scanner does not make it faster, but the flex scanner
499%% does, which is why the move was made.
500%%
501
502property_groups(OctetString) ->
503    Group  = [],
504    Groups = [],
505    property_name(OctetString, Group, Groups).
506
507property_name([Char | Rest] = All, Group, Groups) ->
508    if
509        ?white_space(Char) ->
510            property_name(Rest, Group, Groups);
511        ?end_of_line(Char) ->
512            property_name(Rest, Group, Groups);
513        true ->
514            Name = [],
515            do_property_name(All, Name, Group, Groups)
516    end;
517property_name([] = All, Group, Groups) ->
518    Name = [],
519    do_property_name(All, Name, Group, Groups).
520
521do_property_name([Char | Rest], Name, Group, Groups)
522  when (Char =:= $=) andalso (Name =/= []) ->
523    %% Now we have a complete name
524    if
525	(Name =:= "v") andalso (Group =/= []) ->
526	    %% v= is a property group delimiter,
527	    %% lets create yet another property group.
528	    Groups2 = [lists:reverse(Group) | Groups],
529	    Group2 = [],
530	    property_value(Rest, Name, Group2, Groups2);
531	true ->
532	    %% Use current property group
533	    property_value(Rest, Name, Group, Groups)
534    end;
535do_property_name([Char | Rest], Name, Group, Groups) ->
536    case ?classify_char4(Char) of
537        safe_char_upper ->
538            do_property_name(Rest, [Char | Name], Group, Groups);
539        safe_char ->
540            do_property_name(Rest, [Char | Name], Group, Groups);
541        _ ->
542            throw({error, {bad_prop_name, lists:reverse(Name), Char}})
543    end;
544do_property_name([], [], [], Groups) ->
545    lists:reverse(Groups);
546do_property_name([], [], Group, Groups) ->
547    Group2 = lists:reverse(Group),
548    lists:reverse([Group2 | Groups]);
549do_property_name([], Name, Group, Groups) when Name =/= [] ->
550    %% Assume end of line
551    Value  = [],
552    PP     = make_property_parm(Name, Value),
553    Group2 = lists:reverse([PP | Group]),
554    lists:reverse([Group2 | Groups]).
555
556-ifdef(megaco_scanner_inline).
557-compile({inline,[{property_value,4}]}).
558-endif.
559property_value(Chars, Name, Group, Groups) ->
560    Value = [],
561    do_property_value(Chars, Name, Value, Group, Groups).
562
563do_property_value([Char | Rest], Name, Value, Group, Groups) ->
564    if
565        ?end_of_line(Char) ->
566            %% Now we have a complete "name=value" pair
567            PP = make_property_parm(Name, Value),
568            property_name(Rest, [PP | Group], Groups);
569        true ->
570            do_property_value(Rest, Name, [Char | Value], Group, Groups)
571    end;
572do_property_value([], Name, Value, Group, Groups) ->
573    %% Assume end of line
574    PP = make_property_parm(Name, Value),
575    Group2 = lists:reverse([PP | Group]),
576    lists:reverse([Group2 | Groups]).
577
578-ifdef(megaco_scanner_inline).
579-compile({inline,[{make_property_parm,2}]}).
580-endif.
581make_property_parm(Name, Value) ->
582    %% Record name, name field, value field, extraInfo field
583    {'PropertyParm',
584     lists:reverse(Name),
585     [lists:reverse(Value)],
586     asn1_NOVALUE}.
587
588
589%% </prev-parser-stuff>
590%% ===========================================================================
591
592select_token([$o, $- | LowerText], Version) ->
593    select_token(LowerText, Version);
594select_token([$w, $- | LowerText], Version) ->
595    select_token(LowerText, Version);
596select_token(LowerText, Version) ->
597    case LowerText of
598        "add"                   -> 'AddToken';
599        "a"                     -> 'AddToken';
600        "andlgc"                when (Version >= 3) -> 'AndAUDITSelectToken'; % v3
601        "audit"                 -> 'AuditToken';
602        "at"                    -> 'AuditToken';
603        "auditcapability"       -> 'AuditCapToken';
604        "ac"                    -> 'AuditCapToken';
605        "auditvalue"            -> 'AuditValueToken';
606        "av"                    -> 'AuditValueToken';
607	"authentication"        -> 'AuthToken';
608        "au"                    -> 'AuthToken';
609        "both"                  when (Version >= 3) -> 'BothToken';         % v3
610        "b"                     when (Version >= 3) -> 'BothToken';         % v3
611        "bothway"               -> 'BothwayToken';
612        "bw"                    -> 'BothwayToken';
613        "brief"                 -> 'BriefToken';
614        "br"                    -> 'BriefToken';
615        "buffer"                -> 'BufferToken';
616        "bf"                    -> 'BufferToken';
617        "context"               -> 'CtxToken';
618        "c"                     -> 'CtxToken';
619        "contextattr"           when (Version >= 3) -> 'ContextAttrToken';  % v3
620        "ct"                    when (Version >= 3) -> 'ContextAttrToken';  % v3
621        "contextlist"           when (Version >= 3) -> 'ContextListToken';  % v3
622        "clt"                   when (Version >= 3) -> 'ContextListToken';  % v3
623        "contextaudit"          -> 'ContextAuditToken';
624        "ca"                    -> 'ContextAuditToken';
625	"digitmap"              -> 'DigitMapToken';
626	"dm"                    -> 'DigitMapToken';
627        "spadirection"          when (Version >= 3) -> 'DirectionToken';    % v3
628        "direction"             when (Version >= 3) -> 'DirectionToken';    % v3 (pre-v3a/v3b)
629        "spadi"                 when (Version >= 3) -> 'DirectionToken';    % v3
630        "di"                    when (Version >= 3) -> 'DirectionToken';    % v3 (pre-v3a/v3b)
631        "discard"               -> 'DiscardToken';
632        "ds"                    -> 'DiscardToken';
633        "disconnected"          -> 'DisconnectedToken';
634        "dc"                    -> 'DisconnectedToken';
635        "delay"                 -> 'DelayToken';
636        "dl"                    -> 'DelayToken';
637        "delete"                -> 'DeleteToken';
638        "de"                    -> 'DeleteToken';
639        "duration"              -> 'DurationToken';
640        "dr"                    -> 'DurationToken';
641        "embed"                 -> 'EmbedToken';
642        "em"                    -> 'EmbedToken';
643        "emergency"             -> 'EmergencyToken';
644        "eg"                    -> 'EmergencyToken';
645        "emergencyofftoken"     -> 'EmergencyOffToken';
646        "emergencyoff"          when (Version >= 3) -> 'EmergencyOffToken';   % v3
647        "ego"                   -> 'EmergencyOffToken';
648        "emergencyvalue"        when (Version >= 3) -> 'EmergencyValueToken'; % v3
649        "egv"                   when (Version >= 3) -> 'EmergencyValueToken'; % v3
650        "error"                 -> 'ErrorToken';
651        "er"                    -> 'ErrorToken';
652        "eventbuffer"           -> 'EventBufferToken';
653        "eb"                    -> 'EventBufferToken';
654        "events"                -> 'EventsToken';
655        "e"                     -> 'EventsToken';
656        "external"              when (Version >= 3) -> 'ExternalToken';     % v3
657        "ex"                    when (Version >= 3) -> 'ExternalToken';     % v3
658        "failover"              -> 'FailoverToken';
659        "fl"                    -> 'FailoverToken';
660        "forced"                -> 'ForcedToken';
661        "fo"                    -> 'ForcedToken';
662        "graceful"              -> 'GracefulToken';
663        "gr"                    -> 'GracefulToken';
664        "h221"                  -> 'H221Token';
665        "h223"                  -> 'H223Token';
666        "h226"                  -> 'H226Token';
667        "handoff"               -> 'HandOffToken';
668        "ho"                    -> 'HandOffToken';
669        "iepscall"              when (Version >= 3) -> 'IEPSToken';         % v3
670        "ieps"                  when (Version >= 3) -> 'IEPSToken';         % v3
671        "inactive"              -> 'InactiveToken';
672        "in"                    -> 'InactiveToken';
673        "internal"              when (Version >= 3) -> 'InternalToken';     % v3
674        "it"                    when (Version >= 3) -> 'InternalToken';     % v3
675        "immackrequired"        -> 'ImmAckRequiredToken';
676        "ia"                    -> 'ImmAckRequiredToken';
677        "inservice"             -> 'InSvcToken';
678        "intersignal"           when (Version >= 3) -> 'IntsigDelayToken'; % v3
679        "spais"                 when (Version >= 3) -> 'IntsigDelayToken'; % v3
680        "intbyevent"            -> 'InterruptByEventToken';
681        "ibe"                   -> 'InterruptByEventToken';
682        "intbysigdescr"         -> 'InterruptByNewSignalsDescrToken';
683        "ibs"                   -> 'InterruptByNewSignalsDescrToken';
684        "iv"                    -> 'InSvcToken';
685        "isolate"               -> 'IsolateToken';
686        "is"                    -> 'IsolateToken';
687	"iterationtoken"        when (Version >= 3) -> 'IterationToken'; % v3
688	"ir"                    when (Version >= 3) -> 'IterationToken'; % v3
689        "keepactive"            -> 'KeepActiveToken';
690        "ka"                    -> 'KeepActiveToken';
691	"local"                 -> 'LocalToken';
692        "l"                     -> 'LocalToken';
693        "localcontrol"          -> 'LocalControlToken';
694        "lockstep"              -> 'LockStepToken';
695        "sp"                    -> 'LockStepToken';
696        "o"                     -> 'LocalControlToken';
697        "loopback"              -> 'LoopbackToken';
698        "lb"                    -> 'LoopbackToken';
699        "media"                 -> 'MediaToken';
700        "m"                     -> 'MediaToken';
701        %% "megaco"                -> 'MegacopToken';
702	%% "!"                     -> 'megacoptoken';
703	"segment"               when (Version >= 3) -> 'MessageSegmentToken'; % v3
704	"sm"                    when (Version >= 3) -> 'MessageSegmentToken'; % v3
705        "method"                -> 'MethodToken';
706        "mt"                    -> 'MethodToken';
707        "mtp"                   -> 'MtpToken';
708        "mgcidtotry"            -> 'MgcIdToken';
709        "mg"                    -> 'MgcIdToken';
710        "mode"                  -> 'ModeToken';
711        "mo"                    -> 'ModeToken';
712        "modify"                -> 'ModifyToken';
713        "mf"                    -> 'ModifyToken';
714        "modem"                 -> 'ModemToken';
715        "md"                    -> 'ModemToken';
716        "move"                  -> 'MoveToken';
717        "mv"                    -> 'MoveToken';
718        "mux"                   -> 'MuxToken';
719        "mx"                    -> 'MuxToken';
720        "nevernotify"           when (Version >= 3) -> 'NeverNotifyToken'; % v3
721        "nbnn"                  when (Version >= 3) -> 'NeverNotifyToken'; % v3
722        "notify"                -> 'NotifyToken';
723        "n"                     -> 'NotifyToken';
724        "notifycompletion"      -> 'NotifyCompletionToken';
725        "nc"                    -> 'NotifyCompletionToken';
726        "immediatenotify"       when (Version >= 3) -> 'NotifyImmediateToken';    % v3
727        "nbin"                  when (Version >= 3) -> 'NotifyImmediateToken';    % v3
728        "regulatednotify"       when (Version >= 3) -> 'NotifyRegulatedToken';    % v3
729        "nbrn"                  when (Version >= 3) -> 'NotifyRegulatedToken';    % v3
730        "nx64kservice"          when (Version >= 2) -> 'Nx64kToken';              % v2
731        "n64"                   when (Version >= 2) -> 'Nx64kToken';              % v2
732        "observedevents"        -> 'ObservedEventsToken';
733        "oe"                    -> 'ObservedEventsToken';
734        "oneway"                -> 'OnewayToken';
735        "ow"                    -> 'OnewayToken';
736        "onewayboth"            when (Version >= 3) -> 'OnewayBothToken';     % v3
737        "owb"                   when (Version >= 3) -> 'OnewayBothToken';     % v3
738        "onewayexternal"        when (Version >= 3) -> 'OnewayExternalToken'; % v3
739        "owe"                   when (Version >= 3) -> 'OnewayExternalToken'; % v3
740        "off"                   -> 'OffToken';
741        "on"                    -> 'OnToken';
742        "onoff"                 -> 'OnOffToken';
743        "oo"                    -> 'OnOffToken';
744        "orlgc"                 when (Version >= 3) -> 'OrAUDITselectToken';  % v3
745        "otherreason"           -> 'OtherReasonToken';
746        "or"                    -> 'OtherReasonToken';
747        "outofservice"          -> 'OutOfSvcToken';
748        "os"                    -> 'OutOfSvcToken';
749        "packages"              -> 'PackagesToken';
750        "pg"                    -> 'PackagesToken';
751        "pending"               -> 'PendingToken';
752        "pn"                    -> 'PendingToken';
753        "priority"              -> 'PriorityToken';
754        "pr"                    -> 'PriorityToken';
755        "profile"               -> 'ProfileToken';
756        "pf"                    -> 'ProfileToken';
757        "reason"                -> 'ReasonToken';
758        "re"                    -> 'ReasonToken';
759        "receiveonly"           -> 'RecvonlyToken';
760        "requestid"             when (Version >= 3) -> 'RequestIDToken';    % v3
761        "rq"                    when (Version >= 3) -> 'RequestIDToken';    % v3
762        "rc"                    -> 'RecvonlyToken';
763        "reply"                 -> 'ReplyToken';
764        "p"                     -> 'ReplyToken';
765        "reseteventsdescriptor" when (Version >= 3) -> 'ResetEventsDescriptorToken'; % v3
766        "rse"                   when (Version >= 3) -> 'ResetEventsDescriptorToken'; % v3
767        "transactionresponseack"-> 'ResponseAckToken';
768        "k"                     -> 'ResponseAckToken';
769        "restart"               -> 'RestartToken';
770        "rs"                    -> 'RestartToken';
771	"remote"                -> 'RemoteToken';
772	"r"                     -> 'RemoteToken';
773        "sparequestid"          -> 'RequestIDToken';
774        "sparq"                 -> 'RequestIDToken';
775        "reservedgroup"         -> 'ReservedGroupToken';
776        "rg"                    -> 'ReservedGroupToken';
777        "reservedvalue"         -> 'ReservedValueToken';
778        "rv"                    -> 'ReservedValueToken';
779        "end"                   when (Version >= 3) -> 'SegmentationCompleteToken'; % v3
780        "&"                     when (Version >= 3) -> 'SegmentationCompleteToken'; % v3
781        "sendonly"              -> 'SendonlyToken';
782        "so"                    -> 'SendonlyToken';
783        "sendreceive"           -> 'SendrecvToken';
784        "sr"                    -> 'SendrecvToken';
785        "services"              -> 'ServicesToken';
786        "sv"                    -> 'ServicesToken';
787        "servicestates"         -> 'ServiceStatesToken';
788        "si"                    -> 'ServiceStatesToken';
789        "servicechange"         -> 'ServiceChangeToken';
790        "sc"                    -> 'ServiceChangeToken';
791        "servicechangeaddress"  -> 'ServiceChangeAddressToken';
792        "ad"                    -> 'ServiceChangeAddressToken';
793        "servicechangeinc"      when (Version >= 3) -> 'ServiceChangeIncompleteToken'; % v3
794        "sic"                   when (Version >= 3) -> 'ServiceChangeIncompleteToken'; % v3
795        "signallist"            -> 'SignalListToken';
796        "sl"                    -> 'SignalListToken';
797        "signals"               -> 'SignalsToken';
798        "sg"                    -> 'SignalsToken';
799        "signaltype"            -> 'SignalTypeToken';
800        "sy"                    -> 'SignalTypeToken';
801        "statistics"            -> 'StatsToken';
802        "sa"                    -> 'StatsToken';
803        "stream"                -> 'StreamToken';
804        "st"                    -> 'StreamToken';
805        "subtract"              -> 'SubtractToken';
806        "s"                     -> 'SubtractToken';
807        "synchisdn"             -> 'SynchISDNToken';
808        "sn"                    -> 'SynchISDNToken';
809        "terminationstate"      -> 'TerminationStateToken';
810        "ts"                    -> 'TerminationStateToken';
811        "test"                  -> 'TestToken';
812        "te"                    -> 'TestToken';
813        "timeout"               -> 'TimeOutToken';
814        "to"                    -> 'TimeOutToken';
815        "topology"              -> 'TopologyToken';
816        "tp"                    -> 'TopologyToken';
817        "transaction"           -> 'TransToken';
818        "t"                     -> 'TransToken';
819        "v18"                   -> 'V18Token';
820        "v22"                   -> 'V22Token';
821        "v22b"                  -> 'V22bisToken';
822        "v32"                   -> 'V32Token';
823        "v32b"                  -> 'V32bisToken';
824        "v34"                   -> 'V34Token';
825        "v76"                   -> 'V76Token';
826        "v90"                   -> 'V90Token';
827        "v91"                   -> 'V91Token';
828        "version"               -> 'VersionToken';
829        "v"                     -> 'VersionToken';
830	[_,_,_,_,_,_,_,_,$t,_,_,_,_,_,_,_,_] -> % Could be a time-stamp
831	    [D1,D2,D3,D4,D5,D6,D7,D8,_,T1,T2,T3,T4,T5,T6,T7,T8] = LowerText,
832	    select_TimeStampToken(D1,D2,D3,D4,D5,D6,D7,D8,
833				  T1,T2,T3,T4,T5,T6,T7,T8);
834	_                       -> 'SafeChars'
835    end.
836
837select_TimeStampToken(D1,D2,D3,D4,D5,D6,D7,D8,
838		      T1,T2,T3,T4,T5,T6,T7,T8)
839  when ($0 =< D1) andalso (D1 =< $9) andalso
840       ($0 =< D2) andalso (D2 =< $9) andalso
841       ($0 =< D3) andalso (D3 =< $9) andalso
842       ($0 =< D4) andalso (D4 =< $9) andalso
843       ($0 =< D5) andalso (D5 =< $9) andalso
844       ($0 =< D6) andalso (D6 =< $9) andalso
845       ($0 =< D7) andalso (D7 =< $9) andalso
846       ($0 =< D8) andalso (D8 =< $9) andalso
847       ($0 =< T1) andalso (T1 =< $9) andalso
848       ($0 =< T2) andalso (T2 =< $9) andalso
849       ($0 =< T3) andalso (T3 =< $9) andalso
850       ($0 =< T4) andalso (T4 =< $9) andalso
851       ($0 =< T5) andalso (T5 =< $9) andalso
852       ($0 =< T6) andalso (T6 =< $9) andalso
853       ($0 =< T7) andalso (T7 =< $9) andalso
854       ($0 =< T8) andalso (T8 =< $9) ->
855    'TimeStampToken';
856select_TimeStampToken(_D1,_D2,_D3,_D4,_D5,_D6,_D7,_D8,
857		      _T1,_T2,_T3,_T4,_T5,_T6,_T7,_T8) ->
858    'SafeChars'.
859
860
861%% d(F) ->
862%%     d(F, []).
863
864%% d(F, A) ->
865%%     d(get(dbg), F, A).
866
867%% d(true, F, A) ->
868%%     io:format("DBG:~p:" ++ F ++ "~n", [?MODULE|A]);
869%% d(_, _, _) ->
870%%     ok.
871