aboutsummaryrefslogtreecommitdiffstats
path: root/lib/megaco/src/text/megaco_text_parser_prev3b.hrl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/megaco/src/text/megaco_text_parser_prev3b.hrl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/megaco/src/text/megaco_text_parser_prev3b.hrl')
-rw-r--r--lib/megaco/src/text/megaco_text_parser_prev3b.hrl1717
1 files changed, 1717 insertions, 0 deletions
diff --git a/lib/megaco/src/text/megaco_text_parser_prev3b.hrl b/lib/megaco/src/text/megaco_text_parser_prev3b.hrl
new file mode 100644
index 0000000000..ba10dfb943
--- /dev/null
+++ b/lib/megaco/src/text/megaco_text_parser_prev3b.hrl
@@ -0,0 +1,1717 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%%----------------------------------------------------------------------
+%% Purpose : Define semantic text parser actions
+%%----------------------------------------------------------------------
+
+
+-include_lib("megaco/include/megaco.hrl").
+-include_lib("megaco/include/megaco_message_prev3b.hrl").
+-define(encoder_version_pre_prev3c,true).
+-include("megaco_text_tokens.hrl").
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{make_safe_token,1}]}).
+-endif.
+make_safe_token(Token) ->
+ {_TokenTag, Line, Text} = Token,
+ {safeToken, Line, Text}.
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{ensure_value,1}]}).
+-endif.
+ensure_value(Token) ->
+ case Token of
+ {safeToken, _Line, Text} when is_list(Text) ->
+ Text; % We really should ensure length
+ {'QuotedChars', _Line, Text} when is_list(Text) ->
+ Text; % We really should ensure length
+ Text when is_list(Text) ->
+ Text % We really should ensure length
+ end.
+
+%% NAME = ALPHA *63(ALPHA / DIGIT / "_" )
+-ifdef(megaco_parser_inline).
+-compile({inline,[{ensure_NAME,1}]}).
+-endif.
+ensure_NAME(Token) ->
+ {_TokenTag, _Line, Text} = Token,
+ Text. %% BUGBUG: ensure length and chars
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{ensure_requestID,1}]}).
+-endif.
+ensure_requestID(Token) ->
+ case Token of
+ {safeToken, _Line, "*"} ->
+ ?megaco_all_request_id;
+ _ ->
+ ensure_uint32(Token)
+ end.
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{ensure_streamID,1}]}).
+-endif.
+ensure_streamID(StreamId) ->
+ ensure_uint16(StreamId).
+
+ensure_auth_header(SpiToken, SnToken, AdToken) ->
+ Spi = ensure_hex(SpiToken, 8, 8),
+ Sn = ensure_hex(SnToken, 8, 8),
+ Ad = ensure_hex(AdToken, 24, 64),
+ #'AuthenticationHeader'{secParmIndex = Spi, seqNum = Sn, ad = Ad}.
+
+%% The values 0x0, 0xFFFFFFFE and 0xFFFFFFFF are reserved.
+%% ContextID = (UINT32 / "*" / "-" / "$")
+-ifdef(megaco_parser_inline).
+-compile({inline,[{ensure_contextID,1}]}).
+-endif.
+ensure_contextID(Token) ->
+ {_TokenTag, Line, Text} = Token,
+ case Text of
+ "*" -> ?megaco_all_context_id;
+ "-" -> ?megaco_null_context_id;
+ "\$" -> ?megaco_choose_context_id;
+ Int ->
+ CID = ensure_uint32(Int),
+ if
+ (CID =/= 0) andalso
+ (CID =/= 16#FFFFFFFE) andalso
+ (CID =/= 16#FFFFFFFF) ->
+ CID;
+ true ->
+ return_error(Line, {bad_ContextID, CID})
+ end
+ end.
+
+ensure_domainAddress([{_T, _L, _A} = Addr0], Port) ->
+ Addr = ensure_ip4addr(Addr0),
+ {ip4Address, #'IP4Address'{address = Addr, portNumber = Port}};
+ensure_domainAddress([colon,colon], Port) ->
+ Addr = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
+ {ip6Address, #'IP6Address'{address = Addr, portNumber = Port}};
+ensure_domainAddress(Addr0, Port) ->
+ Addr = ensure_ip6addr(Addr0),
+ {ip6Address, #'IP6Address'{address = Addr, portNumber = Port}}.
+
+
+ensure_ip4addr(Token) ->
+ {_TokenTag, Line, Addr} = Token,
+%% case string:tokens(Addr, [$.]) of
+%% [T1, T2, T3, T4] ->
+ case split_ip4addr_text(Addr, []) of
+ [T1, T2, T3, T4] ->
+ %% We optimize by sending only the text part (Addr) of
+ %% the token to the function.
+ %% If something is wrong, then we do not get a proper
+ %% position and therefor we catch and issue the
+ %% the error again (with the proper line number).
+ case (catch [
+ ensure_uint(T1, 0, 255),
+ ensure_uint(T2, 0, 255),
+ ensure_uint(T3, 0, 255),
+ ensure_uint(T4, 0, 255)
+ ]) of
+ A when is_list(A) ->
+ A;
+ _ ->
+ return_error(Line, {bad_IP4address, Addr})
+ end;
+ _ ->
+ return_error(Line, {bad_IP4address, Addr})
+ end.
+
+split_ip4addr_text([], Acc) ->
+ [ lists:reverse(Acc) ];
+split_ip4addr_text([$. | Rest], Acc) ->
+ [ lists:reverse(Acc) | split_ip4addr_text(Rest, []) ];
+split_ip4addr_text([H | T], Acc) ->
+ split_ip4addr_text(T, [H | Acc]).
+
+
+ensure_ip6addr([colon,colon|T]) ->
+ [H1|T1] = lists:reverse(T),
+ case do_ensure_ip6addr(T1, true, [ensure_hex4_or_ip4addr(H1)], 1) of
+ {true, A} when length(A) == 16 ->
+ A;
+ {true, B} when length(B) < 16 ->
+ lists:duplicate(16 - length(B), 0) ++ B;
+ {true, C} ->
+ throw({error, {?MODULE, {bad_mid_ip6addr_length, C}}})
+ end;
+ensure_ip6addr(L) ->
+ case lists:reverse(L) of
+ [colon, colon| T] ->
+ case do_ensure_ip6addr(T, true, [], 1) of
+ {true, A} when length(A) == 16 ->
+ A;
+ {true, B} when length(B) < 16 ->
+ B ++ lists:duplicate(16 - length(B), 0);
+ {true, C} ->
+ throw({error, {?MODULE, {bad_mid_ip6addr_length, C}}})
+ end;
+ [H|L1] -> % A (last element) could be an ip4 address
+ case do_ensure_ip6addr(L1,false,[ensure_hex4_or_ip4addr(H)],1) of
+ {false, A} when length(A) == 16 ->
+ A;
+ %% allow a pad even if the address is full (i.e. 16)
+ {true, B} when length(B) =< 17 ->
+ do_ensure_ip6addr_padding(B, 0);
+ {Pad, C} ->
+ throw({error, {?MODULE, {bad_mid_ip6addr_length, Pad, C}}})
+ end
+
+ end.
+
+
+do_ensure_ip6addr([], Pad, Acc, _) ->
+ {Pad, lists:flatten(Acc)};
+do_ensure_ip6addr([colon,colon|T], false, Acc, Line) ->
+ do_ensure_ip6addr(T, true, [pad|Acc], Line);
+do_ensure_ip6addr([colon,colon|T], true, Acc, Line) ->
+ return_error(Line, {bad_mid_duplicate_padding, T, Acc});
+do_ensure_ip6addr([colon|T], Pad, Acc, Line) ->
+ do_ensure_ip6addr(T, Pad, Acc, Line);
+do_ensure_ip6addr([{_, Line, _} = A|T], Pad, Acc, _) ->
+ do_ensure_ip6addr(T, Pad, [ensure_hex4(A)|Acc], Line).
+
+do_ensure_ip6addr_padding([], _) ->
+ [];
+do_ensure_ip6addr_padding([pad|T], N) ->
+ lists:duplicate(16 - (N + length(T)), 0) ++ T;
+do_ensure_ip6addr_padding([H|T], N) ->
+ [H|do_ensure_ip6addr_padding(T, N+1)].
+
+ensure_hex4_or_ip4addr({TokenTag, Line, Addr} = V) ->
+ case string:tokens(Addr, [$.]) of
+ [T1, T2, T3, T4] ->
+ A1 = ensure_uint({TokenTag, Line, T1}, 0, 255),
+ A2 = ensure_uint({TokenTag, Line, T2}, 0, 255),
+ A3 = ensure_uint({TokenTag, Line, T3}, 0, 255),
+ A4 = ensure_uint({TokenTag, Line, T4}, 0, 255),
+ [A1, A2, A3, A4];
+ _ ->
+ ensure_hex4(V)
+ %% %% BMK BMK BMK
+ %% %% Here we should test for hexseq
+ %% return_error(Line, {bad_IP4address, Addr})
+ end.
+
+ensure_hex4({_TokenTag, Line, Hex4})
+ when length(Hex4) =< 4, length(Hex4) > 0 ->
+ case (catch do_ensure_hex4(Hex4)) of
+ IL when is_list(IL) andalso (length(IL) =:= 2) ->
+ IL;
+ Error ->
+ return_error(Line, {bad_hex4, Hex4, Error})
+ end.
+
+do_ensure_hex4([_H1, _H2, _H3, _H4] = H) ->
+ hex_to_int(H, []);
+do_ensure_hex4([H2, H3, H4]) ->
+ hex_to_int([$0, H2, H3, H4], []);
+do_ensure_hex4([H3, H4]) ->
+ hex_to_int([$0, $0, H3, H4], []);
+do_ensure_hex4([H4]) ->
+ hex_to_int([$0, $0, $0, H4], []).
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{ensure_domainName,2}]}).
+-endif.
+ensure_domainName(Token, Port) ->
+ {_TokenTag, _Line, Name} = Token,
+ %% BUGBUG: validate name
+ {domainName, #'DomainName'{name = Name, portNumber = Port}}.
+
+%% extensionParameter= "X" ("-" / "+") 1*6(ALPHA / DIGIT)
+-ifdef(megaco_parser_inline).
+-compile({inline,[{ensure_extensionParameter,1}]}).
+-endif.
+ensure_extensionParameter(Token) ->
+ {_TokenTag, Line, Text} = Token,
+ case Text of
+ [X, S | _Chars] ->
+ if
+ X /= $X, X /= $x,
+ S /= $+, S /= $- ->
+ return_error(Line, {bad_extension_parameter, Text});
+ true ->
+ {extension_parameter, Text}
+ end;
+ _ ->
+ return_error(Line, {bad_extension_parameter, Text})
+ end.
+
+ensure_message(MegacopToken, MID, Body) ->
+%% #'ServiceChangeProfile'{profileName = Name,
+%% version = Version} =
+%% ensure_profile(MegacopToken),
+%% case Name of
+%% "megaco" ->
+%% #'Message'{version = Version, mId = MID, messageBody = Body};
+%% [$!] ->
+%% #'Message'{version = Version, mId = MID, messageBody = Body}
+%% end.
+ {_TokenTag, Line, Text} = MegacopToken,
+ case split_Megacop(Text, []) of
+ {Name, Version} ->
+ Version2 = ensure_version(Version),
+ case Name of
+ "megaco" ->
+ #'Message'{version = Version2,
+ mId = MID,
+ messageBody = Body};
+ [$!] ->
+ #'Message'{version = Version2,
+ mId = MID,
+ messageBody = Body}
+ end;
+ _ ->
+ return_error(Line, {bad_name_or_version, Text})
+ end.
+
+split_Megacop([], _) ->
+ error;
+split_Megacop([$/ | Version], Acc) ->
+ {lists:reverse(Acc), Version};
+split_Megacop([H | T], Acc) ->
+ split_Megacop(T, [H | Acc]).
+
+
+%% Corr1:
+%% As of corr 1 ModemDescriptor has been deprecated.
+%% and since this functon is only used when creating
+%% a ModemDescriptor, iit is removed.
+%% modemType = (V32bisToken / V22bisToken / V18Token /
+%% V22Token / V32Token / V34Token / V90Token /
+%% V91Token / SynchISDNToken / extensionParameter)
+%% ensure_modemType({_TokenTag, _Line, Text} = Token) ->
+%% case Text of
+%% "v32b" -> v32bis;
+%% "v22b" -> v22bis;
+%% "v18" -> v18;
+%% "v22" -> v22;
+%% "v32" -> v32;
+%% "v34" -> v34;
+%% "v90" -> v90;
+%% "v91" -> v91;
+%% "synchisdn" -> synchISDN;
+%% "sn" -> synchISDN;
+%% [$x | _] -> ensure_extensionParameter(Token)
+%% end.
+
+%% An mtp address is five octets long
+-ifdef(megaco_parser_inline).
+-compile({inline,[{ensure_mtpAddress,1}]}).
+-endif.
+ensure_mtpAddress(Token) ->
+ {_TokenTag, _Line, Addr} = Token,
+ %% BUGBUG: validate address
+ {mtpAddress, Addr}.
+
+%% MuxType = ( H221Token / H223Token / H226Token / V76Token / extensionParameter )
+-ifdef(megaco_parser_inline).
+-compile({inline,[{ensure_muxType,1}]}).
+-endif.
+ensure_muxType(Token) ->
+ {_TokenTag, _Line, Text} = Token,
+ case Text of
+ "h221" -> h221;
+ "h223" -> h223;
+ "h226" -> h226;
+ "v76" -> v76;
+ "nx64k" -> nx64k; % v2
+ [$x | _] -> ensure_extensionParameter(Token)
+ end.
+
+%% packagesItem = NAME "-" UINT16
+%% NAME = ALPHA *63(ALPHA / DIGIT / "_" )
+-ifdef(megaco_parser_inline).
+-compile({inline,[{ensure_packagesItem,1}]}).
+-endif.
+ensure_packagesItem(Token) ->
+ {_TokenTag, Line, Text} = Token,
+ case split_packagesItem(Text, []) of
+ {Name, Version} ->
+ %% As we don't ensure length of the names, there is no point
+ %% in doing the ensure_NAME thing...
+ #'PackagesItem'{packageName = Name,
+ packageVersion = ensure_uint(Version, 0, 99)};
+ _ ->
+ return_error(Line, {bad_PackagesItem, Text})
+ end.
+
+split_packagesItem([], _) ->
+ error;
+split_packagesItem([$- | Version], Acc) ->
+ {lists:reverse(Acc), Version};
+split_packagesItem([H|T], Acc) ->
+ split_packagesItem(T, [H|Acc]).
+
+
+%% pkgdName = (PackageName / "*") SLASH (ItemID / "*" )
+%% PackageName = NAME
+%% ItemID = NAME
+-ifdef(megaco_parser_inline).
+-compile({inline,[{ensure_pkgdName,1}]}).
+-endif.
+ensure_pkgdName(Token) ->
+ {_TokenTag, Line, Text} = Token,
+ case ensure_pkgdName(Text, []) of
+ ok ->
+ %% As we don't really do any checks on the strings
+ %% (length or content) there is really no point in
+ %% "ensuring" the name and item part of the
+ %% package name
+ %% ensure_name_or_star(Name),
+ %% ensure_name_or_star(Item),
+ Text;
+ _ ->
+ return_error(Line, {bad_pkgdName, Text})
+ end.
+
+ensure_pkgdName([], _) ->
+ error;
+ensure_pkgdName([$/ | T], Acc)
+ when ((length(T) > 0) andalso (length(Acc) > 0)) ->
+ ok;
+ensure_pkgdName([H | T], Acc) ->
+ ensure_pkgdName(T, [H | Acc]).
+
+
+%% -compile({inline,[{ensure_name_or_star,1}]}).
+%% ensure_name_or_star(Val) ->
+%% %% case Token of
+%% %% {_, _, Name} when Name =:= "*" ->
+%% %% Name;
+%% %% _ ->
+%% %% ensure_NAME(Token)
+%% %% end.
+%% if
+%% Val =:= "*" ->
+%% Val;
+%% true ->
+%% %% as we don't really validate the text part of the token(s),
+%% %% we can just return the value assuming it to be correct...
+%% Val
+%% end.
+
+
+%% v2 - start
+
+merge_indAudMediaDescriptor_streams(asn1_NOVALUE, []) ->
+ asn1_NOVALUE;
+merge_indAudMediaDescriptor_streams(Stream, [])
+ when is_record(Stream, 'IndAudStreamParms') ->
+ {oneStream, Stream};
+merge_indAudMediaDescriptor_streams(asn1_NOVALUE, MStreams) ->
+ {multiStream, lists:reverse(MStreams)};
+merge_indAudMediaDescriptor_streams(Stream, MStreams) ->
+ return_error(0,
+ {invalid_indAudMediaDescriptor_streams, {Stream, MStreams}}).
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{merge_indAudMediaDescriptor,1}]}).
+-endif.
+merge_indAudMediaDescriptor(Vals) ->
+ merge_indAudMediaDescriptor(Vals, asn1_NOVALUE, asn1_NOVALUE, []).
+
+merge_indAudMediaDescriptor([], TSD, OneStream, MultiStreams) ->
+ Streams = merge_indAudMediaDescriptor_streams(OneStream, MultiStreams),
+ #'IndAudMediaDescriptor'{termStateDescr = TSD,
+ streams = Streams};
+
+merge_indAudMediaDescriptor([{termStateDescr, H}|T], asn1_NOVALUE, OS, MS) ->
+ merge_indAudMediaDescriptor(T, H, OS, MS);
+merge_indAudMediaDescriptor([{streamDescr, Val}|T], TSD, asn1_NOVALUE, MS) ->
+ merge_indAudMediaDescriptor(T, TSD, asn1_NOVALUE, [Val|MS]);
+merge_indAudMediaDescriptor([{streamParm, Val}|T], TSD, asn1_NOVALUE, []) ->
+ merge_indAudMediaDescriptor(T, TSD, Val, []);
+merge_indAudMediaDescriptor(Vals, TSD, OneStream, MultiStream) ->
+ return_error(0, {invalid_indAudMediaDescriptor,
+ {Vals, TSD, OneStream, MultiStream}}).
+
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{merge_indAudLocalControlDescriptor,1}]}).
+-endif.
+merge_indAudLocalControlDescriptor(Parms) ->
+ do_merge_indAudLocalControlDescriptor(Parms,
+ #'IndAudLocalControlDescriptor'{}).
+
+do_merge_indAudLocalControlDescriptor([Parm | Parms], Desc) ->
+ case Parm of
+ modeToken when Desc#'IndAudLocalControlDescriptor'.streamMode =:= asn1_NOVALUE ->
+ Desc2 = Desc#'IndAudLocalControlDescriptor'{streamMode = 'NULL'},
+ do_merge_indAudLocalControlDescriptor(Parms, Desc2);
+ reservedGroupToken when Desc#'IndAudLocalControlDescriptor'.reserveGroup =:= asn1_NOVALUE ->
+ Desc2 = Desc#'IndAudLocalControlDescriptor'{reserveGroup = 'NULL'},
+ do_merge_indAudLocalControlDescriptor(Parms, Desc2);
+ reservedValueToken when Desc#'IndAudLocalControlDescriptor'.reserveValue =:= asn1_NOVALUE ->
+ Desc2 = Desc#'IndAudLocalControlDescriptor'{reserveValue = 'NULL'},
+ do_merge_indAudLocalControlDescriptor(Parms, Desc2);
+ {pkgdName, Val} when Desc#'IndAudLocalControlDescriptor'.propertyParms =:= asn1_NOVALUE ->
+ PropParms = [#'IndAudPropertyParm'{name = Val}],
+ Desc2 = Desc#'IndAudLocalControlDescriptor'{propertyParms = PropParms},
+ do_merge_indAudLocalControlDescriptor(Parms, Desc2);
+ {pkgdName, Val} when is_list(Desc#'IndAudLocalControlDescriptor'.propertyParms) ->
+ PropParms = Desc#'IndAudLocalControlDescriptor'.propertyParms,
+ PropParms2 = [#'IndAudPropertyParm'{name = Val} | PropParms],
+ Desc2 = Desc#'IndAudLocalControlDescriptor'{propertyParms = PropParms2},
+ do_merge_indAudLocalControlDescriptor(Parms, Desc2)
+ end;
+do_merge_indAudLocalControlDescriptor([], Desc) ->
+ case Desc#'IndAudLocalControlDescriptor'.propertyParms of
+ [_ | _] = PropParms -> % List has more then one element
+ PropParms2= lists:reverse(PropParms),
+ Desc#'IndAudLocalControlDescriptor'{propertyParms = PropParms2};
+ _ ->
+ Desc
+ end.
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{ensure_indAudLocalParm,1}]}).
+-endif.
+ensure_indAudLocalParm(Token) ->
+ case Token of
+ {safeToken, _Line, "mode"} -> modeToken;
+ {safeToken, _Line, "mo"} -> modeToken;
+ {safeToken, _Line, "reservedgroup"} -> reservedGroupToken;
+ {safeToken, _Line, "rg"} -> reservedGroupToken;
+ {safeToken, _Line, "reservedvalue"} -> reservedValueToken;
+ {safeToken, _Line, "rv"} -> reservedValueToken;
+ PkgdName -> {pkgdName,
+ ensure_pkgdName(PkgdName)}
+ end.
+
+merge_indAudTerminationStateDescriptor({pkgdName, Val}) ->
+ PropParm = #'IndAudPropertyParm'{name = Val},
+ #'IndAudTerminationStateDescriptor'{propertyParms = [PropParm]};
+merge_indAudTerminationStateDescriptor(serviceStatesToken) ->
+ #'IndAudTerminationStateDescriptor'{serviceState = 'NULL'};
+merge_indAudTerminationStateDescriptor(bufferToken) ->
+ #'IndAudTerminationStateDescriptor'{eventBufferControl = 'NULL'}.
+
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{merge_indAudEventBufferDescriptor,2}]}).
+-endif.
+merge_indAudEventBufferDescriptor(EventName, SpecParams) ->
+ IAEBD = #'IndAudEventBufferDescriptor'{eventName = EventName},
+ do_merge_indAudEventBufferDescriptor(SpecParams, IAEBD).
+
+do_merge_indAudEventBufferDescriptor(asn1_NOVALUE, IAEBD) ->
+ IAEBD;
+do_merge_indAudEventBufferDescriptor({streamID, StreamID}, IAEBD) ->
+ IAEBD#'IndAudEventBufferDescriptor'{streamID = StreamID};
+do_merge_indAudEventBufferDescriptor({eventParameterName, _Name} = EPN,
+ IAEBD) ->
+ %% BUGBUG BUGBUG BUGBUG
+ %% This is an ugly hack to allow the eventParamName which only
+ %% exists in the text encoding...
+ IAEBD#'IndAudEventBufferDescriptor'{streamID = EPN}.
+
+
+ensure_indAudSignalListParm(SIG) when is_record(SIG, 'Signal') ->
+ ensure_indAudSignal(SIG).
+
+ensure_indAudSignal(#'Signal'{signalName = SignalName,
+ streamID = asn1_NOVALUE,
+ sigType = asn1_NOVALUE,
+ duration = asn1_NOVALUE,
+ notifyCompletion = asn1_NOVALUE,
+ keepActive = asn1_NOVALUE,
+ sigParList = []}) ->
+ #'IndAudSignal'{signalName = SignalName}.
+
+
+ensure_IADMD({_TokenTag, _Line,
+ #'DigitMapDescriptor'{digitMapName = Name,
+ digitMapValue = asn1_NOVALUE}}) ->
+ #'IndAudDigitMapDescriptor'{digitMapName = Name}.
+
+
+merge_indAudPackagesDescriptor(#'PackagesItem'{packageName = N,
+ packageVersion = V}) ->
+ #'IndAudPackagesDescriptor'{packageName = N,
+ packageVersion = V}.
+
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{ensure_indAudTerminationStateParm,1}]}).
+-endif.
+ensure_indAudTerminationStateParm(Token) ->
+ case Token of
+ {safeToken, _Line, "servicestates"} -> serviceStatesToken;
+ {safeToken, _Line, "si"} -> serviceStatesToken;
+ {safeToken, _Line, "buffer"} -> bufferToken;
+ {safeToken, _Line, "bf"} -> bufferToken;
+ PkgdName -> {pkgdName,
+ ensure_pkgdName(PkgdName)}
+ end.
+
+
+%% Types modified by v2:
+
+merge_auditDescriptor([]) ->
+ #'AuditDescriptor'{};
+merge_auditDescriptor(Tokens) when is_list(Tokens) ->
+ case lists:keysearch(terminationAudit, 1, Tokens) of
+ {value, {terminationAudit, TA}} ->
+ case lists:keydelete(terminationAudit, 1, Tokens) of
+ [] ->
+ #'AuditDescriptor'{auditPropertyToken = TA};
+ AuditTokens ->
+ #'AuditDescriptor'{auditToken = AuditTokens,
+ auditPropertyToken = TA}
+ end;
+ false ->
+ #'AuditDescriptor'{auditToken = Tokens}
+ end;
+merge_auditDescriptor(_) ->
+ #'AuditDescriptor'{}.
+
+
+%% v2 - end
+
+
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{merge_ServiceChangeParm,1}]}).
+-endif.
+merge_ServiceChangeParm(Parms) ->
+ Required = [serviceChangeReason, serviceChangeMethod],
+ merge_ServiceChangeParm(Parms, #'ServiceChangeParm'{}, Required).
+
+merge_ServiceChangeParm([], SCP, []) ->
+ SCP;
+
+merge_ServiceChangeParm([], _SCP, Required) ->
+ exit({missing_required_serviceChangeParm, Required});
+
+merge_ServiceChangeParm([{address, Val}|Parms], SCP0, Req)
+ when (SCP0#'ServiceChangeParm'.serviceChangeAddress == asn1_NOVALUE) andalso
+ (SCP0#'ServiceChangeParm'.serviceChangeMgcId == asn1_NOVALUE) ->
+ SCP = SCP0#'ServiceChangeParm'{serviceChangeAddress = Val},
+ merge_ServiceChangeParm(Parms, SCP, Req);
+merge_ServiceChangeParm([{address, Val}|_Parms], SCP0, _Req)
+ when SCP0#'ServiceChangeParm'.serviceChangeAddress == asn1_NOVALUE ->
+ MgcId = SCP0#'ServiceChangeParm'.serviceChangeMgcId,
+ exit({not_both_address_mgcid_serviceChangeParm, Val, MgcId});
+
+merge_ServiceChangeParm([{mgc_id, Val}|Parms], SCP0, Req)
+ when (SCP0#'ServiceChangeParm'.serviceChangeMgcId == asn1_NOVALUE) andalso
+ (SCP0#'ServiceChangeParm'.serviceChangeAddress == asn1_NOVALUE) ->
+ SCP = SCP0#'ServiceChangeParm'{serviceChangeMgcId = Val},
+ merge_ServiceChangeParm(Parms, SCP, Req);
+merge_ServiceChangeParm([{mgc_id, Val}|_Parms], SCP0, _Req)
+ when SCP0#'ServiceChangeParm'.serviceChangeMgcId == asn1_NOVALUE ->
+ Addr = SCP0#'ServiceChangeParm'.serviceChangeAddress,
+ exit({not_both_address_mgcid_serviceChangeParm, Val, Addr});
+
+merge_ServiceChangeParm([{profile, Val}|Parms], SCP0, Req)
+ when SCP0#'ServiceChangeParm'.serviceChangeProfile == asn1_NOVALUE ->
+ SCP = SCP0#'ServiceChangeParm'{serviceChangeProfile = Val},
+ merge_ServiceChangeParm(Parms, SCP, Req);
+
+merge_ServiceChangeParm([{version, Val}|Parms], SCP0, Req)
+ when SCP0#'ServiceChangeParm'.serviceChangeVersion == asn1_NOVALUE ->
+ SCP = SCP0#'ServiceChangeParm'{serviceChangeVersion = Val},
+ merge_ServiceChangeParm(Parms, SCP, Req);
+
+%% REQUIRED (i.e. no default value)
+merge_ServiceChangeParm([{reason, Val}|Parms], SCP0, Req0)
+ when SCP0#'ServiceChangeParm'.serviceChangeReason == undefined ->
+ SCP = SCP0#'ServiceChangeParm'{serviceChangeReason = Val},
+ Req = lists:delete(serviceChangeReason, Req0),
+ merge_ServiceChangeParm(Parms, SCP, Req);
+
+merge_ServiceChangeParm([{delay, Val}|Parms], SCP0, Req)
+ when SCP0#'ServiceChangeParm'.serviceChangeDelay == asn1_NOVALUE ->
+ SCP = SCP0#'ServiceChangeParm'{serviceChangeDelay = Val},
+ merge_ServiceChangeParm(Parms, SCP, Req);
+
+%% REQUIRED (i.e. no default value)
+merge_ServiceChangeParm([{method, Val}|Parms], SCP0, Req0)
+ when SCP0#'ServiceChangeParm'.serviceChangeMethod == undefined ->
+ SCP = SCP0#'ServiceChangeParm'{serviceChangeMethod = Val},
+ Req = lists:delete(serviceChangeMethod, Req0),
+ merge_ServiceChangeParm(Parms, SCP, Req);
+
+merge_ServiceChangeParm([{time_stamp, Val}|Parms], SCP0, Req)
+ when SCP0#'ServiceChangeParm'.timeStamp == asn1_NOVALUE ->
+ SCP = SCP0#'ServiceChangeParm'{timeStamp = Val},
+ merge_ServiceChangeParm(Parms, SCP, Req);
+
+merge_ServiceChangeParm([{extension, _Val}|Parms], SCP0, Req) ->
+ merge_ServiceChangeParm(Parms, SCP0, Req);
+
+merge_ServiceChangeParm([{audit_item, Val}|Parms], SCP0, Req)
+ when (SCP0#'ServiceChangeParm'.serviceChangeInfo == asn1_NOVALUE) andalso
+ is_atom(Val) ->
+ SCI = #'AuditDescriptor'{auditToken = [Val]},
+ SCP = SCP0#'ServiceChangeParm'{serviceChangeInfo = SCI},
+ merge_ServiceChangeParm(Parms, SCP, Req);
+merge_ServiceChangeParm([{audit_item, Val}|Parms], SCP0, Req)
+ when (SCP0#'ServiceChangeParm'.serviceChangeInfo == asn1_NOVALUE) andalso
+ is_tuple(Val) ->
+ SCI = #'AuditDescriptor'{auditPropertyToken = [Val]},
+ SCP = SCP0#'ServiceChangeParm'{serviceChangeInfo = SCI},
+ merge_ServiceChangeParm(Parms, SCP, Req);
+merge_ServiceChangeParm([{audit_item, Val}|Parms], SCP0, Req)
+ when is_record(SCP0#'ServiceChangeParm'.serviceChangeInfo, 'AuditDescriptor') andalso
+ is_atom(Val) ->
+ SCI0 = SCP0#'ServiceChangeParm'.serviceChangeInfo,
+ L = SCI0#'AuditDescriptor'.auditToken,
+ SCI = SCI0#'AuditDescriptor'{auditToken = [Val|L]},
+ SCP = SCP0#'ServiceChangeParm'{serviceChangeInfo = SCI},
+ merge_ServiceChangeParm(Parms, SCP, Req);
+merge_ServiceChangeParm([{audit_item, Val}|Parms], SCP0, Req)
+ when is_record(SCP0#'ServiceChangeParm'.serviceChangeInfo, 'AuditDescriptor') andalso
+ is_tuple(Val) ->
+ SCI0 = SCP0#'ServiceChangeParm'.serviceChangeInfo,
+ L = SCI0#'AuditDescriptor'.auditPropertyToken,
+ SCI = SCI0#'AuditDescriptor'{auditPropertyToken = [Val|L]},
+ SCP = SCP0#'ServiceChangeParm'{serviceChangeInfo = SCI},
+ merge_ServiceChangeParm(Parms, SCP, Req);
+
+merge_ServiceChangeParm([incomplete|Parms], SCP0, Req)
+ when SCP0#'ServiceChangeParm'.serviceChangeIncompleteFlag == asn1_NOVALUE ->
+ SCP = SCP0#'ServiceChangeParm'{serviceChangeIncompleteFlag = 'NULL'},
+ merge_ServiceChangeParm(Parms, SCP, Req);
+
+merge_ServiceChangeParm([{Tag, Val}|_Parms], SCP, _Req) ->
+ Val2 =
+ case Tag of
+ address ->
+ SCP#'ServiceChangeParm'.serviceChangeAddress;
+ mgc_id ->
+ SCP#'ServiceChangeParm'.serviceChangeMgcId;
+ profile ->
+ SCP#'ServiceChangeParm'.serviceChangeProfile;
+ version ->
+ SCP#'ServiceChangeParm'.serviceChangeVersion;
+ reason ->
+ SCP#'ServiceChangeParm'.serviceChangeReason;
+ delay ->
+ SCP#'ServiceChangeParm'.serviceChangeDelay;
+ method ->
+ SCP#'ServiceChangeParm'.serviceChangeMethod;
+ time_stamp ->
+ SCP#'ServiceChangeParm'.timeStamp;
+ audit_item ->
+ SCP#'ServiceChangeParm'.serviceChangeInfo
+ end,
+ exit({at_most_once_serviceChangeParm, {Tag, Val, Val2}});
+merge_ServiceChangeParm([Parm|_Parms], SCP, _Req) ->
+ Parm2 =
+ case Parm of
+ incomplete ->
+ SCP#'ServiceChangeParm'.serviceChangeIncompleteFlag
+ end,
+ exit({at_most_once_serviceChangeParm, {Parm, Parm2}}).
+
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{merge_ServiceChangeResParm,1}]}).
+-endif.
+merge_ServiceChangeResParm(Parms) ->
+ merge_ServiceChangeResParm(Parms, #'ServiceChangeResParm'{}).
+
+merge_ServiceChangeResParm([], SCRP) ->
+ SCRP;
+merge_ServiceChangeResParm([{address, Val}|Parms], SCRP0)
+ when (SCRP0#'ServiceChangeResParm'.serviceChangeAddress == asn1_NOVALUE) andalso
+ (SCRP0#'ServiceChangeResParm'.serviceChangeMgcId == asn1_NOVALUE) ->
+ SCRP = SCRP0#'ServiceChangeResParm'{serviceChangeAddress = Val},
+ merge_ServiceChangeResParm(Parms, SCRP);
+merge_ServiceChangeResParm([{address, Val}|_Parms], SCRP0)
+ when SCRP0#'ServiceChangeResParm'.serviceChangeAddress == asn1_NOVALUE ->
+ MgcId = SCRP0#'ServiceChangeResParm'.serviceChangeMgcId,
+ exit({not_both_address_mgcid_servChgReplyParm, Val, MgcId});
+
+merge_ServiceChangeResParm([{mgc_id, Val}|Parms], SCRP0)
+ when (SCRP0#'ServiceChangeResParm'.serviceChangeMgcId == asn1_NOVALUE) andalso
+ (SCRP0#'ServiceChangeResParm'.serviceChangeAddress == asn1_NOVALUE) ->
+ SCRP = SCRP0#'ServiceChangeResParm'{serviceChangeMgcId = Val},
+ merge_ServiceChangeResParm(Parms, SCRP);
+merge_ServiceChangeResParm([{mgc_id, Val}|_Parms], SCRP0)
+ when SCRP0#'ServiceChangeResParm'.serviceChangeMgcId == asn1_NOVALUE ->
+ Addr = SCRP0#'ServiceChangeResParm'.serviceChangeAddress,
+ exit({not_both_address_mgcid_servChgReplyParm, Val, Addr});
+
+merge_ServiceChangeResParm([{profile, Val}|Parms], SCRP0)
+ when SCRP0#'ServiceChangeResParm'.serviceChangeProfile == asn1_NOVALUE ->
+ SCRP = SCRP0#'ServiceChangeResParm'{serviceChangeProfile = Val},
+ merge_ServiceChangeResParm(Parms, SCRP);
+
+merge_ServiceChangeResParm([{version, Val}|Parms], SCRP0)
+ when SCRP0#'ServiceChangeResParm'.serviceChangeVersion == asn1_NOVALUE ->
+ SCRP = SCRP0#'ServiceChangeResParm'{serviceChangeVersion = Val},
+ merge_ServiceChangeResParm(Parms, SCRP);
+
+merge_ServiceChangeResParm([{time_stamp, Val}|Parms], SCRP0)
+ when SCRP0#'ServiceChangeResParm'.timeStamp == asn1_NOVALUE ->
+ SCRP = SCRP0#'ServiceChangeResParm'{timeStamp = Val},
+ merge_ServiceChangeResParm(Parms, SCRP);
+
+merge_ServiceChangeResParm([{Tag, Val}|_Parms], SCRP) ->
+ Val2 =
+ case Tag of
+ address -> SCRP#'ServiceChangeResParm'.serviceChangeAddress;
+ mgc_id -> SCRP#'ServiceChangeResParm'.serviceChangeMgcId;
+ profile -> SCRP#'ServiceChangeResParm'.serviceChangeProfile;
+ version -> SCRP#'ServiceChangeResParm'.serviceChangeVersion;
+ time_stamp -> SCRP#'ServiceChangeResParm'.timeStamp
+ end,
+ exit({at_most_once_servChgReplyParm, {Tag, Val, Val2}}).
+
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{ensure_serviceChangeMethod,1}]}).
+-endif.
+ensure_serviceChangeMethod(Token) ->
+ case Token of
+ {safeToken, _Line, "fl"} ->
+ failover;
+ {safeToken, _Line, "failover"} ->
+ failover;
+ {safeToken, _Line, "fo"} ->
+ forced;
+ {safeToken, _Line, "forced"} ->
+ forced;
+ {safeToken, _Line, "gr"} ->
+ graceful;
+ {safeToken, _Line, "graceful"} ->
+ graceful;
+ {safeToken, _Line, "rs"} ->
+ restart;
+ {safeToken, _Line, "restart"} ->
+ restart;
+ {safeToken, _Line, "dc"} ->
+ disconnected;
+ {safeToken, _Line, "disconnected"} ->
+ disconnected;
+ {safeToken, _Line, "ho"} ->
+ handOff;
+ {safeToken, _Line, "handoff"} ->
+ handOff;
+ {safeToken, Line, Text} ->
+ return_error(Line, {bad_serviceChangeMethod, Text})
+ end.
+
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{ensure_profile,1}]}).
+-endif.
+ensure_profile(Token) ->
+ {_TokenTag, Line, Text} = Token,
+ case string:tokens(Text, [$/]) of
+ [Name, Version] ->
+ Version2 = ensure_version(Version),
+ #'ServiceChangeProfile'{profileName = Name, version = Version2};
+ _ ->
+ return_error(Line, {bad_profile, Text})
+ end.
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{ensure_version,1}]}).
+-endif.
+ensure_version(Version) ->
+ ensure_uint(Version, 0, 99).
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{merge_signalRequest,2}]}).
+-endif.
+merge_signalRequest(SignalName, PropertyParms) ->
+ Sig = #'Signal'{signalName = SignalName},
+ SPL = [],
+ do_merge_signalRequest(Sig, PropertyParms, SPL).
+
+do_merge_signalRequest(Sig, [H | T], SPL) ->
+ case H of
+ {stream, SID} when Sig#'Signal'.streamID == asn1_NOVALUE ->
+ do_merge_signalRequest(Sig#'Signal'{streamID = SID}, T, SPL);
+ {signal_type, SigType} when Sig#'Signal'.sigType == asn1_NOVALUE ->
+ do_merge_signalRequest(Sig#'Signal'{sigType = SigType}, T, SPL);
+ {duration, Duration} when Sig#'Signal'.duration == asn1_NOVALUE ->
+ do_merge_signalRequest(Sig#'Signal'{duration = Duration}, T, SPL);
+ {notify_completion, NC} when Sig#'Signal'.notifyCompletion == asn1_NOVALUE ->
+ do_merge_signalRequest(Sig#'Signal'{notifyCompletion = NC}, T, SPL);
+ keepActive when Sig#'Signal'.keepActive == asn1_NOVALUE->
+ do_merge_signalRequest(Sig#'Signal'{keepActive = true}, T, SPL);
+ {other, Name, PP} ->
+ SP = #'SigParameter'{sigParameterName = Name,
+ value = PP#'PropertyParm'.value,
+ extraInfo = PP#'PropertyParm'.extraInfo},
+ do_merge_signalRequest(Sig, T, [SP | SPL]);
+ {direction, Dir} when Sig#'Signal'.direction == asn1_NOVALUE->
+ do_merge_signalRequest(Sig#'Signal'{direction = Dir}, T, SPL);
+ {requestId, RID} when Sig#'Signal'.requestID == asn1_NOVALUE->
+ do_merge_signalRequest(Sig#'Signal'{requestID = RID}, T, SPL);
+ _ ->
+ return_error(0, {bad_sigParm, H})
+ end;
+do_merge_signalRequest(Sig, [], SPL) ->
+ Sig#'Signal'{sigParList = lists:reverse(SPL)} .
+
+
+%% eventStream = StreamToken EQUAL StreamID
+%% eventOther = eventParameterName parmValue
+-ifdef(megaco_parser_inline).
+-compile({inline,[{select_stream_or_other,2}]}).
+-endif.
+select_stream_or_other(EventParameterName, ParmValue) ->
+ if
+ (EventParameterName =:= "st") orelse
+ (EventParameterName =:= "stream") ->
+ case ParmValue of
+ #'PropertyParm'{value = [Value]} ->
+ {stream, ensure_uint16(Value)};
+ _ ->
+ {stream, ensure_uint16(ParmValue)}
+ end;
+ true ->
+ #'PropertyParm'{value = Value} = ParmValue,
+ EP = #'EventParameter'{eventParameterName = EventParameterName,
+ value = Value},
+ {other, EP}
+ end.
+
+%% select_stream_or_other("st", #'PropertyParm'{value = [Value]}) ->
+%% {stream, ensure_uint16(Value)};
+%% select_stream_or_other("st", Value) ->
+%% {stream, ensure_uint16(Value)};
+%% select_stream_or_other("stream", #'PropertyParm'{value = [Value]}) ->
+%% {stream, ensure_uint16(Value)};
+%% select_stream_or_other("stream", Value) ->
+%% {stream, ensure_uint16(Value)};
+%% select_stream_or_other(Name, #'PropertyParm'{value = Value}) ->
+%% EP = #'EventParameter'{eventParameterName = Name,
+%% value = Value},
+%% {other, EP}.
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{ensure_eventDM,1}]}).
+-endif.
+ensure_eventDM(Token) ->
+ {_TokenTag, Line, DMD} = Token,
+ if
+ is_record(DMD, 'DigitMapDescriptor') ->
+ Name = DMD#'DigitMapDescriptor'.digitMapName,
+ Val = DMD#'DigitMapDescriptor'.digitMapValue,
+ if
+ (Name =:= asn1_NOVALUE) andalso (Val =/= asn1_NOVALUE) ->
+ {'DigitMapValue', Start, Short, Long, Duration, Body} = Val,
+ DMV = #'DigitMapValue'{startTimer = Start,
+ shortTimer = Short,
+ longTimer = Long,
+ digitMapBody = Body,
+ durationTimer = Duration},
+ {eventDM, {digitMapValue, DMV}};
+ (Name =/= asn1_NOVALUE) andalso (Val =:= asn1_NOVALUE) ->
+ {eventDM, {digitMapName, Name}};
+ true ->
+ return_error(Line, {bad_eventDM, DMD})
+ end;
+ true ->
+ return_error(Line, {bad_eventDM, DMD})
+ end.
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{ensure_DMD,1}]}).
+-endif.
+ensure_DMD(Token) ->
+ {_TokenTag, Line, DMD} = Token,
+ if
+ is_record(DMD, 'DigitMapDescriptor') ->
+ Val2 =
+ case DMD#'DigitMapDescriptor'.digitMapValue of
+ %% Note that the values of the digitMapBody and
+ %% durationTimers are swapped by the scanner
+ %% (this is done because of a problem in the flex scanner).
+ #'DigitMapValue'{startTimer = asn1_NOVALUE,
+ shortTimer = asn1_NOVALUE,
+ longTimer = asn1_NOVALUE,
+ durationTimer = [],
+ digitMapBody = asn1_NOVALUE} ->
+ asn1_NOVALUE;
+ #'DigitMapValue'{durationTimer = Body,
+ digitMapBody = Duration} = DMV ->
+ %% Convert to version 1 DigitMapValue
+ DMV#'DigitMapValue'{digitMapBody = Body,
+ durationTimer = Duration};
+ Other ->
+ Other
+ end,
+ DMD#'DigitMapDescriptor'{digitMapValue = Val2};
+ true ->
+ return_error(Line, {bad_DigitMapDescriptor, DMD})
+ end.
+
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{merge_observed_event,3}]}).
+-endif.
+merge_observed_event(ObservedEvents, EventName, TimeStamp) ->
+ StreamId = asn1_NOVALUE,
+ EPL = [],
+ do_merge_observed_event(ObservedEvents, EventName, TimeStamp, StreamId, EPL).
+
+do_merge_observed_event([{stream, StreamID} | T], EventName, TimeStamp, asn1_NOVALUE, EPL) ->
+ do_merge_observed_event(T, EventName, TimeStamp, StreamID, EPL);
+do_merge_observed_event([{other, PP} | T], EventName, TimeStamp, StreamID, EPL) ->
+ do_merge_observed_event(T, EventName, TimeStamp, StreamID, [PP | EPL]);
+do_merge_observed_event([], EventName, TimeStamp, StreamID, EPL) ->
+ #'ObservedEvent'{eventName = EventName,
+ timeNotation = TimeStamp,
+ streamID = StreamID,
+ eventParList = lists:reverse(EPL)}.
+
+merge_eventSpec(OE)
+ when is_record(OE, 'ObservedEvent') andalso
+ (OE#'ObservedEvent'.timeNotation == asn1_NOVALUE) ->
+ #'EventSpec'{eventName = OE#'ObservedEvent'.eventName,
+ streamID = OE#'ObservedEvent'.streamID,
+ eventParList = OE#'ObservedEvent'.eventParList};
+merge_eventSpec(OE) ->
+ return_error(0, {bad_event_spec, OE}).
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{merge_eventParameters,1}]}).
+-endif.
+merge_eventParameters(Params) ->
+ StreamId = asn1_NOVALUE,
+ EPL = [],
+ RA = #'RequestedActions'{},
+ HasA = no,
+ do_merge_eventParameters(Params, StreamId, EPL, RA, HasA) .
+
+do_merge_eventParameters([H | T], StreamId, EPL, RA, HasA) ->
+ case H of
+ keepActive when RA#'RequestedActions'.keepActive =:= asn1_NOVALUE ->
+ RA2 = RA#'RequestedActions'{keepActive = true},
+ do_merge_eventParameters(T, StreamId, EPL, RA2, yes);
+ {embed, SD, SED} when RA#'RequestedActions'.signalsDescriptor =:= asn1_NOVALUE ->
+ RA2 = RA#'RequestedActions'{signalsDescriptor = SD,
+ secondEvent = SED},
+ do_merge_eventParameters(T, StreamId, EPL, RA2, yes);
+ {eventDM, DM} when RA#'RequestedActions'.eventDM =:= asn1_NOVALUE ->
+ RA2 = RA#'RequestedActions'{eventDM = DM},
+ do_merge_eventParameters(T, StreamId, EPL, RA2, yes);
+ {stream, NewStreamId} when StreamId =:= asn1_NOVALUE ->
+ do_merge_eventParameters(T, NewStreamId, EPL, RA, HasA);
+ {other, PP} when is_record(PP, 'PropertyParm') ->
+ EP = #'EventParameter'{eventParameterName = PP#'PropertyParm'.name,
+ value = PP#'PropertyParm'.value,
+ extraInfo = PP#'PropertyParm'.extraInfo},
+ do_merge_eventParameters(T, StreamId, [EP | EPL], RA, HasA);
+ {other, EP} when is_record(EP, 'EventParameter') ->
+ do_merge_eventParameters(T, StreamId, [EP | EPL], RA, HasA);
+ _ ->
+ return_error(0, {bad_eventParameter, H})
+ end;
+do_merge_eventParameters([], StreamId, EPL, RA, yes) ->
+ #'RequestedEvent'{streamID = StreamId,
+ eventAction = RA,
+ evParList = lists:reverse(EPL)};
+do_merge_eventParameters([], StreamId, EPL, _RA, no) ->
+ #'RequestedEvent'{streamID = StreamId,
+ eventAction = asn1_NOVALUE,
+ evParList = lists:reverse(EPL)}.
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{merge_secondEventParameters,1}]}).
+-endif.
+merge_secondEventParameters(Params) ->
+ StreamId = asn1_NOVALUE,
+ EPL = [],
+ SRA = #'SecondRequestedActions'{},
+ HasA = no,
+ do_merge_secondEventParameters(Params, StreamId, EPL, SRA, HasA) .
+
+do_merge_secondEventParameters([H | T], StreamId, EPL, SRA, HasA) ->
+ case H of
+ keepActive when SRA#'SecondRequestedActions'.keepActive =:= asn1_NOVALUE ->
+ SRA2 = SRA#'SecondRequestedActions'{keepActive = true},
+ do_merge_secondEventParameters(T, StreamId, EPL, SRA2, yes);
+ {second_embed, SD} when SRA#'SecondRequestedActions'.signalsDescriptor =:= asn1_NOVALUE ->
+ SRA2 = SRA#'SecondRequestedActions'{signalsDescriptor = SD},
+ do_merge_secondEventParameters(T, StreamId, EPL, SRA2, yes);
+ {eventDM, DM} when SRA#'SecondRequestedActions'.eventDM =:= asn1_NOVALUE ->
+ SRA2 = SRA#'SecondRequestedActions'{eventDM = DM},
+ do_merge_secondEventParameters(T, StreamId, EPL, SRA2, yes);
+ {stream, NewStreamId} when StreamId =:= asn1_NOVALUE ->
+ do_merge_secondEventParameters(T, NewStreamId, EPL, SRA, HasA);
+ {other, PP} when is_record(PP, 'PropertyParm') ->
+ EP = #'EventParameter'{eventParameterName = PP#'PropertyParm'.name,
+ value = PP#'PropertyParm'.value,
+ extraInfo = PP#'PropertyParm'.extraInfo},
+ do_merge_secondEventParameters(T, StreamId, [EP | EPL], SRA, HasA);
+ {other, EP} when is_record(EP, 'EventParameter') ->
+ do_merge_secondEventParameters(T, StreamId, [EP | EPL], SRA, HasA);
+ _ ->
+ return_error(0, {bad_secondEventParameter, H})
+ end;
+do_merge_secondEventParameters([], StreamId, EPL, SRA, yes) ->
+ #'SecondRequestedEvent'{streamID = StreamId,
+ eventAction = SRA,
+ evParList = lists:reverse(EPL)};
+do_merge_secondEventParameters([], StreamId, EPL, _SRA, no) ->
+ #'SecondRequestedEvent'{streamID = StreamId,
+ eventAction = asn1_NOVALUE,
+ evParList = lists:reverse(EPL)}.
+
+%% terminationID = "ROOT" / pathName / "$" / "*"
+%% Total length of pathName must not exceed 64 chars.
+%% pathName = ["*"] NAME *("/" / "*"/ ALPHA / DIGIT /"_" / "$" )
+%% ["@" pathDomainName ]
+%% ABNF allows two or more consecutive "." although it is meaningless
+%% in a path domain name.
+%% pathDomainName = (ALPHA / DIGIT / "*" )
+%% *63(ALPHA / DIGIT / "-" / "*" / ".")
+-ifdef(megaco_parser_inline).
+-compile({inline,[{ensure_terminationID,1}]}).
+-endif.
+ensure_terminationID(Token) ->
+ {safeToken, _Line, LowerText} = Token,
+ %% terminationID = "ROOT" / pathName / "$" / "*"
+ decode_term_id(LowerText, false, [], []).
+
+decode_term_id([H | T], Wild, Id, Component) ->
+ case H of
+ $/ -> decode_term_id(T, Wild, [lists:reverse(Component) | Id], []);
+ $* -> decode_term_id(T, true, Id, [?megaco_all | Component]);
+ $$ -> decode_term_id(T, true, Id, [?megaco_choose | Component]);
+ _ -> decode_term_id(T, Wild, Id, [H | Component])
+ end;
+decode_term_id([], Wild, Id, Component) ->
+ Id2 = [lists:reverse(Component) | Id],
+ #megaco_term_id{contains_wildcards = Wild, id = lists:reverse(Id2)}.
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{ensure_pathName,1}]}).
+-endif.
+ensure_pathName(Token) ->
+ {_TokenTag, _Line, Text} = Token,
+ Text. %% BUGBUG: ensure values
+
+%% TimeStamp = Date "T" Time ; per ISO 8601:1988
+%% Date = 8(DIGIT) ; Date = yyyymmdd
+%% Time = 8(DIGIT) ; Time = hhmmssss
+-ifdef(megaco_parser_inline).
+-compile({inline,[{ensure_timeStamp,1}]}).
+-endif.
+ensure_timeStamp(Token) ->
+ {'TimeStampToken', Line, Text} = Token,
+ case string:tokens(Text, [$T, $t]) of
+ [Date, Time] ->
+ #'TimeNotation'{date = Date, time = Time};
+ _ ->
+ return_error(Line, {bad_timeStamp, Text})
+ end.
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{ensure_transactionID,1}]}).
+-endif.
+ensure_transactionID(TransId) ->
+ ensure_uint32(TransId).
+
+%% transactionAck = transactionID / (transactionID "-" transactionID)
+-ifdef(megaco_parser_inline).
+-compile({inline,[{ensure_transactionAck,1}]}).
+-endif.
+ensure_transactionAck(Tokens) ->
+ {safeToken, _Line, Text} = Tokens,
+ ensure_transactionAck2(Text, []).
+
+ensure_transactionAck2([], Acc) ->
+ Id = lists:reverse(Acc),
+ #'TransactionAck'{firstAck = ensure_transactionID(Id)};
+ensure_transactionAck2([$- | Id2], Acc) ->
+ Id1 = lists:reverse(Acc),
+ #'TransactionAck'{firstAck = ensure_transactionID(Id1),
+ lastAck = ensure_transactionID(Id2)};
+ensure_transactionAck2([H|T], Acc) ->
+ ensure_transactionAck2(T, [H|Acc]).
+
+
+merge_context_request(asn1_NOVALUE, Prop) ->
+ merge_context_request(#'ContextRequest'{}, Prop);
+
+merge_context_request(#'ContextRequest'{priority = asn1_NOVALUE} = CR,
+ {priority, Int}) ->
+ CR#'ContextRequest'{priority = Int};
+
+merge_context_request(#'ContextRequest'{emergency = asn1_NOVALUE} = CR,
+ {emergency, Bool}) ->
+ CR#'ContextRequest'{emergency = Bool};
+
+merge_context_request(#'ContextRequest'{topologyReq = asn1_NOVALUE} = CR,
+ {topology, Desc}) ->
+ CR#'ContextRequest'{topologyReq = Desc};
+
+merge_context_request(#'ContextRequest'{iepscallind = asn1_NOVALUE} = CR,
+ {iepsCallind, Ind}) ->
+ CR#'ContextRequest'{iepscallind = Ind};
+
+merge_context_request(#'ContextRequest'{contextProp = asn1_NOVALUE} = CR,
+ {contextProp, Props}) ->
+ CR#'ContextRequest'{contextProp = Props};
+
+%% The parser handles this, but we drop it in this version
+%% merge_context_request(#'ContextRequest'{contextList = asn1_NOVALUE} = CR,
+%% {contextList, _IDs}) ->
+%% CR#'ContextRequest'{contextList = IDs};
+merge_context_request(CR, {contextList, _IDs}) ->
+ CR;
+
+merge_context_request(CR, {Tag, Val}) ->
+ Val2 =
+ case Tag of
+ priority -> CR#'ContextRequest'.priority;
+ emergency -> CR#'ContextRequest'.emergency;
+ topology -> CR#'ContextRequest'.topologyReq;
+ iepsCallind -> CR#'ContextRequest'.iepscallind;
+ contextProp -> CR#'ContextRequest'.contextProp%% ;
+ %% contextList -> CR#'ContextRequest'.contextList
+ end,
+ exit({at_most_once_contextProperty, {Tag, Val, Val2}}).
+
+
+merge_context_attr_audit_request(CAAR, []) ->
+ CAAR;
+merge_context_attr_audit_request(CAAR, [H|T]) ->
+ case H of
+ priorityAudit when CAAR#'ContextAttrAuditRequest'.priority == asn1_NOVALUE ->
+ CAAR2 = CAAR#'ContextAttrAuditRequest'{priority = 'NULL'},
+ merge_context_attr_audit_request(CAAR2, T);
+
+ emergencyAudit when CAAR#'ContextAttrAuditRequest'.emergency == asn1_NOVALUE ->
+ CAAR2 = CAAR#'ContextAttrAuditRequest'{emergency = 'NULL'},
+ merge_context_attr_audit_request(CAAR2, T);
+
+ topologyAudit when CAAR#'ContextAttrAuditRequest'.topology == asn1_NOVALUE ->
+ CAAR2 = CAAR#'ContextAttrAuditRequest'{topology = 'NULL'},
+ merge_context_attr_audit_request(CAAR2, T);
+
+ iepsCallind when CAAR#'ContextAttrAuditRequest'.iepscallind == asn1_NOVALUE ->
+ CAAR2 = CAAR#'ContextAttrAuditRequest'{iepscallind = 'NULL'},
+ merge_context_attr_audit_request(CAAR2, T);
+
+ {prop, Name} when CAAR#'ContextAttrAuditRequest'.contextPropAud == asn1_NOVALUE ->
+ CPA = [#'IndAudPropertyParm'{name = Name}],
+ CAAR2 = CAAR#'ContextAttrAuditRequest'{contextPropAud = CPA},
+ merge_context_attr_audit_request(CAAR2, T);
+
+ {prop, Name} ->
+ CPA = CAAR#'ContextAttrAuditRequest'.contextPropAud,
+ CPA2 = [#'IndAudPropertyParm'{name = Name}|CPA],
+ CAAR2 = CAAR#'ContextAttrAuditRequest'{contextPropAud = CPA2},
+ merge_context_attr_audit_request(CAAR2, T)
+
+ end.
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{merge_action_request,2}]}).
+-endif.
+merge_action_request(CtxId, Items) ->
+ do_merge_action_request(Items, [], asn1_NOVALUE, asn1_NOVALUE, CtxId).
+
+do_merge_action_request([H|T], CmdReqs, CtxReq, CtxAuditReq, CtxId) ->
+ case H of
+ {commandRequest, CmdReq} ->
+ do_merge_action_request(T, [CmdReq|CmdReqs],
+ CtxReq, CtxAuditReq, CtxId);
+
+ {contextProp, ContextProp} ->
+ do_merge_action_request(T, CmdReqs,
+ merge_context_request(CtxReq, ContextProp),
+ CtxAuditReq, CtxId);
+
+ {contextAudit, ContextAuditReq} when CtxAuditReq == asn1_NOVALUE ->
+ do_merge_action_request(T, CmdReqs,
+ CtxReq, ContextAuditReq, CtxId)
+ end;
+do_merge_action_request([], CmdReqs, CtxReq, CtxAuditReq, CtxId) ->
+ #'ActionRequest'{contextId = CtxId,
+ contextRequest = strip_ContextRequest(CtxReq),
+ contextAttrAuditReq = strip_ContextAttrAuditRequest(CtxAuditReq),
+ commandRequests = lists:reverse(CmdReqs)}.
+
+
+%% OTP-5085:
+%% In order to solve a problem in the parser, the error descriptor
+%% has been put last in the non-empty commandReplyList, if it is not
+%% asn1_NOVALUE
+-ifdef(megaco_parser_inline).
+-compile({inline,[{merge_action_reply,1}]}).
+-endif.
+merge_action_reply(Items) ->
+ do_merge_action_reply(Items, asn1_NOVALUE, asn1_NOVALUE, []).
+
+do_merge_action_reply([], Err, Ctx, Cmds) ->
+ #'ActionReply'{errorDescriptor = Err,
+ contextReply = strip_ContextRequest(Ctx),
+ commandReply = lists:reverse(Cmds)};
+do_merge_action_reply([H|T], Err0, CR, Cmds) ->
+ case H of
+ {error, Err1} when Err0 == asn1_NOVALUE ->
+ do_merge_action_reply(T, Err1, CR, Cmds);
+ {command, Cmd} ->
+ do_merge_action_reply(T, Err0, CR, [Cmd | Cmds]);
+ {context, CtxProp} ->
+ do_merge_action_reply(T, Err0,
+ merge_context_request(CR, CtxProp), Cmds)
+ end.
+
+strip_ContextRequest(#'ContextRequest'{priority = asn1_NOVALUE,
+ emergency = asn1_NOVALUE,
+ topologyReq = asn1_NOVALUE,
+ iepscallind = asn1_NOVALUE,
+ contextProp = asn1_NOVALUE}) ->
+ asn1_NOVALUE;
+strip_ContextRequest(#'ContextRequest'{priority = asn1_NOVALUE,
+ emergency = asn1_NOVALUE,
+ topologyReq = asn1_NOVALUE,
+ iepscallind = asn1_NOVALUE,
+ contextProp = []}) ->
+ asn1_NOVALUE;
+strip_ContextRequest(asn1_NOVALUE) ->
+ asn1_NOVALUE;
+strip_ContextRequest(R) ->
+ R.
+
+strip_ContextAttrAuditRequest(asn1_NOVALUE) ->
+ asn1_NOVALUE;
+strip_ContextAttrAuditRequest(
+ #'ContextAttrAuditRequest'{priority = asn1_NOVALUE,
+ emergency = asn1_NOVALUE,
+ topology = asn1_NOVALUE,
+ iepscallind = asn1_NOVALUE,
+ contextPropAud = asn1_NOVALUE}) ->
+ asn1_NOVALUE;
+strip_ContextAttrAuditRequest(
+ #'ContextAttrAuditRequest'{priority = asn1_NOVALUE,
+ emergency = asn1_NOVALUE,
+ topology = asn1_NOVALUE,
+ iepscallind = asn1_NOVALUE,
+ contextPropAud = []}) ->
+ asn1_NOVALUE;
+strip_ContextAttrAuditRequest(R) ->
+ R.
+
+merge_AmmRequest_descriptors([], Acc) ->
+ lists:reverse(Acc);
+merge_AmmRequest_descriptors([{_, deprecated}|Descs], Acc) ->
+ merge_AmmRequest_descriptors(Descs, Acc);
+merge_AmmRequest_descriptors([Desc|Descs], Acc) ->
+ merge_AmmRequest_descriptors(Descs, [Desc|Acc]).
+
+make_commandRequest({CmdTag, {_TokenTag, _Line, Text}}, Cmd) ->
+ Req = #'CommandRequest'{command = {CmdTag, Cmd}},
+ case Text of
+ [$w, $- | _] ->
+ Req#'CommandRequest'{wildcardReturn = 'NULL'};
+ [$o, $-, $w, $- | _] ->
+ Req#'CommandRequest'{optional = 'NULL', wildcardReturn = 'NULL'};
+ [$o, $- | _] ->
+ Req#'CommandRequest'{optional = 'NULL'};
+ _ ->
+ Req
+ end.
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{merge_terminationAudit,1}]}).
+-endif.
+merge_terminationAudit(AuditReturnParameters) ->
+ lists:reverse(do_merge_terminationAudit(AuditReturnParameters, [], [])).
+
+do_merge_terminationAudit([H| T], ARPs, AuditItems) ->
+ case H of
+ {auditReturnItem, AuditItem} ->
+ do_merge_terminationAudit(T, ARPs, [AuditItem | AuditItems]);
+ AuditReturnParameter ->
+ do_merge_terminationAudit(T, [AuditReturnParameter | ARPs], AuditItems)
+ end;
+do_merge_terminationAudit([], AuditReturnParameters, []) ->
+ AuditReturnParameters;
+do_merge_terminationAudit([], AuditReturnParameters, AuditItems) ->
+ AuditDescriptor = #'AuditDescriptor'{auditToken = AuditItems},
+ AuditReturnParameter = {emptyDescriptors, AuditDescriptor},
+ [AuditReturnParameter | AuditReturnParameters].
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{merge_mediaDescriptor,1}]}).
+-endif.
+merge_mediaDescriptor(MediaParms) ->
+ do_merge_mediaDescriptor(MediaParms, asn1_NOVALUE, [], []).
+
+do_merge_mediaDescriptor([H | T], TS, One, Multi) ->
+ case H of
+ {streamParm, Parm} when Multi =:= [] ->
+ do_merge_mediaDescriptor(T, TS, [Parm | One], Multi);
+ {streamDescriptor, Desc} when One =:= [] ->
+ do_merge_mediaDescriptor(T, TS, One, [Desc | Multi]);
+ {termState, TS2} when TS =:= asn1_NOVALUE ->
+ do_merge_mediaDescriptor(T, TS2, One, Multi);
+ _ ->
+ return_error(0, {bad_merge_mediaDescriptor, [H, TS, One, Multi]})
+ end;
+do_merge_mediaDescriptor([], TS, One, Multi) ->
+ if
+ (One =:= []) ->
+ if (Multi =:= []) ->
+ #'MediaDescriptor'{streams = asn1_NOVALUE,
+ termStateDescr = TS};
+ true -> % (Multi =/= [])
+ Streams = {multiStream, lists:reverse(Multi)},
+ #'MediaDescriptor'{streams = Streams,
+ termStateDescr = TS}
+ end;
+ true -> % (One =/= [])
+ if
+ (Multi =:= []) ->
+ Streams = {oneStream, merge_streamParms(One)},
+ #'MediaDescriptor'{streams = Streams,
+ termStateDescr = TS};
+ true -> % (Multi =/= [])
+ return_error(0,
+ {bad_merge_mediaDescriptor, [TS, One, Multi]})
+ end
+ end.
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{merge_streamParms,1}]}).
+-endif.
+merge_streamParms(TaggedStreamParms) ->
+ SP = #'StreamParms'{},
+ do_merge_streamParms(TaggedStreamParms, SP).
+
+do_merge_streamParms([{Tag, D} | T] = All, SP) ->
+ case Tag of
+ local when SP#'StreamParms'.localDescriptor =:= asn1_NOVALUE ->
+ do_merge_streamParms(T, SP#'StreamParms'{localDescriptor = D});
+ remote when SP#'StreamParms'.remoteDescriptor =:= asn1_NOVALUE ->
+ do_merge_streamParms(T, SP#'StreamParms'{remoteDescriptor = D});
+ control ->
+ LCD =
+ case SP#'StreamParms'.localControlDescriptor of
+ asn1_NOVALUE ->
+ #'LocalControlDescriptor'{propertyParms = []};
+ PrevLCD ->
+ PrevLCD
+ end,
+ LCD2 = do_merge_control_streamParms(D, LCD),
+ do_merge_streamParms(T, SP#'StreamParms'{localControlDescriptor = LCD2});
+ statistics when SP#'StreamParms'.statisticsDescriptor =:= asn1_NOVALUE ->
+ do_merge_streamParms(T, SP#'StreamParms'{statisticsDescriptor = D});
+ _ ->
+ return_error(0, {do_merge_streamParms, [All, SP]})
+ end;
+do_merge_streamParms([], SP)
+ when is_record(SP#'StreamParms'.localControlDescriptor,
+ 'LocalControlDescriptor') ->
+ LCD = SP#'StreamParms'.localControlDescriptor,
+ PP = LCD#'LocalControlDescriptor'.propertyParms,
+ LCD2 = LCD#'LocalControlDescriptor'{propertyParms = lists:reverse(PP)},
+ SP#'StreamParms'{localControlDescriptor = LCD2};
+do_merge_streamParms([], SP) ->
+ SP.
+
+
+do_merge_control_streamParms([{SubTag, SD} | T] = All, LCD) ->
+ case SubTag of
+ group when LCD#'LocalControlDescriptor'.reserveGroup =:= asn1_NOVALUE ->
+ LCD2 = LCD#'LocalControlDescriptor'{reserveGroup = SD},
+ do_merge_control_streamParms(T, LCD2);
+ value when LCD#'LocalControlDescriptor'.reserveValue =:= asn1_NOVALUE ->
+ LCD2 = LCD#'LocalControlDescriptor'{reserveValue = SD},
+ do_merge_control_streamParms(T, LCD2);
+ mode when LCD#'LocalControlDescriptor'.streamMode =:= asn1_NOVALUE ->
+ LCD2 = LCD#'LocalControlDescriptor'{streamMode = SD},
+ do_merge_control_streamParms(T, LCD2);
+ prop ->
+ PP = LCD#'LocalControlDescriptor'.propertyParms,
+ LCD2 = LCD#'LocalControlDescriptor'{propertyParms = [SD | PP]},
+ do_merge_control_streamParms(T, LCD2);
+ _ ->
+ return_error(0, {do_merge_control_streamParms, [All, LCD]})
+ end;
+do_merge_control_streamParms([], LCD) ->
+ LCD.
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{merge_terminationStateDescriptor,1}]}).
+-endif.
+merge_terminationStateDescriptor(Parms) ->
+ TSD = #'TerminationStateDescriptor'{propertyParms = []},
+ do_merge_terminationStateDescriptor(Parms, TSD).
+
+do_merge_terminationStateDescriptor([{Tag, Val} | T], TSD) ->
+ case Tag of
+ serviceState when TSD#'TerminationStateDescriptor'.serviceState =:= asn1_NOVALUE ->
+ TSD2 = TSD#'TerminationStateDescriptor'{serviceState = Val},
+ do_merge_terminationStateDescriptor(T, TSD2);
+ eventBufferControl when TSD#'TerminationStateDescriptor'.eventBufferControl =:= asn1_NOVALUE->
+ TSD2 = TSD#'TerminationStateDescriptor'{eventBufferControl = Val},
+ do_merge_terminationStateDescriptor(T, TSD2);
+ propertyParm ->
+ PP = TSD#'TerminationStateDescriptor'.propertyParms,
+ TSD2 = TSD#'TerminationStateDescriptor'{propertyParms = [Val | PP]},
+ do_merge_terminationStateDescriptor(T, TSD2)
+ end;
+do_merge_terminationStateDescriptor([], TSD) ->
+ PP = TSD#'TerminationStateDescriptor'.propertyParms,
+ TSD#'TerminationStateDescriptor'{propertyParms = lists:reverse(PP)}.
+
+
+-ifdef(megaco_nscanner_props).
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{ensure_prop_groups,1}]}).
+-endif.
+ensure_prop_groups(Token) ->
+ {_TokenTag, _Line, Text} = Token,
+ Group = [],
+ parse_prop_name(Text, Group).
+
+parse_prop_name([Char | Rest] = All, Group) ->
+ if
+ ?white_space(Char) ->
+ parse_prop_name(Rest, Group);
+ ?end_of_line(Char) ->
+ parse_prop_name(Rest, Group);
+ true ->
+ Name = [],
+ do_parse_prop_name(All, Name, Group)
+ end;
+parse_prop_name([] = All, Group) ->
+ Name = [],
+ do_parse_prop_name(All, Name, Group).
+
+do_parse_prop_name([Char | Rest], Name, Group)
+ when (Char =:= $=) andalso (Name =/= []) ->
+ %% Now we have a complete name
+ if
+ (Name =:= "v") andalso (Group =/= []) ->
+ %% v= is a property group delimiter,
+ %% lets create yet another property group.
+ NewGroup = [],
+ [lists:reverse(Group) | parse_prop_value(Rest, Name, NewGroup)];
+ true ->
+ %% Use current property group
+ parse_prop_value(Rest, Name, Group)
+ end;
+do_parse_prop_name([Char | Rest], Name, Group) ->
+ case ?classify_char4(Char) of
+ safe_char_upper ->
+ do_parse_prop_name(Rest, [Char | Name], Group);
+ safe_char ->
+ do_parse_prop_name(Rest, [Char | Name], Group);
+ _ ->
+ return_error(0, {bad_prop_name, lists:reverse(Name), Char})
+ end;
+do_parse_prop_name([], [], []) ->
+ [];
+do_parse_prop_name([], [], Group) ->
+ [lists:reverse(Group)];
+do_parse_prop_name([], Name, Group) when Name =/= [] ->
+ %% Assume end of line
+ Value = [],
+ PP = make_prop_parm(Name, Value),
+ Group2 = lists:reverse([PP | Group]),
+ [Group2].
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{parse_prop_value,3}]}).
+-endif.
+parse_prop_value(Chars, Name, Group) ->
+ Value = [],
+ do_parse_prop_value(Chars, Name, Value, Group).
+
+do_parse_prop_value([Char | Rest], Name, Value, Group) ->
+ if
+ ?end_of_line(Char) ->
+ %% Now we have a complete "name=value" pair
+ PP = make_prop_parm(Name, Value),
+ parse_prop_name(Rest, [PP | Group]);
+ true ->
+ do_parse_prop_value(Rest, Name, [Char | Value], Group)
+ end;
+do_parse_prop_value([], Name, Value, Group) ->
+ %% Assume end of line
+ PP = make_prop_parm(Name, Value),
+ Group2 = lists:reverse([PP | Group]),
+ [Group2].
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{make_prop_parm,2}]}).
+-endif.
+make_prop_parm(Name, Value) ->
+ #'PropertyParm'{name = lists:reverse(Name),
+ value = [lists:reverse(Value)]}.
+
+-else. % -ifdef(megaco_nscanner_props).
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{ensure_prop_groups,1}]}).
+-endif.
+ensure_prop_groups(Token) ->
+ {_TokenTag, _Line, Groups} = Token,
+ Groups.
+
+%% do_ensure_prop_groups(Groups) when is_list(Groups) ->
+%% [ensure_prop_group(Group) || Group <- Groups];
+%% do_ensure_prop_groups(BadGroups) ->
+%% throw({error, {?MODULE, {bad_property_groups, BadGroups}}}).
+
+%% -ifdef(megaco_parser_inline).
+%% -compile({inline,[{ensure_prop_group,1}]}).
+%% -endif.
+%% ensure_prop_group(Group) when is_list(Group) ->
+%% [ensure_prop_parm(PropParm) || PropParm <- Group];
+%% ensure_prop_group(BadGroup) ->
+%% throw({error, {?MODULE, {bad_property_group, BadGroup}}}).
+
+%% -ifdef(megaco_parser_inline).
+%% -compile({inline,[{ensure_prop_parm,1}]}).
+%% -endif.
+%% ensure_prop_parm(#property_parm{name = Name,
+%% value = Value}) ->
+%% #'PropertyParm'{name = Name,
+%% value = Value};
+%% ensure_prop_parm(PP) when is_record(PP, 'PropertyParm') ->
+%% PP;
+%% ensure_prop_parm(BadPropParm) ->
+%% throw({error, {?MODULE, {bad_property_parm, BadPropParm}}}).
+
+-endif. % -ifdef(megaco_nscanner_props).
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{ensure_uint,3}]}).
+-endif.
+ensure_uint(Token, Min, Max) ->
+ case Token of
+ {_TokenTag, Line, Val} when is_integer(Val) ->
+ ensure_uint(Val, Min, Max, Line);
+ {_TokenTag, Line, Text} ->
+ case (catch list_to_integer(Text)) of
+ {'EXIT', _} ->
+ return_error(Line, {not_an_integer, Text});
+ Val when is_integer(Val) ->
+ ensure_uint(Val, Min, Max, Line)
+ end;
+ Val when is_integer(Val) ->
+ ensure_uint(Val, Min, Max, 0);
+ Text ->
+ case (catch list_to_integer(Text)) of
+ {'EXIT', _} ->
+ return_error(0, {not_an_integer, Text});
+ Val when is_integer(Val) ->
+ ensure_uint(Val, Min, Max, 0)
+ end
+ end.
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{ensure_uint,4}]}).
+-endif.
+ensure_uint(Val, Min, Max, Line) ->
+ if
+ is_integer(Min) andalso (Val >= Min) ->
+ if
+ is_integer(Max) andalso (Val =< Max) ->
+ Val;
+ Max =:= infinity ->
+ Val;
+ true ->
+ return_error(Line, {too_large_integer, Val, Max})
+ end;
+ true ->
+ return_error(Line, {too_small_integer, Val, Min})
+ end.
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{ensure_uint16,1}]}).
+-endif.
+ensure_uint16(Int) ->
+ ensure_uint(Int, 0, 65535).
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{ensure_uint32,1}]}).
+-endif.
+ensure_uint32(Int) ->
+ ensure_uint(Int, 0, 4294967295) .
+
+%% OTP-4710
+ensure_hex({_TokenTag, _Line, [$0, $x |Chars]}, Min, Max) ->
+ ensure_uint(length(Chars), Min, Max),
+ hex_to_int(Chars, []);
+ensure_hex({_TokenTag, _Line, [$0, $X |Chars]}, Min, Max) ->
+ ensure_uint(length(Chars), Min, Max),
+ hex_to_int(Chars, []);
+ensure_hex([$0, $x |Chars], Min, Max) ->
+ ensure_uint(length(Chars), Min, Max),
+ hex_to_int(Chars, []);
+ensure_hex([$0, $X |Chars], Min, Max) ->
+ ensure_uint(length(Chars), Min, Max),
+ hex_to_int(Chars, []).
+
+%% OTP-4710
+hex_to_int([], Acc) ->
+ lists:reverse(Acc);
+hex_to_int([Char1,Char2|Tail], Acc) ->
+ Int1 = hchar_to_int(Char1),
+ Int2 = hchar_to_int(Char2),
+ Val = Int2 bor (Int1 bsl 4),
+ hex_to_int(Tail, [Val| Acc]);
+hex_to_int([Char], Acc) ->
+ Int = hchar_to_int(Char),
+ lists:reverse([Int|Acc]).
+
+hchar_to_int(Char) when ($0 =< Char) andalso (Char =< $9) ->
+ Char - $0;
+hchar_to_int(Char) when ($A =< Char) andalso (Char =< $F) ->
+ Char - $A + 10; % OTP-4710
+hchar_to_int(Char) when ($a =< Char) andalso (Char =< $f) ->
+ Char - $a + 10. % OTP-4710
+
+-ifdef(megaco_parser_inline).
+-compile({inline,[{value_of,1}]}).
+-endif.
+value_of(Token) ->
+ {_TokenTag, _Line, Text} = Token,
+ Text.
+
+
+%% -------------------------------------------------------------------
+
+%% d(F) ->
+%% d(F,[]).
+%% d(F, A) ->
+%% %% d(true, F, A).
+%% d(get(dbg), F, A).
+
+%% d(true, F, A) ->
+%% io:format("DBG:~w:" ++ F ++ "~n", [?MODULE | A]);
+%% d(_, _, _) ->
+%% ok.
+