~ubuntu-branches/debian/squeeze/erlang/squeeze

« back to all changes in this revision

Viewing changes to lib/megaco/src/text/megaco_text_parser_prev3b.hrl

  • Committer: Bazaar Package Importer
  • Author(s): Erlang Packagers, Sergei Golovan
  • Date: 2006-12-03 17:07:44 UTC
  • mfrom: (2.1.11 feisty)
  • Revision ID: james.westby@ubuntu.com-20061203170744-rghjwupacqlzs6kv
Tags: 1:11.b.2-4
[ Sergei Golovan ]
Fixed erlang-base and erlang-base-hipe prerm scripts.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
%% ``The contents of this file are subject to the Erlang Public License,
 
2
%% Version 1.1, (the "License"); you may not use this file except in
 
3
%% compliance with the License. You should have received a copy of the
 
4
%% Erlang Public License along with this software. If not, it can be
 
5
%% retrieved via the world wide web at http://www.erlang.org/.
 
6
%% 
 
7
%% Software distributed under the License is distributed on an "AS IS"
 
8
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 
9
%% the License for the specific language governing rights and limitations
 
10
%% under the License.
 
11
%% 
 
12
%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
 
13
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
 
14
%% AB. All Rights Reserved.''
 
15
%% 
 
16
%%     $Id$
 
17
%%
 
18
%%----------------------------------------------------------------------
 
19
%% Purpose : Define semantic text parser actions
 
20
%%----------------------------------------------------------------------
 
21
 
 
22
 
 
23
-include_lib("megaco/include/megaco.hrl").
 
24
-include_lib("megaco/include/megaco_message_prev3b.hrl").
 
25
-define(encoder_version_pre_prev3c,true).
 
26
-include("megaco_text_tokens.hrl").
 
27
 
 
28
make_safe_token({_TokenTag, Line, Text}) ->
 
29
    {safeToken, Line, Text}.
 
30
 
 
31
ensure_value({safeToken, _Line, Text}) ->
 
32
    ensure_value(Text);
 
33
ensure_value({'QuotedChars', _Line, Text}) ->
 
34
    ensure_value(Text);
 
35
ensure_value(Text) when list(Text) ->
 
36
    Text. %% BUGBUG: ensure length
 
37
 
 
38
%% NAME       = ALPHA *63(ALPHA / DIGIT / "_" )
 
39
ensure_NAME({_TokenTag, _Line, Text}) ->
 
40
    Text.  %% BUGBUG: ensure length and chars
 
41
 
 
42
ensure_requestID({safeToken, _Line, "*"}) ->
 
43
    ?megaco_all_request_id;
 
44
ensure_requestID(RequestId) ->
 
45
    ensure_uint32(RequestId).
 
46
 
 
47
ensure_streamID(StreamId) ->
 
48
    ensure_uint16(StreamId).
 
49
 
 
50
ensure_auth_header(SpiToken, SnToken, AdToken) ->
 
51
    Spi = ensure_hex(SpiToken, 8,  8),
 
52
    Sn  = ensure_hex(SnToken,  8,  8), 
 
53
    Ad  = ensure_hex(AdToken, 24, 64),
 
54
    #'AuthenticationHeader'{secParmIndex = Spi, seqNum = Sn, ad = Ad}.
 
55
 
 
56
%% The values 0x0, 0xFFFFFFFE and 0xFFFFFFFF are reserved.
 
57
%% ContextID         = (UINT32 / "*" / "-" / "$")
 
58
ensure_contextID({_TokenTag, Line, Text}) ->
 
59
    case Text of
 
60
        "*"  -> ?megaco_all_context_id;
 
61
        "-"  -> ?megaco_null_context_id;
 
62
        "\$" -> ?megaco_choose_context_id;
 
63
        Int  ->
 
64
            CID = ensure_uint32(Int),
 
65
            if
 
66
                (CID =/= 0) and
 
67
                (CID =/= 16#FFFFFFFE) and
 
68
                (CID =/= 16#FFFFFFFF) ->
 
69
                    CID;
 
70
                true ->
 
71
                    return_error(Line, {bad_ContextID, CID})
 
72
            end
 
73
    end.
 
74
 
 
75
ensure_domainAddress([{_T, _L, _A} = Addr0], Port) ->
 
76
    Addr = ensure_ip4addr(Addr0), 
 
77
    {ip4Address, #'IP4Address'{address = Addr, portNumber = Port}};
 
78
ensure_domainAddress([colon,colon], Port) ->
 
79
    Addr = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
 
80
    {ip6Address, #'IP6Address'{address = Addr, portNumber = Port}};
 
81
ensure_domainAddress(Addr0, Port) ->
 
82
    Addr = ensure_ip6addr(Addr0),
 
83
    {ip6Address, #'IP6Address'{address = Addr, portNumber = Port}}.
 
84
 
 
85
 
 
86
ensure_ip4addr({TokenTag, Line, Addr}) ->
 
87
    case string:tokens(Addr, [$.]) of
 
88
        [T1, T2, T3, T4] ->
 
89
            A1 = ensure_uint({TokenTag, Line, T1}, 0, 255),
 
90
            A2 = ensure_uint({TokenTag, Line, T2}, 0, 255),
 
91
            A3 = ensure_uint({TokenTag, Line, T3}, 0, 255),
 
92
            A4 = ensure_uint({TokenTag, Line, T4}, 0, 255),
 
93
            [A1, A2, A3, A4];
 
94
        _ ->
 
95
            return_error(Line, {bad_IP4address, Addr})
 
96
    end.
 
97
 
 
98
 
 
99
ensure_ip6addr([colon,colon|T]) ->
 
100
    [H1|T1] = lists:reverse(T),
 
101
    case do_ensure_ip6addr(T1, true, [ensure_hex4_or_ip4addr(H1)], 1) of
 
102
        {true, A} when length(A) == 16 ->
 
103
            A;
 
104
        {true, B} when length(B) < 16 ->
 
105
            lists:duplicate(16 - length(B), 0) ++ B;
 
106
        {true, C} ->
 
107
            throw({error, {?MODULE, {bad_mid_ip6addr_length, C}}})
 
108
    end;
 
109
ensure_ip6addr(L) ->
 
110
    case lists:reverse(L) of
 
111
        [colon, colon| T] ->
 
112
            case do_ensure_ip6addr(T, true, [], 1) of
 
113
                {true, A} when length(A) == 16 ->
 
114
                    A;
 
115
                {true, B} when length(B) < 16 ->
 
116
                    B ++ lists:duplicate(16 - length(B), 0);
 
117
                {true, C} ->
 
118
                    throw({error, {?MODULE, {bad_mid_ip6addr_length, C}}})
 
119
            end;
 
120
        [H|L1] -> % A (last element) could be an ip4 address
 
121
            case do_ensure_ip6addr(L1,false,[ensure_hex4_or_ip4addr(H)],1) of
 
122
                {false, A} when length(A) == 16 -> 
 
123
                    A;
 
124
                %% allow a pad even if the address is full (i.e. 16)
 
125
                {true, B} when length(B) =< 17 -> 
 
126
                    do_ensure_ip6addr_padding(B, 0);
 
127
                {Pad, C} ->
 
128
                    throw({error, {?MODULE, {bad_mid_ip6addr_length, Pad, C}}})
 
129
            end
 
130
 
 
131
    end.
 
132
 
 
133
 
 
134
do_ensure_ip6addr([], Pad, Acc, _) ->
 
135
    {Pad, lists:flatten(Acc)};
 
136
do_ensure_ip6addr([colon,colon|T], false, Acc, Line) ->
 
137
    do_ensure_ip6addr(T, true, [pad|Acc], Line);
 
138
do_ensure_ip6addr([colon,colon|T], true, Acc, Line) ->
 
139
    return_error(Line, {bad_mid_duplicate_padding, T, Acc});
 
140
do_ensure_ip6addr([colon|T], Pad, Acc, Line) ->
 
141
    do_ensure_ip6addr(T, Pad, Acc, Line);
 
142
do_ensure_ip6addr([{_, Line, _} = A|T], Pad, Acc, _) ->
 
143
    do_ensure_ip6addr(T, Pad, [ensure_hex4(A)|Acc], Line).
 
144
 
 
145
do_ensure_ip6addr_padding([], _) ->
 
146
    [];
 
147
do_ensure_ip6addr_padding([pad|T], N) ->
 
148
    lists:duplicate(16 - (N + length(T)), 0) ++ T;
 
149
do_ensure_ip6addr_padding([H|T], N) ->
 
150
    [H|do_ensure_ip6addr_padding(T, N+1)].
 
151
 
 
152
ensure_hex4_or_ip4addr({TokenTag, Line, Addr} = V) ->
 
153
    case string:tokens(Addr, [$.]) of
 
154
        [T1, T2, T3, T4] ->
 
155
            A1 = ensure_uint({TokenTag, Line, T1}, 0, 255),
 
156
            A2 = ensure_uint({TokenTag, Line, T2}, 0, 255),
 
157
            A3 = ensure_uint({TokenTag, Line, T3}, 0, 255),
 
158
            A4 = ensure_uint({TokenTag, Line, T4}, 0, 255),
 
159
            [A1, A2, A3, A4];
 
160
        _ ->
 
161
            ensure_hex4(V)
 
162
            %%      %% BMK BMK BMK 
 
163
            %%      %% Here we should test for hexseq
 
164
            %%      return_error(Line, {bad_IP4address, Addr})
 
165
    end.
 
166
 
 
167
ensure_hex4({_TokenTag, Line, Hex4}) 
 
168
  when length(Hex4) =< 4, length(Hex4) > 0 ->
 
169
    case (catch do_ensure_hex4(Hex4)) of
 
170
        IL when list(IL), length(IL) == 2 ->
 
171
            IL;
 
172
        Error ->
 
173
            return_error(Line, {bad_hex4, Hex4, Error})
 
174
    end.
 
175
 
 
176
do_ensure_hex4([_H1, _H2, _H3, _H4] = H) ->
 
177
    hex_to_int(H, []);
 
178
do_ensure_hex4([H2, H3, H4]) ->
 
179
    hex_to_int([$0, H2, H3, H4], []);
 
180
do_ensure_hex4([H3, H4]) ->
 
181
    hex_to_int([$0, $0, H3, H4], []);
 
182
do_ensure_hex4([H4]) ->
 
183
    hex_to_int([$0, $0, $0, H4], []).
 
184
 
 
185
ensure_domainName({_TokenTag, _Line, Name}, Port) ->
 
186
    %% BUGBUG: validate name
 
187
    {domainName, #'DomainName'{name = Name, portNumber = Port}}.
 
188
 
 
189
%% extensionParameter= "X"  ("-" / "+") 1*6(ALPHA / DIGIT)
 
190
ensure_extensionParameter({_TokenTag, Line, Text}) ->
 
191
    case Text of
 
192
        [X, S | _Chars] ->
 
193
            if
 
194
                X /= $X, X /= $x,
 
195
                S /= $+, S /= $- ->
 
196
                    return_error(Line, {bad_extension_parameter, Text});
 
197
                true ->
 
198
                    {extension_parameter, Text}
 
199
            end;
 
200
        _ ->
 
201
            return_error(Line, {bad_extension_parameter, Text})
 
202
    end.
 
203
 
 
204
ensure_message(MegacopToken,  MID, Body) ->
 
205
    #'ServiceChangeProfile'{profileName = Name,
 
206
                            version     = Version} = 
 
207
        ensure_profile(MegacopToken),
 
208
    case Name of
 
209
        "megaco" ->
 
210
            #'Message'{version = Version, mId = MID, messageBody = Body};
 
211
        [$!]  ->
 
212
            #'Message'{version = Version, mId = MID, messageBody = Body}
 
213
    end.
 
214
 
 
215
 
 
216
%% Corr1:
 
217
%% As of corr 1 ModemDescriptor has been deprecated.
 
218
%% and since this functon is only used when creating
 
219
%% a ModemDescriptor, iit is removed.
 
220
%% modemType         = (V32bisToken / V22bisToken / V18Token / 
 
221
%%                      V22Token / V32Token / V34Token / V90Token / 
 
222
%%                      V91Token / SynchISDNToken / extensionParameter)
 
223
%% ensure_modemType({_TokenTag, _Line, Text} = Token) ->
 
224
%%     case Text of
 
225
%%         "v32b"      -> v32bis;
 
226
%%         "v22b"      -> v22bis;
 
227
%%         "v18"       -> v18;
 
228
%%         "v22"       -> v22;
 
229
%%         "v32"       -> v32;
 
230
%%         "v34"       -> v34;
 
231
%%         "v90"       -> v90;
 
232
%%         "v91"       -> v91;
 
233
%%         "synchisdn" -> synchISDN;
 
234
%%         "sn"        -> synchISDN;
 
235
%%         [$x | _]               -> ensure_extensionParameter(Token)
 
236
%%     end.
 
237
 
 
238
%% An mtp address is five octets long
 
239
ensure_mtpAddress({_TokenTag, _Line, Addr}) ->
 
240
    %% BUGBUG: validate address
 
241
    {mtpAddress, Addr}.
 
242
 
 
243
%% MuxType = ( H221Token / H223Token / H226Token / V76Token / extensionParameter )
 
244
ensure_muxType({_TokenTag, _Line, Text} = Token) ->
 
245
    case Text of
 
246
        "h221"  -> h221;
 
247
        "h223"  -> h223;
 
248
        "h226"  -> h226;
 
249
        "v76"   -> v76;
 
250
        "nx64k" -> nx64k; % v2
 
251
        [$x | _]          -> ensure_extensionParameter(Token)
 
252
    end.
 
253
 
 
254
%% packagesItem      = NAME "-" UINT16
 
255
%% NAME              = ALPHA *63(ALPHA / DIGIT / "_" )
 
256
ensure_packagesItem({TokenTag, Line, Text}) ->
 
257
    case string:tokens(Text, [$-]) of
 
258
        [Name, Version] ->
 
259
            #'PackagesItem'{packageName    = ensure_NAME({TokenTag, Line, Name}),
 
260
                            packageVersion = ensure_uint({TokenTag, Line, Version}, 0, 99)};
 
261
        _ ->
 
262
            return_error(Line, {bad_PackagesItem, Text})
 
263
    end.
 
264
 
 
265
%% pkgdName          =  (PackageName / "*")  SLASH  (ItemID / "*" )
 
266
%% PackageName       = NAME
 
267
%% ItemID            = NAME
 
268
ensure_pkgdName({TokenTag, Line, Text}) ->
 
269
    case string:tokens(Text, [$/]) of
 
270
        [Name, Item] ->
 
271
            ensure_name_or_star({TokenTag, Line, Name}),
 
272
            ensure_name_or_star({TokenTag, Line, Item}),
 
273
            Text;
 
274
        _ ->
 
275
            return_error(Line, {bad_pkgdName, Text})
 
276
    end.
 
277
 
 
278
ensure_name_or_star({_, _, Name}) when Name == "*" ->
 
279
    Name;
 
280
ensure_name_or_star(Name) ->
 
281
    ensure_NAME(Name).
 
282
 
 
283
 
 
284
 
 
285
%% v2 - start
 
286
 
 
287
merge_indAudMediaDescriptor_streams(asn1_NOVALUE, []) ->
 
288
    asn1_NOVALUE;
 
289
merge_indAudMediaDescriptor_streams(Stream, [])  
 
290
  when record(Stream, 'IndAudStreamParms') ->
 
291
    {oneStream, Stream};
 
292
merge_indAudMediaDescriptor_streams(asn1_NOVALUE, MStreams) ->
 
293
    {multiStream, lists:reverse(MStreams)};
 
294
merge_indAudMediaDescriptor_streams(Stream, MStreams) ->
 
295
    return_error(0, 
 
296
                 {invalid_indAudMediaDescriptor_streams, {Stream, MStreams}}).
 
297
 
 
298
merge_indAudMediaDescriptor(Vals) ->
 
299
    merge_indAudMediaDescriptor(Vals, asn1_NOVALUE, asn1_NOVALUE, []).
 
300
 
 
301
merge_indAudMediaDescriptor([], TSD, OneStream, MultiStreams) ->
 
302
    Streams = merge_indAudMediaDescriptor_streams(OneStream, MultiStreams),
 
303
    #'IndAudMediaDescriptor'{termStateDescr = TSD,
 
304
                             streams        = Streams};
 
305
 
 
306
merge_indAudMediaDescriptor([{termStateDescr, H}|T], asn1_NOVALUE, OS, MS) ->
 
307
    merge_indAudMediaDescriptor(T, H, OS, MS);
 
308
merge_indAudMediaDescriptor([{streamDescr, Val}|T], TSD, asn1_NOVALUE, MS) ->
 
309
    merge_indAudMediaDescriptor(T, TSD, asn1_NOVALUE, [Val|MS]);
 
310
merge_indAudMediaDescriptor([{streamParm, Val}|T], TSD, asn1_NOVALUE, []) ->
 
311
    merge_indAudMediaDescriptor(T, TSD, Val, []);
 
312
merge_indAudMediaDescriptor(Vals, TSD, OneStream, MultiStream) ->
 
313
    return_error(0, {invalid_indAudMediaDescriptor, 
 
314
                     {Vals, TSD, OneStream, MultiStream}}).
 
315
 
 
316
 
 
317
merge_indAudLocalControlDescriptor(Parms) ->
 
318
    do_merge_indAudLocalControlDescriptor(Parms, 
 
319
                                          #'IndAudLocalControlDescriptor'{}).
 
320
                                          
 
321
do_merge_indAudLocalControlDescriptor([Parm | Parms], Desc) ->
 
322
%%     d("do_merge_indAudLocalControlDescriptor -> entry when"
 
323
%%       "~n   Parm: ~p"
 
324
%%       "~n   Desc: ~p", [Parm, Desc]),
 
325
    case Parm of
 
326
        modeToken when Desc#'IndAudLocalControlDescriptor'.streamMode == asn1_NOVALUE ->
 
327
            Desc2 = Desc#'IndAudLocalControlDescriptor'{streamMode = 'NULL'},
 
328
            do_merge_indAudLocalControlDescriptor(Parms, Desc2);
 
329
        reservedGroupToken when Desc#'IndAudLocalControlDescriptor'.reserveGroup == asn1_NOVALUE ->
 
330
            Desc2 = Desc#'IndAudLocalControlDescriptor'{reserveGroup = 'NULL'},
 
331
            do_merge_indAudLocalControlDescriptor(Parms, Desc2);
 
332
        reservedValueToken when Desc#'IndAudLocalControlDescriptor'.reserveValue == asn1_NOVALUE ->
 
333
            Desc2 = Desc#'IndAudLocalControlDescriptor'{reserveValue = 'NULL'},
 
334
            do_merge_indAudLocalControlDescriptor(Parms, Desc2);
 
335
        {pkgdName, Val} when Desc#'IndAudLocalControlDescriptor'.propertyParms == asn1_NOVALUE ->
 
336
            PropParms = [#'IndAudPropertyParm'{name = Val}],
 
337
            Desc2 = Desc#'IndAudLocalControlDescriptor'{propertyParms = PropParms},
 
338
            do_merge_indAudLocalControlDescriptor(Parms, Desc2);
 
339
        {pkgdName, Val} when list(Desc#'IndAudLocalControlDescriptor'.propertyParms) ->
 
340
            PropParms = Desc#'IndAudLocalControlDescriptor'.propertyParms,
 
341
            PropParms2 = [#'IndAudPropertyParm'{name = Val} | PropParms],
 
342
            Desc2 = Desc#'IndAudLocalControlDescriptor'{propertyParms = PropParms2},
 
343
            do_merge_indAudLocalControlDescriptor(Parms, Desc2)
 
344
    end;
 
345
do_merge_indAudLocalControlDescriptor([], Desc) ->
 
346
%%     d("do_merge_indAudLocalControlDescriptor -> entry when"
 
347
%%       "~n   Desc: ~p", [Desc]),
 
348
    case Desc#'IndAudLocalControlDescriptor'.propertyParms of
 
349
        [_ | _] = PropParms -> % List has more then one element
 
350
            PropParms2= lists:reverse(PropParms),
 
351
            Desc#'IndAudLocalControlDescriptor'{propertyParms = PropParms2};
 
352
        _ ->
 
353
            Desc
 
354
    end.
 
355
 
 
356
ensure_indAudLocalParm(Token) ->
 
357
    case Token of
 
358
        {safeToken, _Line, "mode"}          -> modeToken;
 
359
        {safeToken, _Line, "mo"}            -> modeToken;
 
360
        {safeToken, _Line, "reservedgroup"} -> reservedGroupToken;
 
361
        {safeToken, _Line, "rg"}            -> reservedGroupToken;
 
362
        {safeToken, _Line, "reservedvalue"} -> reservedValueToken;
 
363
        {safeToken, _Line, "rv"}            -> reservedValueToken;
 
364
        PkgdName                            -> {pkgdName,
 
365
                                                ensure_pkgdName(PkgdName)}
 
366
    end.
 
367
 
 
368
merge_indAudTerminationStateDescriptor({pkgdName, Val}) ->
 
369
    PropParm = #'IndAudPropertyParm'{name = Val},
 
370
    #'IndAudTerminationStateDescriptor'{propertyParms = [PropParm]};
 
371
merge_indAudTerminationStateDescriptor(serviceStatesToken) ->
 
372
    #'IndAudTerminationStateDescriptor'{serviceState = 'NULL'};
 
373
merge_indAudTerminationStateDescriptor(bufferToken) ->
 
374
    #'IndAudTerminationStateDescriptor'{eventBufferControl = 'NULL'}.
 
375
 
 
376
 
 
377
merge_indAudEventBufferDescriptor(EventName, SpecParams) ->
 
378
    IAEBD = #'IndAudEventBufferDescriptor'{eventName = EventName},
 
379
    do_merge_indAudEventBufferDescriptor(SpecParams, IAEBD).
 
380
 
 
381
do_merge_indAudEventBufferDescriptor(asn1_NOVALUE, IAEBD) ->
 
382
    IAEBD;
 
383
do_merge_indAudEventBufferDescriptor({streamID, StreamID}, IAEBD) ->
 
384
    IAEBD#'IndAudEventBufferDescriptor'{streamID = StreamID};
 
385
do_merge_indAudEventBufferDescriptor({eventParameterName, _Name} = EPN, 
 
386
                                     IAEBD) ->
 
387
    %% BUGBUG BUGBUG BUGBUG 
 
388
    %% This is an ugly hack to allow the eventParamName which only
 
389
    %% exists in the text encoding...
 
390
    IAEBD#'IndAudEventBufferDescriptor'{streamID = EPN}.
 
391
 
 
392
 
 
393
ensure_indAudSignalListParm(SIG) when record(SIG, 'Signal') ->
 
394
    ensure_indAudSignal(SIG).
 
395
 
 
396
ensure_indAudSignal(#'Signal'{signalName       = SignalName,
 
397
                              streamID         = asn1_NOVALUE, 
 
398
                              sigType          = asn1_NOVALUE, 
 
399
                              duration         = asn1_NOVALUE, 
 
400
                              notifyCompletion = asn1_NOVALUE, 
 
401
                              keepActive       = asn1_NOVALUE, 
 
402
                              sigParList       = []}) ->
 
403
    #'IndAudSignal'{signalName = SignalName}.
 
404
    
 
405
 
 
406
ensure_IADMD({_TokenTag, _Line, 
 
407
              #'DigitMapDescriptor'{digitMapName  = Name,
 
408
                                    digitMapValue = asn1_NOVALUE}}) ->
 
409
    #'IndAudDigitMapDescriptor'{digitMapName = Name}.
 
410
 
 
411
 
 
412
merge_indAudPackagesDescriptor(#'PackagesItem'{packageName    = N,
 
413
                                               packageVersion = V}) ->
 
414
    #'IndAudPackagesDescriptor'{packageName    = N,
 
415
                                packageVersion = V}.
 
416
 
 
417
 
 
418
ensure_indAudTerminationStateParm(Token) ->
 
419
    case Token of
 
420
        {safeToken, _Line, "servicestates"} -> serviceStatesToken;
 
421
        {safeToken, _Line, "si"}            -> serviceStatesToken;
 
422
        {safeToken, _Line, "buffer"}        -> bufferToken;
 
423
        {safeToken, _Line, "bf"}            -> bufferToken;
 
424
        PkgdName                            -> {pkgdName,
 
425
                                                ensure_pkgdName(PkgdName)}
 
426
    end.
 
427
 
 
428
 
 
429
%% Types modified by v2:
 
430
 
 
431
merge_auditDescriptor([]) ->
 
432
    #'AuditDescriptor'{};
 
433
merge_auditDescriptor(Tokens) when list(Tokens) ->
 
434
    case lists:keysearch(terminationAudit, 1, Tokens) of
 
435
        {value, {terminationAudit, TA}} ->
 
436
            case lists:keydelete(terminationAudit, 1, Tokens) of
 
437
                [] ->
 
438
                    #'AuditDescriptor'{auditPropertyToken = TA};
 
439
                AuditTokens ->
 
440
                    #'AuditDescriptor'{auditToken         = AuditTokens,
 
441
                                       auditPropertyToken = TA}
 
442
            end;
 
443
        false ->
 
444
            #'AuditDescriptor'{auditToken = Tokens}
 
445
    end;
 
446
merge_auditDescriptor(_) ->
 
447
    #'AuditDescriptor'{}.
 
448
 
 
449
 
 
450
%% v2 - end
 
451
 
 
452
 
 
453
 
 
454
merge_ServiceChangeParm(Parms) ->
 
455
    Required = [serviceChangeReason, serviceChangeMethod],
 
456
    merge_ServiceChangeParm(Parms, #'ServiceChangeParm'{}, Required).
 
457
 
 
458
merge_ServiceChangeParm([], SCP, []) ->
 
459
    SCP;
 
460
 
 
461
merge_ServiceChangeParm([], _SCP, Required) ->
 
462
    exit({missing_required_serviceChangeParm, Required});
 
463
 
 
464
merge_ServiceChangeParm([{address, Val}|Parms], SCP0, Req) 
 
465
  when SCP0#'ServiceChangeParm'.serviceChangeAddress == asn1_NOVALUE,
 
466
       SCP0#'ServiceChangeParm'.serviceChangeMgcId == asn1_NOVALUE ->
 
467
    SCP = SCP0#'ServiceChangeParm'{serviceChangeAddress = Val},
 
468
    merge_ServiceChangeParm(Parms, SCP, Req);
 
469
merge_ServiceChangeParm([{address, Val}|_Parms], SCP0, _Req) 
 
470
  when SCP0#'ServiceChangeParm'.serviceChangeAddress == asn1_NOVALUE ->
 
471
    MgcId = SCP0#'ServiceChangeParm'.serviceChangeMgcId,
 
472
    exit({not_both_address_mgcid_serviceChangeParm, Val, MgcId});
 
473
 
 
474
merge_ServiceChangeParm([{mgc_id, Val}|Parms], SCP0, Req) 
 
475
  when SCP0#'ServiceChangeParm'.serviceChangeMgcId == asn1_NOVALUE,
 
476
       SCP0#'ServiceChangeParm'.serviceChangeAddress == asn1_NOVALUE ->
 
477
    SCP = SCP0#'ServiceChangeParm'{serviceChangeMgcId = Val},
 
478
    merge_ServiceChangeParm(Parms, SCP, Req);
 
479
merge_ServiceChangeParm([{mgc_id, Val}|_Parms], SCP0, _Req) 
 
480
  when SCP0#'ServiceChangeParm'.serviceChangeMgcId == asn1_NOVALUE ->
 
481
    Addr = SCP0#'ServiceChangeParm'.serviceChangeAddress,
 
482
    exit({not_both_address_mgcid_serviceChangeParm, Val, Addr});
 
483
 
 
484
merge_ServiceChangeParm([{profile, Val}|Parms], SCP0, Req) 
 
485
  when SCP0#'ServiceChangeParm'.serviceChangeProfile == asn1_NOVALUE ->
 
486
    SCP = SCP0#'ServiceChangeParm'{serviceChangeProfile = Val},
 
487
    merge_ServiceChangeParm(Parms, SCP, Req);
 
488
 
 
489
merge_ServiceChangeParm([{version, Val}|Parms], SCP0, Req) 
 
490
  when SCP0#'ServiceChangeParm'.serviceChangeVersion == asn1_NOVALUE ->
 
491
    SCP = SCP0#'ServiceChangeParm'{serviceChangeVersion = Val},
 
492
    merge_ServiceChangeParm(Parms, SCP, Req);
 
493
 
 
494
%% REQUIRED (i.e. no default value)
 
495
merge_ServiceChangeParm([{reason, Val}|Parms], SCP0, Req0) 
 
496
  when SCP0#'ServiceChangeParm'.serviceChangeReason == undefined ->
 
497
    SCP = SCP0#'ServiceChangeParm'{serviceChangeReason = Val},
 
498
    Req = lists:delete(serviceChangeReason, Req0),
 
499
    merge_ServiceChangeParm(Parms, SCP, Req);
 
500
 
 
501
merge_ServiceChangeParm([{delay, Val}|Parms], SCP0, Req) 
 
502
  when SCP0#'ServiceChangeParm'.serviceChangeDelay == asn1_NOVALUE ->
 
503
    SCP = SCP0#'ServiceChangeParm'{serviceChangeDelay = Val},
 
504
    merge_ServiceChangeParm(Parms, SCP, Req);
 
505
 
 
506
%% REQUIRED (i.e. no default value)
 
507
merge_ServiceChangeParm([{method, Val}|Parms], SCP0, Req0) 
 
508
  when SCP0#'ServiceChangeParm'.serviceChangeMethod == undefined ->
 
509
    SCP = SCP0#'ServiceChangeParm'{serviceChangeMethod = Val},
 
510
    Req = lists:delete(serviceChangeMethod, Req0),
 
511
    merge_ServiceChangeParm(Parms, SCP, Req);
 
512
 
 
513
merge_ServiceChangeParm([{time_stamp, Val}|Parms], SCP0, Req) 
 
514
  when SCP0#'ServiceChangeParm'.timeStamp == asn1_NOVALUE ->
 
515
    SCP = SCP0#'ServiceChangeParm'{timeStamp = Val},
 
516
    merge_ServiceChangeParm(Parms, SCP, Req);
 
517
 
 
518
merge_ServiceChangeParm([{extension, _Val}|Parms], SCP0, Req) ->
 
519
    merge_ServiceChangeParm(Parms, SCP0, Req);
 
520
 
 
521
merge_ServiceChangeParm([{audit_item, Val}|Parms], SCP0, Req) 
 
522
  when SCP0#'ServiceChangeParm'.serviceChangeInfo == asn1_NOVALUE, atom(Val) ->
 
523
    SCI = #'AuditDescriptor'{auditToken = [Val]},
 
524
    SCP = SCP0#'ServiceChangeParm'{serviceChangeInfo = SCI},
 
525
    merge_ServiceChangeParm(Parms, SCP, Req);
 
526
merge_ServiceChangeParm([{audit_item, Val}|Parms], SCP0, Req) 
 
527
  when SCP0#'ServiceChangeParm'.serviceChangeInfo == asn1_NOVALUE,tuple(Val) ->
 
528
    SCI = #'AuditDescriptor'{auditPropertyToken = [Val]},
 
529
    SCP = SCP0#'ServiceChangeParm'{serviceChangeInfo = SCI},
 
530
    merge_ServiceChangeParm(Parms, SCP, Req);
 
531
merge_ServiceChangeParm([{audit_item, Val}|Parms], SCP0, Req) 
 
532
  when record(SCP0#'ServiceChangeParm'.serviceChangeInfo, 'AuditDescriptor'),
 
533
       atom(Val) ->
 
534
    SCI0 = SCP0#'ServiceChangeParm'.serviceChangeInfo,
 
535
    L    = SCI0#'AuditDescriptor'.auditToken,
 
536
    SCI  = SCI0#'AuditDescriptor'{auditToken = [Val|L]},
 
537
    SCP  = SCP0#'ServiceChangeParm'{serviceChangeInfo = SCI},
 
538
    merge_ServiceChangeParm(Parms, SCP, Req);
 
539
merge_ServiceChangeParm([{audit_item, Val}|Parms], SCP0, Req) 
 
540
  when record(SCP0#'ServiceChangeParm'.serviceChangeInfo, 'AuditDescriptor'),
 
541
       tuple(Val) ->
 
542
    SCI0 = SCP0#'ServiceChangeParm'.serviceChangeInfo,
 
543
    L    = SCI0#'AuditDescriptor'.auditPropertyToken,
 
544
    SCI  = SCI0#'AuditDescriptor'{auditPropertyToken = [Val|L]},
 
545
    SCP  = SCP0#'ServiceChangeParm'{serviceChangeInfo = SCI},
 
546
    merge_ServiceChangeParm(Parms, SCP, Req);
 
547
 
 
548
merge_ServiceChangeParm([incomplete|Parms], SCP0, Req) 
 
549
  when SCP0#'ServiceChangeParm'.serviceChangeIncompleteFlag == asn1_NOVALUE ->
 
550
    SCP = SCP0#'ServiceChangeParm'{serviceChangeIncompleteFlag = 'NULL'},
 
551
    merge_ServiceChangeParm(Parms, SCP, Req);
 
552
 
 
553
merge_ServiceChangeParm([{Tag, Val}|_Parms], SCP, _Req) ->
 
554
    Val2 = 
 
555
        case Tag of
 
556
            address -> 
 
557
                SCP#'ServiceChangeParm'.serviceChangeAddress;
 
558
            mgc_id     -> 
 
559
                SCP#'ServiceChangeParm'.serviceChangeMgcId;
 
560
            profile    -> 
 
561
                SCP#'ServiceChangeParm'.serviceChangeProfile;
 
562
            version    -> 
 
563
                SCP#'ServiceChangeParm'.serviceChangeVersion;
 
564
            reason     -> 
 
565
                SCP#'ServiceChangeParm'.serviceChangeReason;
 
566
            delay      -> 
 
567
                SCP#'ServiceChangeParm'.serviceChangeDelay;
 
568
            method     -> 
 
569
                SCP#'ServiceChangeParm'.serviceChangeMethod;
 
570
            time_stamp -> 
 
571
                SCP#'ServiceChangeParm'.timeStamp;
 
572
            audit_item -> 
 
573
                SCP#'ServiceChangeParm'.serviceChangeInfo
 
574
        end,
 
575
    exit({at_most_once_serviceChangeParm, {Tag, Val, Val2}});
 
576
merge_ServiceChangeParm([Parm|_Parms], SCP, _Req) ->
 
577
    Parm2 = 
 
578
        case Parm of
 
579
            incomplete -> 
 
580
                SCP#'ServiceChangeParm'.serviceChangeIncompleteFlag
 
581
        end,
 
582
    exit({at_most_once_serviceChangeParm, {Parm, Parm2}}).
 
583
 
 
584
 
 
585
merge_ServiceChangeResParm(Parms) ->
 
586
    merge_ServiceChangeResParm(Parms, #'ServiceChangeResParm'{}).
 
587
 
 
588
merge_ServiceChangeResParm([], SCRP) ->
 
589
    SCRP;
 
590
merge_ServiceChangeResParm([{address, Val}|Parms], SCRP0) 
 
591
  when SCRP0#'ServiceChangeResParm'.serviceChangeAddress == asn1_NOVALUE,
 
592
       SCRP0#'ServiceChangeResParm'.serviceChangeMgcId == asn1_NOVALUE ->
 
593
    SCRP = SCRP0#'ServiceChangeResParm'{serviceChangeAddress = Val},
 
594
    merge_ServiceChangeResParm(Parms, SCRP);
 
595
merge_ServiceChangeResParm([{address, Val}|_Parms], SCRP0) 
 
596
  when SCRP0#'ServiceChangeResParm'.serviceChangeAddress == asn1_NOVALUE ->
 
597
    MgcId = SCRP0#'ServiceChangeResParm'.serviceChangeMgcId,
 
598
    exit({not_both_address_mgcid_servChgReplyParm, Val, MgcId});
 
599
 
 
600
merge_ServiceChangeResParm([{mgc_id, Val}|Parms], SCRP0)
 
601
  when SCRP0#'ServiceChangeResParm'.serviceChangeMgcId == asn1_NOVALUE,
 
602
       SCRP0#'ServiceChangeResParm'.serviceChangeAddress == asn1_NOVALUE -> 
 
603
    SCRP = SCRP0#'ServiceChangeResParm'{serviceChangeMgcId = Val},
 
604
    merge_ServiceChangeResParm(Parms, SCRP);
 
605
merge_ServiceChangeResParm([{mgc_id, Val}|_Parms], SCRP0)
 
606
  when SCRP0#'ServiceChangeResParm'.serviceChangeMgcId == asn1_NOVALUE -> 
 
607
    Addr = SCRP0#'ServiceChangeResParm'.serviceChangeAddress,
 
608
    exit({not_both_address_mgcid_servChgReplyParm, Val, Addr});
 
609
 
 
610
merge_ServiceChangeResParm([{profile, Val}|Parms], SCRP0)
 
611
  when SCRP0#'ServiceChangeResParm'.serviceChangeProfile == asn1_NOVALUE ->
 
612
    SCRP = SCRP0#'ServiceChangeResParm'{serviceChangeProfile = Val},
 
613
    merge_ServiceChangeResParm(Parms, SCRP);
 
614
 
 
615
merge_ServiceChangeResParm([{version, Val}|Parms], SCRP0)
 
616
  when SCRP0#'ServiceChangeResParm'.serviceChangeVersion == asn1_NOVALUE ->
 
617
    SCRP = SCRP0#'ServiceChangeResParm'{serviceChangeVersion = Val},
 
618
    merge_ServiceChangeResParm(Parms, SCRP);
 
619
 
 
620
merge_ServiceChangeResParm([{time_stamp, Val}|Parms], SCRP0)
 
621
  when SCRP0#'ServiceChangeResParm'.timeStamp == asn1_NOVALUE ->
 
622
    SCRP = SCRP0#'ServiceChangeResParm'{timeStamp = Val},
 
623
    merge_ServiceChangeResParm(Parms, SCRP);
 
624
 
 
625
merge_ServiceChangeResParm([{Tag, Val}|_Parms], SCRP) ->
 
626
    Val2 = 
 
627
        case Tag of
 
628
            address    -> SCRP#'ServiceChangeResParm'.serviceChangeAddress;
 
629
            mgc_id     -> SCRP#'ServiceChangeResParm'.serviceChangeMgcId;
 
630
            profile    -> SCRP#'ServiceChangeResParm'.serviceChangeProfile;
 
631
            version    -> SCRP#'ServiceChangeResParm'.serviceChangeVersion;
 
632
            time_stamp -> SCRP#'ServiceChangeResParm'.timeStamp
 
633
        end,
 
634
    exit({at_most_once_servChgReplyParm, {Tag, Val, Val2}}).
 
635
 
 
636
 
 
637
ensure_serviceChangeMethod({safeToken, _Line, "fl"}) ->
 
638
    failover;
 
639
ensure_serviceChangeMethod({safeToken, _Line, "failover"}) ->
 
640
    failover;
 
641
ensure_serviceChangeMethod({safeToken, _Line, "fo"}) -> 
 
642
    forced;
 
643
ensure_serviceChangeMethod({safeToken, _Line, "forced"}) ->
 
644
    forced;
 
645
ensure_serviceChangeMethod({safeToken, _Line, "gr"}) ->
 
646
    graceful;
 
647
ensure_serviceChangeMethod({safeToken, _Line, "graceful"}) ->
 
648
    graceful;
 
649
ensure_serviceChangeMethod({safeToken, _Line, "rs"}) ->
 
650
    restart;
 
651
ensure_serviceChangeMethod({safeToken, _Line, "restart"}) ->
 
652
    restart;
 
653
ensure_serviceChangeMethod({safeToken, _Line, "dc"}) ->
 
654
    disconnected;
 
655
ensure_serviceChangeMethod({safeToken, _Line, "disconnected"}) ->
 
656
    disconnected;
 
657
ensure_serviceChangeMethod({safeToken, _Line, "ho"}) ->
 
658
    handOff;
 
659
ensure_serviceChangeMethod({safeToken, _Line, "handoff"}) ->
 
660
    handOff;
 
661
ensure_serviceChangeMethod({safeToken, Line, Text}) ->
 
662
    return_error(Line, {bad_serviceChangeMethod, Text}).
 
663
 
 
664
 
 
665
ensure_profile({_TokenTag, Line, Text}) ->
 
666
    case string:tokens(Text, [$/]) of
 
667
        [Name, Version] ->
 
668
            Version2 = ensure_version(Version),
 
669
            #'ServiceChangeProfile'{profileName = Name, version = Version2};
 
670
        _ ->
 
671
            return_error(Line, {bad_profile, Text})
 
672
    end.
 
673
 
 
674
ensure_version(Version) ->
 
675
    ensure_uint(Version, 0, 99).
 
676
 
 
677
merge_signalRequest(SignalName, PropertyParms) ->
 
678
    Sig = #'Signal'{signalName = SignalName},
 
679
    SPL = [],
 
680
    do_merge_signalRequest(Sig, PropertyParms, SPL).
 
681
 
 
682
do_merge_signalRequest(Sig, [H | T], SPL) ->
 
683
    case H of
 
684
        {stream, SID} when Sig#'Signal'.streamID == asn1_NOVALUE ->
 
685
            do_merge_signalRequest(Sig#'Signal'{streamID = SID}, T, SPL);
 
686
        {signal_type, SigType} when Sig#'Signal'.sigType == asn1_NOVALUE ->
 
687
            do_merge_signalRequest(Sig#'Signal'{sigType = SigType}, T, SPL);
 
688
        {duration, Duration} when Sig#'Signal'.duration == asn1_NOVALUE ->
 
689
            do_merge_signalRequest(Sig#'Signal'{duration = Duration}, T, SPL);
 
690
        {notify_completion, NC} when Sig#'Signal'.notifyCompletion == asn1_NOVALUE ->
 
691
            do_merge_signalRequest(Sig#'Signal'{notifyCompletion = NC}, T, SPL);
 
692
        keepActive when Sig#'Signal'.keepActive == asn1_NOVALUE->
 
693
            do_merge_signalRequest(Sig#'Signal'{keepActive = true}, T, SPL);
 
694
        {other, Name, PP} ->
 
695
            SP = #'SigParameter'{sigParameterName = Name, 
 
696
                                 value            = PP#'PropertyParm'.value,
 
697
                                 extraInfo        = PP#'PropertyParm'.extraInfo},
 
698
            do_merge_signalRequest(Sig, T, [SP | SPL]);
 
699
        {direction, Dir} when Sig#'Signal'.direction == asn1_NOVALUE->
 
700
            do_merge_signalRequest(Sig#'Signal'{direction = Dir}, T, SPL);
 
701
        {requestId, RID} when Sig#'Signal'.requestID == asn1_NOVALUE->
 
702
            do_merge_signalRequest(Sig#'Signal'{requestID = RID}, T, SPL);
 
703
        _ ->
 
704
            return_error(0, {bad_sigParm, H})
 
705
    end;
 
706
do_merge_signalRequest(Sig, [], SPL) ->
 
707
    Sig#'Signal'{sigParList = lists:reverse(SPL)} .
 
708
 
 
709
%% eventStream       = StreamToken EQUAL StreamID
 
710
%% eventOther        = eventParameterName parmValue
 
711
select_stream_or_other("st", #'PropertyParm'{value = [Value]}) ->
 
712
    {stream, ensure_uint16(Value)};
 
713
select_stream_or_other("st", Value) ->
 
714
    {stream, ensure_uint16(Value)};
 
715
select_stream_or_other("stream", #'PropertyParm'{value = [Value]}) ->
 
716
    {stream, ensure_uint16(Value)};
 
717
select_stream_or_other("stream", Value) ->
 
718
    {stream, ensure_uint16(Value)};
 
719
select_stream_or_other(Name, #'PropertyParm'{value = Value}) ->
 
720
    EP = #'EventParameter'{eventParameterName = Name,
 
721
                           value              = Value},
 
722
    {other, EP}.
 
723
 
 
724
ensure_eventDM({_TokenTag, Line, DMD}) 
 
725
  when record(DMD, 'DigitMapDescriptor') ->
 
726
    Name = DMD#'DigitMapDescriptor'.digitMapName,
 
727
    Val  = DMD#'DigitMapDescriptor'.digitMapValue,
 
728
    if
 
729
        Name  == asn1_NOVALUE, Val /= asn1_NOVALUE ->
 
730
            {'DigitMapValue', Start, Short, Long, Duration, Body} = Val,
 
731
            DMV = #'DigitMapValue'{startTimer    = Start, 
 
732
                                   shortTimer    = Short, 
 
733
                                   longTimer     = Long, 
 
734
                                   digitMapBody  = Body,
 
735
                                   durationTimer = Duration},
 
736
            {eventDM, {digitMapValue, DMV}};
 
737
        Name  /= asn1_NOVALUE, Val == asn1_NOVALUE ->
 
738
            {eventDM, {digitMapName, Name}};
 
739
        true ->
 
740
            return_error(Line, {bad_eventDM, DMD})
 
741
    end.
 
742
    
 
743
ensure_DMD({_TokenTag, _Line, DMD}) 
 
744
  when record(DMD, 'DigitMapDescriptor') ->
 
745
    Val2 = 
 
746
        case DMD#'DigitMapDescriptor'.digitMapValue of
 
747
            %% Note that the values of the digitMapBody and durationTimers
 
748
            %% are swapped by the scanner (this is done because of a 
 
749
            %% problem in the flex scanner).
 
750
            #'DigitMapValue'{durationTimer = Body,
 
751
                             digitMapBody  = Duration} = DMV ->
 
752
                %% Convert to version 1 DigitMapValue
 
753
                DMV#'DigitMapValue'{digitMapBody  = Body,
 
754
                                    durationTimer = Duration};
 
755
            Other ->
 
756
                Other
 
757
        end,
 
758
    DMD#'DigitMapDescriptor'{digitMapValue = Val2}.
 
759
 
 
760
 
 
761
merge_observed_event(ObservedEvents, EventName, TimeStamp) ->
 
762
    StreamId = asn1_NOVALUE,
 
763
    EPL = [],
 
764
    do_merge_observed_event(ObservedEvents, EventName, TimeStamp, StreamId, EPL).
 
765
 
 
766
do_merge_observed_event([{stream, StreamID} | T], EventName, TimeStamp, asn1_NOVALUE, EPL) ->
 
767
    do_merge_observed_event(T, EventName, TimeStamp, StreamID, EPL);
 
768
do_merge_observed_event([{other, PP} | T], EventName, TimeStamp, StreamID, EPL) ->
 
769
    do_merge_observed_event(T, EventName, TimeStamp, StreamID, [PP | EPL]);
 
770
do_merge_observed_event([], EventName, TimeStamp, StreamID, EPL) ->
 
771
    #'ObservedEvent'{eventName    = EventName,
 
772
                     timeNotation = TimeStamp,
 
773
                     streamID     = StreamID,
 
774
                     eventParList = lists:reverse(EPL)}.
 
775
 
 
776
merge_eventSpec(OE) when record(OE, 'ObservedEvent'),
 
777
                         OE#'ObservedEvent'.timeNotation == asn1_NOVALUE ->
 
778
    #'EventSpec'{eventName     = OE#'ObservedEvent'.eventName,
 
779
                 streamID      = OE#'ObservedEvent'.streamID,
 
780
                 eventParList  = OE#'ObservedEvent'.eventParList};
 
781
merge_eventSpec(OE) ->
 
782
    return_error(0, {bad_event_spec, OE}).
 
783
 
 
784
merge_eventParameters(Params) ->
 
785
    StreamId = asn1_NOVALUE,
 
786
    EPL      = [],
 
787
    RA       = #'RequestedActions'{},
 
788
    HasA     = no,
 
789
    do_merge_eventParameters(Params, StreamId, EPL, RA, HasA) .
 
790
                                   
 
791
do_merge_eventParameters([H | T], StreamId, EPL, RA, HasA) ->
 
792
    case H of
 
793
        keepActive when RA#'RequestedActions'.keepActive == asn1_NOVALUE ->
 
794
            RA2 = RA#'RequestedActions'{keepActive = true},
 
795
            do_merge_eventParameters(T, StreamId, EPL, RA2, yes);
 
796
        {embed, SD, SED} when RA#'RequestedActions'.signalsDescriptor == asn1_NOVALUE ->
 
797
            RA2 = RA#'RequestedActions'{signalsDescriptor = SD,
 
798
                                          secondEvent       = SED},
 
799
            do_merge_eventParameters(T, StreamId, EPL, RA2, yes);
 
800
        {eventDM, DM} when RA#'RequestedActions'.eventDM == asn1_NOVALUE ->
 
801
            RA2 = RA#'RequestedActions'{eventDM = DM},
 
802
            do_merge_eventParameters(T, StreamId, EPL, RA2, yes);
 
803
        {stream, NewStreamId} when StreamId == asn1_NOVALUE ->
 
804
            do_merge_eventParameters(T, NewStreamId, EPL, RA, HasA);
 
805
        {other, PP} when record(PP, 'PropertyParm') ->
 
806
            EP = #'EventParameter'{eventParameterName = PP#'PropertyParm'.name,
 
807
                                   value              = PP#'PropertyParm'.value,
 
808
                                   extraInfo          = PP#'PropertyParm'.extraInfo},
 
809
            do_merge_eventParameters(T, StreamId, [EP | EPL], RA, HasA);
 
810
        {other, EP} when record(EP, 'EventParameter') ->
 
811
            do_merge_eventParameters(T, StreamId, [EP | EPL], RA, HasA);
 
812
        _ ->
 
813
            return_error(0, {bad_eventParameter, H})
 
814
    end;
 
815
do_merge_eventParameters([], StreamId, EPL, RA, yes) ->
 
816
    #'RequestedEvent'{streamID    = StreamId,
 
817
                      eventAction = RA, 
 
818
                      evParList   = lists:reverse(EPL)};
 
819
do_merge_eventParameters([], StreamId, EPL, _RA, no) ->
 
820
    #'RequestedEvent'{streamID    = StreamId,
 
821
                      eventAction = asn1_NOVALUE, 
 
822
                      evParList   = lists:reverse(EPL)}.
 
823
 
 
824
merge_secondEventParameters(Params) ->
 
825
    StreamId = asn1_NOVALUE,
 
826
    EPL      = [],
 
827
    SRA      = #'SecondRequestedActions'{},
 
828
    HasA     = no,
 
829
    do_merge_secondEventParameters(Params, StreamId, EPL, SRA, HasA) .
 
830
                                   
 
831
do_merge_secondEventParameters([H | T], StreamId, EPL, SRA, HasA) ->
 
832
    case H of
 
833
        keepActive when SRA#'SecondRequestedActions'.keepActive == asn1_NOVALUE ->
 
834
            SRA2 = SRA#'SecondRequestedActions'{keepActive = true},
 
835
            do_merge_secondEventParameters(T, StreamId, EPL, SRA2, yes);
 
836
        {second_embed, SD} when SRA#'SecondRequestedActions'.signalsDescriptor == asn1_NOVALUE ->
 
837
            SRA2 = SRA#'SecondRequestedActions'{signalsDescriptor = SD},
 
838
            do_merge_secondEventParameters(T, StreamId, EPL, SRA2, yes);
 
839
        {eventDM, DM} when SRA#'SecondRequestedActions'.eventDM == asn1_NOVALUE ->
 
840
            SRA2 = SRA#'SecondRequestedActions'{eventDM = DM},
 
841
            do_merge_secondEventParameters(T, StreamId, EPL, SRA2, yes);
 
842
        {stream, NewStreamId} when StreamId == asn1_NOVALUE ->
 
843
            do_merge_secondEventParameters(T, NewStreamId, EPL, SRA, HasA);
 
844
        {other, PP} when record(PP, 'PropertyParm') ->
 
845
            EP = #'EventParameter'{eventParameterName = PP#'PropertyParm'.name,
 
846
                                   value              = PP#'PropertyParm'.value,
 
847
                                   extraInfo          = PP#'PropertyParm'.extraInfo},
 
848
            do_merge_secondEventParameters(T, StreamId, [EP | EPL], SRA, HasA);
 
849
        {other, EP} when record(EP, 'EventParameter') ->
 
850
            do_merge_secondEventParameters(T, StreamId, [EP | EPL], SRA, HasA);
 
851
        _ ->
 
852
            return_error(0, {bad_secondEventParameter, H})
 
853
    end;
 
854
do_merge_secondEventParameters([], StreamId, EPL, SRA, yes) ->
 
855
    #'SecondRequestedEvent'{streamID    = StreamId,
 
856
                            eventAction = SRA, 
 
857
                            evParList   = lists:reverse(EPL)};
 
858
do_merge_secondEventParameters([], StreamId, EPL, _SRA, no) ->
 
859
    #'SecondRequestedEvent'{streamID    = StreamId,
 
860
                            eventAction = asn1_NOVALUE, 
 
861
                            evParList   = lists:reverse(EPL)}.
 
862
 
 
863
%% terminationID     = "ROOT" / pathName / "$" / "*"
 
864
%% Total length of pathName must not exceed 64 chars.
 
865
%% pathName          = ["*"] NAME *("/" / "*"/ ALPHA / DIGIT /"_" / "$" )
 
866
%%                     ["@" pathDomainName ]
 
867
%% ABNF allows two or more consecutive "." although it is meaningless
 
868
%% in a path domain name.
 
869
%% pathDomainName    = (ALPHA / DIGIT / "*" )
 
870
%%                        *63(ALPHA / DIGIT / "-" / "*" / ".")
 
871
ensure_terminationID({safeToken, _Line, LowerText}) ->
 
872
    %% terminationID     = "ROOT" / pathName / "$" / "*"
 
873
    decode_term_id(LowerText, false, [], []).
 
874
 
 
875
decode_term_id([H | T], Wild, Id, Component) ->
 
876
    case H of
 
877
        $/ -> decode_term_id(T, Wild, [lists:reverse(Component) | Id], []);
 
878
        $* -> decode_term_id(T, true, Id, [?megaco_all    | Component]);
 
879
        $$ -> decode_term_id(T, true, Id, [?megaco_choose | Component]);
 
880
        _  -> decode_term_id(T, Wild, Id, [H | Component])
 
881
    end;
 
882
decode_term_id([], Wild, Id, Component) ->
 
883
    Id2 = [lists:reverse(Component) | Id],
 
884
    #megaco_term_id{contains_wildcards = Wild, id = lists:reverse(Id2)}.
 
885
            
 
886
ensure_pathName({_TokenTag, _Line, Text}) ->
 
887
    Text.  %% BUGBUG: ensure values
 
888
 
 
889
%% TimeStamp            = Date "T" Time ; per ISO 8601:1988
 
890
%% Date                 = 8(DIGIT) ; Date = yyyymmdd
 
891
%% Time                 = 8(DIGIT) ; Time = hhmmssss
 
892
ensure_timeStamp({'TimeStampToken', Line, Text}) ->
 
893
    case string:tokens(Text, [$T, $t]) of
 
894
        [Date, Time] ->
 
895
            #'TimeNotation'{date = Date, time = Time};
 
896
        _ ->
 
897
            return_error(Line, {bad_timeStamp, Text})
 
898
    end.
 
899
 
 
900
ensure_transactionID(TransId) ->
 
901
    ensure_uint32(TransId).
 
902
 
 
903
%% transactionAck       = transactionID / (transactionID "-" transactionID)
 
904
ensure_transactionAck({safeToken, _Line, Text}) ->
 
905
    case string:tokens(Text, [$-]) of
 
906
        [Id] ->
 
907
            #'TransactionAck'{firstAck = ensure_transactionID(Id)};
 
908
        [Id, Id2] ->
 
909
            #'TransactionAck'{firstAck = ensure_transactionID(Id),
 
910
                              lastAck  = ensure_transactionID(Id2)}
 
911
    end.
 
912
 
 
913
merge_context_request(asn1_NOVALUE, Prop) ->
 
914
    merge_context_request(#'ContextRequest'{}, Prop);
 
915
 
 
916
merge_context_request(#'ContextRequest'{priority = asn1_NOVALUE} = CR, 
 
917
                      {priority, Int}) ->
 
918
    CR#'ContextRequest'{priority = Int};
 
919
 
 
920
merge_context_request(#'ContextRequest'{emergency = asn1_NOVALUE} = CR, 
 
921
                      {emergency, Bool}) ->
 
922
    CR#'ContextRequest'{emergency = Bool};
 
923
 
 
924
merge_context_request(#'ContextRequest'{topologyReq = asn1_NOVALUE} = CR, 
 
925
                      {topology, Desc}) ->
 
926
    CR#'ContextRequest'{topologyReq = Desc};
 
927
 
 
928
merge_context_request(#'ContextRequest'{iepscallind = asn1_NOVALUE} = CR, 
 
929
                      {iepsCallind, Ind}) ->
 
930
    CR#'ContextRequest'{iepscallind = Ind};
 
931
 
 
932
merge_context_request(#'ContextRequest'{contextProp = asn1_NOVALUE} = CR,
 
933
                      {contextProp, Props}) ->
 
934
    CR#'ContextRequest'{contextProp = Props};
 
935
 
 
936
%% The parser handles this, but we drop it in this version
 
937
%% merge_context_request(#'ContextRequest'{contextList = asn1_NOVALUE} = CR, 
 
938
%%                       {contextList, _IDs}) -> 
 
939
%%    CR#'ContextRequest'{contextList = IDs};
 
940
merge_context_request(CR, {contextList, _IDs}) -> 
 
941
    CR;
 
942
 
 
943
merge_context_request(CR, {Tag, Val}) ->
 
944
    Val2 = 
 
945
        case Tag of
 
946
            priority    -> CR#'ContextRequest'.priority;
 
947
            emergency   -> CR#'ContextRequest'.emergency;
 
948
            topology    -> CR#'ContextRequest'.topologyReq;
 
949
            iepsCallind -> CR#'ContextRequest'.iepscallind;
 
950
            contextProp -> CR#'ContextRequest'.contextProp%% ;
 
951
                           %%    contextList -> CR#'ContextRequest'.contextList
 
952
        end,
 
953
    exit({at_most_once_contextProperty, {Tag, Val, Val2}}).
 
954
            
 
955
 
 
956
merge_context_attr_audit_request(CAAR, []) ->
 
957
    CAAR;
 
958
merge_context_attr_audit_request(CAAR, [H|T]) ->
 
959
    case H of
 
960
        priorityAudit when CAAR#'ContextAttrAuditRequest'.priority == asn1_NOVALUE ->
 
961
            CAAR2 = CAAR#'ContextAttrAuditRequest'{priority = 'NULL'},
 
962
            merge_context_attr_audit_request(CAAR2, T);
 
963
 
 
964
        emergencyAudit when CAAR#'ContextAttrAuditRequest'.emergency == asn1_NOVALUE ->
 
965
            CAAR2 = CAAR#'ContextAttrAuditRequest'{emergency = 'NULL'},
 
966
            merge_context_attr_audit_request(CAAR2, T);
 
967
 
 
968
        topologyAudit when CAAR#'ContextAttrAuditRequest'.topology == asn1_NOVALUE ->
 
969
            CAAR2 = CAAR#'ContextAttrAuditRequest'{topology = 'NULL'},
 
970
            merge_context_attr_audit_request(CAAR2, T);
 
971
 
 
972
        iepsCallind when CAAR#'ContextAttrAuditRequest'.iepscallind == asn1_NOVALUE ->
 
973
            CAAR2 = CAAR#'ContextAttrAuditRequest'{iepscallind = 'NULL'},
 
974
            merge_context_attr_audit_request(CAAR2, T);
 
975
 
 
976
        {prop, Name} when CAAR#'ContextAttrAuditRequest'.contextPropAud == asn1_NOVALUE ->
 
977
            CPA = [#'IndAudPropertyParm'{name = Name}],
 
978
            CAAR2 = CAAR#'ContextAttrAuditRequest'{contextPropAud = CPA},
 
979
            merge_context_attr_audit_request(CAAR2, T);
 
980
 
 
981
        {prop, Name} ->
 
982
            CPA   = CAAR#'ContextAttrAuditRequest'.contextPropAud,
 
983
            CPA2  = [#'IndAudPropertyParm'{name = Name}|CPA],
 
984
            CAAR2 = CAAR#'ContextAttrAuditRequest'{contextPropAud = CPA2},
 
985
            merge_context_attr_audit_request(CAAR2, T)
 
986
    
 
987
    end.
 
988
 
 
989
merge_action_request(CtxId, Items) ->
 
990
    do_merge_action_request(Items, [], asn1_NOVALUE, asn1_NOVALUE, CtxId).
 
991
 
 
992
do_merge_action_request([H|T], CmdReqs, CtxReq, CtxAuditReq, CtxId) ->
 
993
    case H of
 
994
        {commandRequest, CmdReq} ->
 
995
            do_merge_action_request(T, [CmdReq|CmdReqs], 
 
996
                                    CtxReq, CtxAuditReq, CtxId);
 
997
 
 
998
        {contextProp, ContextProp} ->
 
999
            do_merge_action_request(T, CmdReqs, 
 
1000
                                    merge_context_request(CtxReq, ContextProp),
 
1001
                                    CtxAuditReq, CtxId);
 
1002
 
 
1003
        {contextAudit, ContextAuditReq} when CtxAuditReq == asn1_NOVALUE ->
 
1004
            do_merge_action_request(T, CmdReqs, 
 
1005
                                    CtxReq, ContextAuditReq, CtxId)
 
1006
    end;
 
1007
do_merge_action_request([], CmdReqs, CtxReq, CtxAuditReq, CtxId) ->
 
1008
    #'ActionRequest'{contextId           = CtxId,
 
1009
                     contextRequest      = strip_ContextRequest(CtxReq),
 
1010
                     contextAttrAuditReq = strip_ContextAttrAuditRequest(CtxAuditReq),
 
1011
                     commandRequests     = lists:reverse(CmdReqs)}.
 
1012
 
 
1013
 
 
1014
%% OTP-5085: 
 
1015
%% In order to solve a problem in the parser, the error descriptor
 
1016
%% has been put last in the non-empty commandReplyList, if it is not 
 
1017
%% asn1_NOVALUE
 
1018
merge_action_reply(Items) ->
 
1019
    do_merge_action_reply(Items, asn1_NOVALUE, asn1_NOVALUE, []).
 
1020
 
 
1021
do_merge_action_reply([], Err, Ctx, Cmds) ->
 
1022
    #'ActionReply'{errorDescriptor = Err,
 
1023
                   contextReply    = strip_ContextRequest(Ctx),
 
1024
                   commandReply    = lists:reverse(Cmds)};
 
1025
do_merge_action_reply([H|T], Err0, CR, Cmds) ->
 
1026
    case H of
 
1027
        {error, Err1} when Err0 == asn1_NOVALUE ->
 
1028
            do_merge_action_reply(T, Err1, CR, Cmds);
 
1029
        {command, Cmd} ->
 
1030
            do_merge_action_reply(T, Err0, CR, [Cmd | Cmds]);
 
1031
        {context, CtxProp} ->
 
1032
            do_merge_action_reply(T, Err0, 
 
1033
                                  merge_context_request(CR, CtxProp), Cmds)
 
1034
    end.
 
1035
 
 
1036
strip_ContextRequest(#'ContextRequest'{priority    = asn1_NOVALUE,
 
1037
                                       emergency   = asn1_NOVALUE,
 
1038
                                       topologyReq = asn1_NOVALUE,
 
1039
                                       iepscallind = asn1_NOVALUE,
 
1040
                                       contextProp = asn1_NOVALUE}) ->
 
1041
    asn1_NOVALUE;
 
1042
strip_ContextRequest(#'ContextRequest'{priority    = asn1_NOVALUE,
 
1043
                                       emergency   = asn1_NOVALUE,
 
1044
                                       topologyReq = asn1_NOVALUE,
 
1045
                                       iepscallind = asn1_NOVALUE,
 
1046
                                       contextProp = []}) ->
 
1047
    asn1_NOVALUE;
 
1048
strip_ContextRequest(asn1_NOVALUE) ->
 
1049
    asn1_NOVALUE;
 
1050
strip_ContextRequest(R) ->
 
1051
    R.
 
1052
 
 
1053
strip_ContextAttrAuditRequest(asn1_NOVALUE) ->
 
1054
    asn1_NOVALUE;
 
1055
strip_ContextAttrAuditRequest(
 
1056
  #'ContextAttrAuditRequest'{priority       = asn1_NOVALUE,
 
1057
                             emergency      = asn1_NOVALUE,
 
1058
                             topology       = asn1_NOVALUE,
 
1059
                             iepscallind    = asn1_NOVALUE,
 
1060
                             contextPropAud = asn1_NOVALUE}) ->
 
1061
    asn1_NOVALUE;
 
1062
strip_ContextAttrAuditRequest(
 
1063
  #'ContextAttrAuditRequest'{priority       = asn1_NOVALUE,
 
1064
                             emergency      = asn1_NOVALUE,
 
1065
                             topology       = asn1_NOVALUE,
 
1066
                             iepscallind    = asn1_NOVALUE,
 
1067
                             contextPropAud = []}) ->
 
1068
    asn1_NOVALUE;
 
1069
strip_ContextAttrAuditRequest(R) ->
 
1070
    R.
 
1071
 
 
1072
merge_AmmRequest_descriptors([], Acc) ->
 
1073
    lists:reverse(Acc);
 
1074
merge_AmmRequest_descriptors([{_, deprecated}|Descs], Acc) ->
 
1075
    merge_AmmRequest_descriptors(Descs, Acc);
 
1076
merge_AmmRequest_descriptors([Desc|Descs], Acc) ->
 
1077
    merge_AmmRequest_descriptors(Descs, [Desc|Acc]).
 
1078
 
 
1079
make_commandRequest({CmdTag, {_TokenTag, _Line, Text}}, Cmd) ->
 
1080
    Req = #'CommandRequest'{command  = {CmdTag, Cmd}},
 
1081
    case Text of
 
1082
        [$w, $- | _] ->
 
1083
            Req#'CommandRequest'{wildcardReturn = 'NULL'};
 
1084
        [$o, $-, $w, $- | _] ->
 
1085
            Req#'CommandRequest'{optional = 'NULL', wildcardReturn = 'NULL'};
 
1086
        [$o, $- | _] ->
 
1087
            Req#'CommandRequest'{optional = 'NULL'};
 
1088
        _ -> 
 
1089
            Req
 
1090
    end.
 
1091
 
 
1092
merge_terminationAudit(AuditReturnParameters) ->
 
1093
    lists:reverse(do_merge_terminationAudit(AuditReturnParameters, [], [])).
 
1094
 
 
1095
do_merge_terminationAudit([H| T], ARPs, AuditItems) ->
 
1096
    case H of
 
1097
        {auditReturnItem, AuditItem} ->
 
1098
            do_merge_terminationAudit(T, ARPs, [AuditItem | AuditItems]);
 
1099
        AuditReturnParameter ->
 
1100
            do_merge_terminationAudit(T, [AuditReturnParameter | ARPs], AuditItems)
 
1101
    end;
 
1102
do_merge_terminationAudit([], AuditReturnParameters, []) ->
 
1103
    AuditReturnParameters;
 
1104
do_merge_terminationAudit([], AuditReturnParameters, AuditItems) ->
 
1105
    AuditDescriptor = #'AuditDescriptor'{auditToken = AuditItems},
 
1106
    AuditReturnParameter = {emptyDescriptors, AuditDescriptor},
 
1107
    [AuditReturnParameter | AuditReturnParameters].
 
1108
        
 
1109
merge_mediaDescriptor(MediaParms) ->
 
1110
    do_merge_mediaDescriptor(MediaParms, asn1_NOVALUE, [], []).
 
1111
 
 
1112
do_merge_mediaDescriptor([H | T], TS, One, Multi) ->
 
1113
    case H of
 
1114
        {streamParm, Parm} when Multi == [] ->
 
1115
            do_merge_mediaDescriptor(T, TS, [Parm | One], Multi);
 
1116
        {streamDescriptor, Desc} when One == [] ->
 
1117
            do_merge_mediaDescriptor(T, TS, One, [Desc | Multi]);
 
1118
        {termState, TS2} when TS  == asn1_NOVALUE ->
 
1119
            do_merge_mediaDescriptor(T, TS2, One, Multi);
 
1120
        _ ->
 
1121
            return_error(0, {bad_merge_mediaDescriptor, [H, TS, One, Multi]})
 
1122
    end;
 
1123
do_merge_mediaDescriptor([], TS, One, Multi) ->
 
1124
    if
 
1125
        One == [], Multi == [] ->
 
1126
            #'MediaDescriptor'{streams = asn1_NOVALUE,
 
1127
                               termStateDescr = TS};
 
1128
        One /= [], Multi == [] ->
 
1129
            #'MediaDescriptor'{streams = {oneStream, merge_streamParms(One)},
 
1130
                               termStateDescr = TS};
 
1131
        One == [], Multi /= [] ->
 
1132
            #'MediaDescriptor'{streams = {multiStream, lists:reverse(Multi)},
 
1133
                               termStateDescr = TS}
 
1134
    end.
 
1135
  
 
1136
merge_streamParms(TaggedStreamParms) ->
 
1137
    SP = #'StreamParms'{},
 
1138
    do_merge_streamParms(TaggedStreamParms, SP).
 
1139
 
 
1140
do_merge_streamParms([{Tag, D} | T] = All, SP) ->
 
1141
    case Tag of
 
1142
        local when SP#'StreamParms'.localDescriptor  == asn1_NOVALUE ->
 
1143
            do_merge_streamParms(T, SP#'StreamParms'{localDescriptor = D});
 
1144
        remote when SP#'StreamParms'.remoteDescriptor == asn1_NOVALUE ->
 
1145
            do_merge_streamParms(T, SP#'StreamParms'{remoteDescriptor = D});
 
1146
        control ->
 
1147
            LCD = 
 
1148
                case SP#'StreamParms'.localControlDescriptor of
 
1149
                    asn1_NOVALUE ->
 
1150
                        #'LocalControlDescriptor'{propertyParms = []};
 
1151
                    PrevLCD ->
 
1152
                        PrevLCD
 
1153
                end,
 
1154
            LCD2 = do_merge_control_streamParms(D, LCD),
 
1155
            do_merge_streamParms(T, SP#'StreamParms'{localControlDescriptor = LCD2});
 
1156
        statistics when SP#'StreamParms'.statisticsDescriptor == asn1_NOVALUE ->
 
1157
            do_merge_streamParms(T, SP#'StreamParms'{statisticsDescriptor = D});
 
1158
        _ ->
 
1159
            return_error(0, {do_merge_streamParms, [All, SP]})
 
1160
    end;
 
1161
do_merge_streamParms([], SP) when record(SP#'StreamParms'.localControlDescriptor, 'LocalControlDescriptor') ->
 
1162
    LCD  = SP#'StreamParms'.localControlDescriptor,
 
1163
    PP   = LCD#'LocalControlDescriptor'.propertyParms,
 
1164
    LCD2 = LCD#'LocalControlDescriptor'{propertyParms = lists:reverse(PP)},
 
1165
    SP#'StreamParms'{localControlDescriptor = LCD2};
 
1166
do_merge_streamParms([], SP) ->
 
1167
    SP.
 
1168
 
 
1169
 
 
1170
do_merge_control_streamParms([{SubTag, SD} | T] = All, LCD) ->
 
1171
    case SubTag of
 
1172
        group when LCD#'LocalControlDescriptor'.reserveGroup == asn1_NOVALUE ->
 
1173
            LCD2 = LCD#'LocalControlDescriptor'{reserveGroup = SD},
 
1174
            do_merge_control_streamParms(T, LCD2);
 
1175
        value when LCD#'LocalControlDescriptor'.reserveValue == asn1_NOVALUE ->
 
1176
            LCD2 = LCD#'LocalControlDescriptor'{reserveValue = SD},
 
1177
            do_merge_control_streamParms(T, LCD2);
 
1178
        mode when LCD#'LocalControlDescriptor'.streamMode == asn1_NOVALUE ->
 
1179
            LCD2 = LCD#'LocalControlDescriptor'{streamMode = SD},
 
1180
            do_merge_control_streamParms(T, LCD2);
 
1181
        prop ->
 
1182
            PP = LCD#'LocalControlDescriptor'.propertyParms,
 
1183
            LCD2 = LCD#'LocalControlDescriptor'{propertyParms = [SD | PP]},
 
1184
            do_merge_control_streamParms(T, LCD2);
 
1185
        _ ->
 
1186
            return_error(0, {do_merge_control_streamParms, [All, LCD]})
 
1187
  end;
 
1188
do_merge_control_streamParms([], LCD) ->
 
1189
    LCD.
 
1190
 
 
1191
merge_terminationStateDescriptor(Parms) ->
 
1192
    TSD = #'TerminationStateDescriptor'{propertyParms = []},
 
1193
    do_merge_terminationStateDescriptor(Parms, TSD).
 
1194
 
 
1195
do_merge_terminationStateDescriptor([{Tag, Val} | T], TSD) ->
 
1196
    case Tag of
 
1197
        serviceState when TSD#'TerminationStateDescriptor'.serviceState == asn1_NOVALUE ->
 
1198
            TSD2 = TSD#'TerminationStateDescriptor'{serviceState = Val},
 
1199
            do_merge_terminationStateDescriptor(T, TSD2);
 
1200
        eventBufferControl when TSD#'TerminationStateDescriptor'.eventBufferControl == asn1_NOVALUE->
 
1201
            TSD2 = TSD#'TerminationStateDescriptor'{eventBufferControl = Val},
 
1202
            do_merge_terminationStateDescriptor(T, TSD2);
 
1203
        propertyParm ->
 
1204
            PP = TSD#'TerminationStateDescriptor'.propertyParms,
 
1205
            TSD2 = TSD#'TerminationStateDescriptor'{propertyParms = [Val | PP]},
 
1206
            do_merge_terminationStateDescriptor(T, TSD2)
 
1207
    end;
 
1208
do_merge_terminationStateDescriptor([], TSD) ->
 
1209
    PP = TSD#'TerminationStateDescriptor'.propertyParms,
 
1210
    TSD#'TerminationStateDescriptor'{propertyParms = lists:reverse(PP)}.
 
1211
 
 
1212
ensure_prop_groups({_TokenTag, _Line, Text}) ->
 
1213
    Group  = [],
 
1214
    Groups = [],
 
1215
    parse_prop_name(Text, Group, Groups).
 
1216
 
 
1217
parse_prop_name([Char | Rest] = All, Group, Groups) ->
 
1218
    case ?classify_char(Char) of
 
1219
        white_space ->
 
1220
            parse_prop_name(Rest, Group, Groups);
 
1221
        end_of_line ->
 
1222
            parse_prop_name(Rest, Group, Groups);
 
1223
        _ ->
 
1224
            Name = [],
 
1225
            do_parse_prop_name(All, Name, Group, Groups)
 
1226
    end;
 
1227
parse_prop_name([] = All, Group, Groups) ->
 
1228
    Name = [],
 
1229
    do_parse_prop_name(All, Name, Group, Groups).
 
1230
 
 
1231
do_parse_prop_name([Char | Rest], Name, Group, Groups) ->
 
1232
    case ?classify_char(Char) of
 
1233
        safe_char ->
 
1234
            do_parse_prop_name(Rest, [Char | Name], Group, Groups);
 
1235
        rest_char when Char == $=, Name /= [] ->
 
1236
            %% Now we have a complete name
 
1237
            if
 
1238
                Name == "v", Group /= [] ->
 
1239
                    %% v= is a property group delimiter,
 
1240
                    %% lets create yet another property group.
 
1241
                    Groups2 = [lists:reverse(Group) | Groups],
 
1242
                    Group2 = [],
 
1243
                    parse_prop_value(Rest, Name, Group2, Groups2);
 
1244
                true ->
 
1245
                    %% Use current property group
 
1246
                    parse_prop_value(Rest, Name, Group, Groups)
 
1247
            end;
 
1248
        _ ->
 
1249
            return_error(0, {bad_prop_name, lists:reverse(Name), Char})
 
1250
    end;
 
1251
do_parse_prop_name([], [], [], Groups) ->
 
1252
    lists:reverse(Groups);
 
1253
do_parse_prop_name([], [], Group, Groups) ->
 
1254
    Group2 = lists:reverse(Group),
 
1255
    lists:reverse([Group2 | Groups]);
 
1256
do_parse_prop_name([], Name, Group, Groups) when Name /= [] ->
 
1257
    %% Assume end of line
 
1258
    Value = [],
 
1259
    PP = make_prop_parm(Name, Value),
 
1260
    Group2 = lists:reverse([PP | Group]),
 
1261
    lists:reverse([Group2 | Groups]).
 
1262
                   
 
1263
parse_prop_value(Chars, Name, Group, Groups) ->
 
1264
    Value = [],
 
1265
    do_parse_prop_value(Chars, Name, Value, Group, Groups).
 
1266
 
 
1267
do_parse_prop_value([Char | Rest], Name, Value, Group, Groups) ->
 
1268
    case ?classify_char(Char) of
 
1269
        end_of_line ->
 
1270
            %% Now we have a complete "name=value" pair
 
1271
            PP = make_prop_parm(Name, Value),
 
1272
            parse_prop_name(Rest, [PP | Group], Groups);
 
1273
        _ ->
 
1274
            do_parse_prop_value(Rest, Name, [Char | Value], Group, Groups)
 
1275
    end;
 
1276
do_parse_prop_value([], Name, Value, Group, Groups) ->
 
1277
    %% Assume end of line
 
1278
    PP = make_prop_parm(Name, Value),
 
1279
    Group2 = lists:reverse([PP | Group]),
 
1280
    lists:reverse([Group2 | Groups]).
 
1281
 
 
1282
make_prop_parm(Name, Value) ->
 
1283
    #'PropertyParm'{name  = lists:reverse(Name),
 
1284
                    value = [lists:reverse(Value)]}.
 
1285
 
 
1286
ensure_uint({_TokenTag, Line, Val}, Min, Max) when integer(Val) ->
 
1287
    if
 
1288
        integer(Min), Val >= Min ->
 
1289
            if
 
1290
                integer(Max), Val =< Max ->
 
1291
                    Val;
 
1292
                Max == infinity ->
 
1293
                    Val;
 
1294
                true ->
 
1295
                    return_error(Line, {too_large_integer, Val, Max})
 
1296
            end;
 
1297
        true ->
 
1298
            return_error(Line, {too_small_integer, Val, Min})
 
1299
    end;
 
1300
ensure_uint({TokenTag, Line, Text}, Min, Max) ->
 
1301
    case catch list_to_integer(Text) of
 
1302
        {'EXIT', _} ->
 
1303
            return_error(Line, {not_an_integer, Text});
 
1304
        Val when integer(Val) ->
 
1305
            ensure_uint({TokenTag, Line, Val}, Min, Max)
 
1306
   end;
 
1307
ensure_uint(Val, Min, Max) ->
 
1308
    ensure_uint({uint, 0, Val}, Min, Max).
 
1309
 
 
1310
ensure_uint16(Int) ->
 
1311
    ensure_uint(Int, 0, 65535).
 
1312
 
 
1313
ensure_uint32(Int) ->
 
1314
    ensure_uint(Int, 0, 4294967295) .
 
1315
 
 
1316
%% OTP-4710
 
1317
ensure_hex({_TokenTag, _Line, [$0, $x |Chars]}, Min, Max) ->
 
1318
    ensure_uint(length(Chars), Min, Max),
 
1319
    hex_to_int(Chars, []);
 
1320
ensure_hex({_TokenTag, _Line, [$0, $X |Chars]}, Min, Max) ->
 
1321
    ensure_uint(length(Chars), Min, Max),
 
1322
    hex_to_int(Chars, []);
 
1323
ensure_hex([$0, $x |Chars], Min, Max) ->
 
1324
    ensure_uint(length(Chars), Min, Max),
 
1325
    hex_to_int(Chars, []);
 
1326
ensure_hex([$0, $X |Chars], Min, Max) ->
 
1327
    ensure_uint(length(Chars), Min, Max),
 
1328
    hex_to_int(Chars, []).
 
1329
 
 
1330
%% OTP-4710
 
1331
hex_to_int([], Acc) ->
 
1332
    lists:reverse(Acc);
 
1333
hex_to_int([Char1,Char2|Tail], Acc) ->
 
1334
    Int1 = hchar_to_int(Char1),
 
1335
    Int2 = hchar_to_int(Char2),
 
1336
    Val  = Int2 bor (Int1 bsl 4),
 
1337
    hex_to_int(Tail, [Val| Acc]);
 
1338
hex_to_int([Char], Acc) ->
 
1339
    Int = hchar_to_int(Char),
 
1340
    lists:reverse([Int|Acc]).
 
1341
 
 
1342
hchar_to_int(Char) when $0 =< Char, Char =< $9 ->
 
1343
    Char - $0;
 
1344
hchar_to_int(Char) when $A =< Char, Char =< $F ->
 
1345
    Char - $A + 10; % OTP-4710
 
1346
hchar_to_int(Char) when $a =< Char, Char =< $f ->
 
1347
    Char - $a + 10. % OTP-4710
 
1348
 
 
1349
value_of({_TokenTag, _Line, Text}) ->
 
1350
    Text.
 
1351
 
 
1352
 
 
1353
%% -------------------------------------------------------------------
 
1354
 
 
1355
%% d(F) ->
 
1356
%%     d(F,[]).
 
1357
%% d(F, A) ->
 
1358
%%     %% d(true, F, A).
 
1359
%%     d(get(dbg), F, A).
 
1360
 
 
1361
%% d(true, F, A) ->
 
1362
%%     io:format("DBG:~w:" ++ F ++ "~n", [?MODULE | A]);
 
1363
%% d(_, _, _) ->
 
1364
%%     ok.
 
1365