anchor.diff:  get_value(Key, Plist) ->
       proplists:get_value(Key, Plist, []).
   
  -write(Path, Str) ->
  -    w(Path, Str, "~s").
  -
  -write_term(Path, T) ->
  -    w(Path, T, "~p.").
  -
  -w(Path, T, Fmt) ->
  -    {ok, Fd} = file:open(Path, [write]),
  -    io:fwrite(Fd, Fmt ++ "~n", [T]),
  -    file:close(Fd).
  -
   codegen(File, Spec, Outdir, Mode) ->
       Mod = mod(File, orddict:find(name, Spec)),
       Path = filename:join(Outdir, Mod),  %% minus extension
  
positive.code:     write_term(Path ++ ".D", [?VERSION | Spec]);
  
  gen(hrl, Spec, Mod, Path) ->
      gen_hrl(Path ++ ".hrl", Mod, Spec);
  
  gen(erl, Spec, Mod, Path) ->
      Forms = [{?attribute, module, Mod},
               {?attribute, compile, {parse_transform, diameter_exprecs}},
               {?attribute, compile, nowarn_unused_function},
               {?attribute, export, [{name, 0},
                                     {id, 0},
                                     {vendor_id, 0},
                                     {vendor_name, 0},
                                     {decode_avps, 2}, %% in diameter_gen.hrl
                                     {encode_avps, 2}, %%
                                     {msg_name, 2},
                                     {msg_header, 1},
                                     {rec2msg, 1},
                                     {msg2rec, 1},
                                     {name2rec, 1},
                                     {avp_name, 2},
                                     {avp_arity, 2},
                                     {avp_header, 1},
                                     {avp, 3},
                                     {grouped_avp, 3},
                                     {enumerated_avp, 3},
                                     {empty_value, 1},
                                     {dict, 0}]},
               %% diameter.hrl is included for #diameter_avp
               {?attribute, include_lib, "diameter/include/diameter.hrl"},
               {?attribute, include_lib, "diameter/include/diameter_gen.hrl"},
               f_name(Mod),
               f_id(Spec),
               f_vendor_id(Spec),
               f_vendor_name(Spec),
               f_msg_name(Spec),
               f_msg_header(Spec),
               f_rec2msg(Spec),
               f_msg2rec(Spec),
               f_name2rec(Spec),
               f_avp_name(Spec),
               f_avp_arity(Spec),
               f_avp_header(Spec),
               f_avp(Spec),
               f_enumerated_avp(Spec),
               f_empty_value(Spec),
               f_dict(Spec),
               {eof, ?LINE}],
  
      gen_erl(Path, insert_hrl_forms(Spec, Forms)).
  
  gen_erl(Path, Forms) ->
      getr(debug) andalso write_term(Path ++ ".F", Forms),
      write(Path ++ ".erl",
            header() ++ erl_prettypr:format(erl_syntax:form_list(Forms))).
  
  insert_hrl_forms(Spec, Forms) ->
      {H,T} = lists:splitwith(fun is_header/1, Forms),
      H ++ make_hrl_forms(Spec) ++ T.
  
  is_header({attribute, _, export, _}) ->
      false;
  is_header(_) ->
      true.
  
  make_hrl_forms(Spec) ->
      {_Prefix, MsgRecs, GrpRecs, ImportedGrpRecs}
  
negative.code:      [{?clause, [], [], [?TERM([?VERSION | Spec])]}]}.
  
  %%% ------------------------------------------------------------------------
  %%% # gen_hrl/2
  %%% ------------------------------------------------------------------------
  
  gen_hrl(Mod, Spec) ->
      {Prefix, MsgRecs, GrpRecs, ImportedGrpRecs}
          = make_record_forms(Spec),
  
      [hrl_header(Mod),
       forms("Message records",     MsgRecs),
       forms("Grouped AVP records", GrpRecs),
       lists:map(fun({M,Fs}) ->
                         forms("Grouped AVP records from " ++ atom_to_list(M),
                               Fs)
                 end,
                 ImportedGrpRecs),
       format("ENUM Macros", m_enums(Prefix, false, get_value(enum, Spec))),
       format("DEFINE Macros", m_enums(Prefix, false, get_value(define, Spec))),
       lists:map(fun({M,Es}) ->
                         format("ENUM Macros from " ++ atom_to_list(M),
                                m_enums(Prefix, true, Es))
                 end,
                 get_value(import_enums, Spec))].
  
  forms(_, [] = No) ->
      No;
  forms(Banner, Forms) ->
      format(Banner, prettypr(Forms)).
  
  format(_, [] = No) ->
      No;
  format(Banner, Str) ->
      [banner(Banner), Str, $\n].
  
  prettypr(Forms) ->
      erl_prettypr:format(erl_syntax:form_list(Forms)).
  
  banner(Heading) ->
      ["\n\n"
       "%%% -------------------------------------------------------\n"
       "%%% ", Heading, ":\n"
       "%%% -------------------------------------------------------\n\n"].
  
  z(S) ->
      string:join(string:tokens(S, "\s\t"), "\s").
  