aboutsummaryrefslogtreecommitdiffstats
path: root/lib/snmp/src/compile
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/snmp/src/compile
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/snmp/src/compile')
-rw-r--r--lib/snmp/src/compile/Makefile125
-rw-r--r--lib/snmp/src/compile/depend.mk46
-rw-r--r--lib/snmp/src/compile/modules.mk36
-rw-r--r--lib/snmp/src/compile/snmpc.erl1358
-rw-r--r--lib/snmp/src/compile/snmpc.hrl153
-rw-r--r--lib/snmp/src/compile/snmpc_lib.erl1819
-rw-r--r--lib/snmp/src/compile/snmpc_lib.hrl37
-rw-r--r--lib/snmp/src/compile/snmpc_mib_gram.yrl973
-rw-r--r--lib/snmp/src/compile/snmpc_mib_to_hrl.erl391
-rw-r--r--lib/snmp/src/compile/snmpc_misc.erl173
-rw-r--r--lib/snmp/src/compile/snmpc_misc.hrl74
-rw-r--r--lib/snmp/src/compile/snmpc_tok.erl357
12 files changed, 5542 insertions, 0 deletions
diff --git a/lib/snmp/src/compile/Makefile b/lib/snmp/src/compile/Makefile
new file mode 100644
index 0000000000..4be60e1835
--- /dev/null
+++ b/lib/snmp/src/compile/Makefile
@@ -0,0 +1,125 @@
+#-*-makefile-*- ; force emacs to enter makefile-mode
+
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1997-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%
+
+include $(ERL_TOP)/make/target.mk
+
+EBIN = ../../ebin
+
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../../vsn.mk
+
+VSN = $(SNMP_VSN)
+
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/snmp-$(VSN)
+
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+
+include modules.mk
+
+ERL_FILES = $(MODULES:%=%.erl)
+
+TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+
+GENERATED_PARSER = $(PARSER_MODULE:%=%.erl)
+
+PARSER_TARGET = $(PARSER_MODULE).$(EMULATOR)
+
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+ifeq ($(WARN_UNUSED_VARS),true)
+ERL_COMPILE_FLAGS += +warn_unused_vars
+endif
+
+ERL_COMPILE_FLAGS += -I../../include \
+ -Dversion=\"$(VSN)$(PRE_VSN)\" \
+ +'{parse_transform,sys_pre_attributes}' \
+ +'{attribute,insert,app_vsn,$(APP_VSN)}' \
+ -I$(ERL_TOP)/lib/stdlib
+
+YRL_FLAGS = -o .
+
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+debug:
+ @${MAKE} TYPE=debug opt
+
+opt: $(TARGET_FILES)
+
+clean:
+ rm -f $(TARGET_FILES) $(GENERATED_PARSER)
+ rm -f core *~
+
+docs:
+
+info:
+ @echo "PARSER_SRC: $(PARSER_SRC)"
+ @echo "PARSER_MODULE: $(PARSER_MODULE)"
+ @echo ""
+ @echo "GENERATED_PARSER: $(GENERATED_PARSER)"
+ @echo "PARSER_TARGET: $(PARSER_TARGET)"
+ @echo ""
+ @echo "MODULES: $(MODULES)"
+ @echo ""
+ @echo "TARGET_FILES: $(TARGET_FILES)"
+ @echo ""
+ @echo "EBIN: $(EBIN)"
+ @echo ""
+ @echo ""
+
+# ----------------------------------------------------
+# Special Build Targets
+# ----------------------------------------------------
+
+parser: $(PARSER_TARGET)
+
+$(GENERATED_PARSER): $(PARSER_SRC)
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/src
+ $(INSTALL_DIR) $(RELSYSDIR)/src/compiler
+ $(INSTALL_DATA) $(PARSER_SRC) $(ERL_FILES) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src/compiler
+ $(INSTALL_DIR) $(RELSYSDIR)/ebin
+ $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
+
+release_docs_spec:
+
+include depend.mk
+
diff --git a/lib/snmp/src/compile/depend.mk b/lib/snmp/src/compile/depend.mk
new file mode 100644
index 0000000000..75af1bf293
--- /dev/null
+++ b/lib/snmp/src/compile/depend.mk
@@ -0,0 +1,46 @@
+#-*-makefile-*- ; force emacs to enter makefile-mode
+
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2004-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%
+
+snmpc_mib_gram.erl: snmpc_mib_gram.yrl
+
+$(EBIN)/snmpc.$(EMULATOR): \
+ ../../include/snmp_types.hrl \
+ snmpc.erl \
+ snmpc.hrl
+
+$(EBIN)/snmpc_lib.$(EMULATOR): \
+ ../../include/snmp_types.hrl \
+ snmpc_lib.erl \
+ snmpc.hrl
+
+$(EBIN)/snmpc_tok.$(EMULATOR): \
+ snmpc_tok.erl
+
+$(EBIN)/snmpc_misc.$(EMULATOR): \
+ ../../include/snmp_types.hrl \
+ snmpc_misc.erl
+
+$(EBIN)/snmpc_mib_to_hrl.$(EMULATOR): \
+ ../../include/snmp_types.hrl \
+ snmpc_mib_to_hrl.erl
+
+$(EBIN)/snmpc_mib_gram.$(EMULATOR): \
+ ../../include/snmp_types.hrl \
+ snmpc_mib_gram.erl
+
diff --git a/lib/snmp/src/compile/modules.mk b/lib/snmp/src/compile/modules.mk
new file mode 100644
index 0000000000..6365b0e694
--- /dev/null
+++ b/lib/snmp/src/compile/modules.mk
@@ -0,0 +1,36 @@
+#-*-makefile-*- ; force emacs to enter makefile-mode
+
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2004-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%
+
+PARSER_SRC = snmpc_mib_gram.yrl
+
+PARSER_MODULE = $(PARSER_SRC:%.yrl=%)
+
+MODULES = \
+ $(PARSER_MODULE) \
+ snmpc \
+ snmpc_lib \
+ snmpc_mib_to_hrl \
+ snmpc_misc \
+ snmpc_tok
+
+
+INTERNAL_HRL_FILES = \
+ snmpc.hrl \
+ snmpc_lib.hrl \
+ snmpc_misc.hrl
diff --git a/lib/snmp/src/compile/snmpc.erl b/lib/snmp/src/compile/snmpc.erl
new file mode 100644
index 0000000000..8a1f15d4a4
--- /dev/null
+++ b/lib/snmp/src/compile/snmpc.erl
@@ -0,0 +1,1358 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-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%
+%%
+-module(snmpc).
+
+%% API
+-export([compile/1, compile/2, compile/3,
+ mib_to_hrl/1, mib_to_hrl/3,
+ is_consistent/1]).
+
+%% Debug
+-export([look_at/1]).
+
+%% Internal Exports
+-export([init/3]).
+
+-include_lib("stdlib/include/erl_compile.hrl").
+-include("snmp_types.hrl").
+-include("snmpc.hrl").
+-include("snmpc_lib.hrl").
+
+
+look_at(Mib) ->
+ io:format("~p ~n", [snmpc_lib:look_at(Mib)]).
+
+
+%%-----------------------------------------------------------------
+%% Misc compiler stuff
+%%-----------------------------------------------------------------
+
+is_consistent(Filenames) ->
+ snmpc_lib:is_consistent(Filenames).
+
+mib_to_hrl(MibName) ->
+ snmpc_mib_to_hrl:convert(MibName).
+
+mib_to_hrl(MibName, HrlFile, Opts) ->
+ snmpc_mib_to_hrl:compile(MibName, HrlFile, Opts).
+
+
+%%%-----------------------------------------------------------------
+%%% Interface for erl_compile.
+%%%-----------------------------------------------------------------
+
+compile(Input, _Output, Options) ->
+ case compile(Input, make_options(Options)) of
+ {ok, _} ->
+ ok;
+ {error, Reason} ->
+ io:format("~p", [Reason]),
+ error
+ end.
+
+%% Converts generic options to format expected by compile/2
+
+make_options(#options{includes = Incs,
+ outdir = Outdir,
+ warning = Warning,
+ specific = Spec}) ->
+
+ OutdirOpt = {outdir, Outdir},
+
+ WarningOpt =
+ case Warning of
+ 0 -> {warnings, false};
+ _ -> {warnings, true}
+ end,
+
+ IncludeOpt =
+ {i, case Incs of
+ [] ->
+ [""];
+ _ ->
+ lists:map(fun(Dir) -> Dir++"/" end, Incs)
+ end},
+
+ [WarningOpt, OutdirOpt, IncludeOpt | Spec].
+
+%% Returns: {ok, File}|{error, Reason}
+compile([AtomFilename]) when is_atom(AtomFilename) ->
+ compile(atom_to_list(AtomFilename), []), % from cmd line
+ halt();
+compile(FileName) ->
+ compile(FileName, []).
+
+
+%%----------------------------------------------------------------------
+%% Options:
+%% {deprecated, bool()} true
+%% {group_check, bool()} true
+%% {db, volatile|persistent|mnesia} volatile
+%% {i, [import_dir_string()]} ["./"]
+%% {il, [import_lib_dir_string()]} []
+%% {warnings, bool()} true
+%% {outdir, string()} "./"
+%% description
+%% reference
+%% imports
+%% module_identity
+%% {module, string()}
+%% no_defs
+%% (hidden) {verbosity, trace|debug|log|info|silence} silence
+%% (hidden) version
+%% (hidden) options
+%%----------------------------------------------------------------------
+
+compile(FileName, Options) when is_list(FileName) ->
+ true = snmpc_misc:is_string(FileName),
+ DefOpts = [{deprecated, true},
+ {group_check, true},
+ {i, ["./"]},
+ {db, volatile},
+ {warnings, true},
+ {outdir, "./"},
+ {il, []}],
+ Opts = update_options(DefOpts, Options),
+ case check_options(Opts) of
+ ok ->
+ maybe_display_version(Opts),
+ maybe_display_options(Opts),
+ Pid = spawn_link(?MODULE,init,[self(),FileName,Opts]),
+ receive
+ {compile_result,R} -> R;
+ {'EXIT',Pid, Reason} when Reason =/= normal ->
+ exit(Reason)
+ end;
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+maybe_display_version(Opts) ->
+ case lists:member(version, Opts) of
+ true ->
+ Vsn = (catch get_version()),
+ io:format("version: ~s~n", [Vsn]);
+ false ->
+ ok
+ end.
+
+get_version() ->
+ MI = ?MODULE:module_info(),
+ Attr = get_info(attributes, MI),
+ Vsn = get_info(app_vsn, Attr),
+ Comp = get_info(compile, MI),
+ Time = get_info(time, Comp),
+ {Year, Month, Day, Hour, Min, Sec} = Time,
+ io_lib:format("~s [~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w]",
+ [Vsn, Year, Month, Day, Hour, Min, Sec]).
+
+maybe_display_options(Opts) ->
+ case lists:member(options, Opts) of
+ true ->
+ {F, A} = get_options(Opts, [], []),
+ io:format("options: " ++ F ++ "~n", A);
+ false ->
+ ok
+ end.
+
+get_options([], Formats, Args) ->
+ {lists:concat(lists:reverse(Formats)), lists:reverse(Args)};
+get_options([{deprecated, Val}|Opts], Formats, Args) ->
+ get_options(Opts, ["~n deprecated: ~w"|Formats], [Val|Args]);
+get_options([{group_check, Val}|Opts], Formats, Args) ->
+ get_options(Opts, ["~n group_check: ~w"|Formats], [Val|Args]);
+get_options([{db, Val}|Opts], Formats, Args) ->
+ get_options(Opts, ["~n db: ~w"|Formats], [Val|Args]);
+get_options([{i, Val}|Opts], Formats, Args) ->
+ get_options(Opts, ["~n i: ~p"|Formats], [Val|Args]);
+get_options([{il, Val}|Opts], Formats, Args) ->
+ get_options(Opts, ["~n il: ~p"|Formats], [Val|Args]);
+get_options([{outdir, Val}|Opts], Formats, Args) ->
+ get_options(Opts, ["~n outdir: ~s"|Formats], [Val|Args]);
+get_options([{description, Val}|Opts], Formats, Args) ->
+ get_options(Opts, ["~n description: ~w"|Formats], [Val|Args]);
+get_options([description|Opts], Formats, Args) ->
+ get_options(Opts, ["~n description"|Formats], Args);
+get_options([{reference, Val}|Opts], Formats, Args) ->
+ get_options(Opts, ["~n reference: ~w"|Formats], [Val|Args]);
+get_options([reference|Opts], Formats, Args) ->
+ get_options(Opts, ["~n reference"|Formats], Args);
+get_options([{warnings, Val}|Opts], Formats, Args) ->
+ get_options(Opts, ["~n warnings: ~w"|Formats], [Val|Args]);
+get_options([{verbosity, Val}|Opts], Formats, Args) ->
+ get_options(Opts, ["~n verbosity: ~w"|Formats], [Val|Args]);
+get_options([imports|Opts], Formats, Args) ->
+ get_options(Opts, ["~n imports"|Formats], Args);
+get_options([module_identity|Opts], Formats, Args) ->
+ get_options(Opts, ["~n module_identity"|Formats], Args);
+get_options([_|Opts], Formats, Args) ->
+ get_options(Opts, Formats, Args).
+
+
+get_info(Key, Info) ->
+ case lists:keysearch(Key, 1, Info) of
+ {value, {Key, Val}} ->
+ Val;
+ false ->
+ throw("undefined")
+ end.
+
+% p(F, A) ->
+% io:format("DBG: " ++ F ++ "~n", A).
+
+update_options([], Options) ->
+ Options;
+update_options([{Key,DefVal}|DefOpts], Options) ->
+ case snmpc_misc:assq(Key, Options) of
+ false ->
+ update_options(DefOpts, [{Key,DefVal}|Options]);
+ {value, Val} when Key =:= i ->
+ Options1 =
+ lists:keyreplace(Key, 1, Options, {Key, Val++DefVal}),
+ update_options(DefOpts, Options1);
+ {value, Val} when Key =:= il ->
+ Options1 =
+ lists:keyreplace(Key, 1, Options, {Key, Val++DefVal}),
+ update_options(DefOpts, Options1);
+ {value, DefVal} -> %% Same value, no need to update
+ update_options(DefOpts, Options);
+ {value, Val} -> %% New value, so update
+ Options1 =
+ lists:keyreplace(Key, 1, Options, {Key, Val}),
+ update_options(DefOpts, Options1)
+ end.
+
+check_options([]) -> ok;
+check_options([no_symbolic_info|T]) -> check_options(T);
+check_options([{outdir, Str} | T]) when is_list(Str) ->
+ check_options(T);
+check_options([{debug, Atom} | T]) when is_atom(Atom) ->
+ check_options(T);
+check_options([{deprecated, Atom} | T]) when is_atom(Atom) ->
+ check_options(T);
+check_options([{group_check, Atom} | T]) when is_atom(Atom) ->
+ check_options(T);
+check_options([{warnings, Bool} | T]) ->
+ check_bool(warnings, Bool),
+ check_options(T);
+check_options([{db, volatile} | T]) ->
+ check_options(T);
+check_options([{db, persistent} | T]) ->
+ check_options(T);
+check_options([{db, mnesia} | T]) ->
+ check_options(T);
+check_options([{i, [Str|_]} | T]) when is_list(Str) ->
+ check_options(T);
+check_options([{il, []} | T]) ->
+ check_options(T);
+check_options([{il, [Str|_]} | T]) when is_list(Str) ->
+ check_options(T);
+check_options([{description, Bool}| T]) ->
+ check_bool(description, Bool),
+ check_options(T);
+check_options([description| T]) -> %% same as {description, true}
+ check_options(T);
+check_options([{reference, Bool}| T]) ->
+ check_bool(reference, Bool),
+ check_options(T);
+check_options([reference| T]) -> %% same as {reference, true}
+ check_options(T);
+check_options([{verbosity, V} | T]) when is_atom(V) ->
+ snmpc_lib:vvalidate(V),
+ check_options(T);
+check_options([version| T]) ->
+ check_options(T);
+check_options([options| T]) ->
+ check_options(T);
+check_options([imports| T]) ->
+ check_options(T);
+check_options([module_identity| T]) ->
+ check_options(T);
+check_options([{module, M} | T]) when is_atom(M) ->
+ check_options(T);
+check_options([no_defs| T]) ->
+ check_options(T);
+check_options([Opt|_]) ->
+ {error, {invalid_option, Opt}}.
+
+
+check_bool(_Key, Bool) when (Bool =:= true) orelse (Bool =:= false) ->
+ ok;
+check_bool(Key, Val) ->
+ {error, {invalid_option, {Key, Val}}}.
+
+get_group_check(Options) ->
+ snmpc_lib:key1search(group_check, Options, true).
+
+get_deprecated(Options) ->
+ snmpc_lib:key1search(deprecated, Options, true).
+
+get_description(Options) ->
+ get_bool_option(description, Options).
+
+get_reference(Options) ->
+ get_bool_option(reference, Options).
+
+get_bool_option(Option, Options) ->
+ case lists:member(Option, Options) of
+ false ->
+ snmpc_lib:key1search(Option, Options, false);
+ true ->
+ true
+ end.
+
+make_description(Message) ->
+ case get(description) of
+ true ->
+ Message;
+ _ ->
+ undefined
+ end.
+
+make_reference(undefined) ->
+ [];
+make_reference(Reference) ->
+ case get(reference) of
+ true ->
+ [{reference, Reference}];
+ _ ->
+ []
+ end.
+
+
+
+%%----------------------------------------------------------------------
+%% verbosity stuff
+%%----------------------------------------------------------------------
+
+%% Verbosity level is selected from three (historical reasons)
+%% options: warnings, debug and verbosity
+%% - If warnings is true, then verbosity is _atleast_ warning
+%% (even if the verbosity flag is set to silence)
+%% - If debug is true, the verbosity is _atleast_ log
+%% - Otherwise, verbosity is used as is.
+get_verbosity(Options) ->
+ WarningsSeverity =
+ case snmpc_lib:key1search(warnings, Options) of
+ true ->
+ warning;
+ _ ->
+ silence
+ end,
+ case snmpc_lib:key1search(verbosity, Options) of
+ undefined ->
+ %% Backward compatible: If not defined then try debug and convert
+ case snmpc_lib:key1search(debug, Options, false) of
+ true ->
+ log;
+ false ->
+ WarningsSeverity
+ end;
+ silence ->
+ WarningsSeverity;
+ Verbosity ->
+ Verbosity
+ end.
+
+
+%%----------------------------------------------------------------------
+%% The compile process.
+%%----------------------------------------------------------------------
+
+init(From, MibFileName, Options) ->
+ {A,B,C} = now(),
+ random:seed(A,B,C),
+ put(options, Options),
+ put(verbosity, get_verbosity(Options)),
+ put(description, get_description(Options)),
+ put(reference, get_reference(Options)),
+ File = filename:rootname(MibFileName, ".mib"),
+ put(filename, filename:basename(File ++ ".mib")),
+ R = case catch c_impl(File) of
+ {ok, OutFile} -> {ok, OutFile};
+ {'EXIT',error} -> {error, compilation_failed};
+ Error -> exit(Error)
+ end,
+ From ! {compile_result, R}.
+
+
+c_impl(File) ->
+ {ok, PData} = parse(File),
+ ?vtrace("Syntax analysis:"
+ "~n PData: ~p", [PData]),
+ MibName = compile_parsed_data(PData),
+ ?vtrace("Compiler output:"
+ "~n CDATA: ~p", [get(cdata)]),
+ save(File, MibName, get(options)).
+
+compile_parsed_data(#pdata{mib_name = MibName,
+ imports = Imports,
+ defs = Definitions}) ->
+ snmpc_lib:import(Imports),
+ update_imports(Imports),
+ Deprecated = get_deprecated(get(options)),
+ definitions_loop(Definitions, Deprecated),
+ MibName.
+
+update_imports(Imports) ->
+ case lists:member(imports, get(options)) of
+ true ->
+ IMPs = do_update_imports(Imports, []),
+ CDATA = get(cdata),
+ put(cdata, CDATA#cdata{imports = IMPs});
+ false ->
+ ok
+ end.
+
+do_update_imports([], Acc) ->
+ lists:reverse(Acc);
+do_update_imports([{{Mib, ImportsFromMib0},_Line}|Imports], Acc) ->
+ ImportsFromMib = [Name || {_, Name} <- ImportsFromMib0],
+ Import = {Mib, ImportsFromMib},
+ do_update_imports(Imports, [Import|Acc]).
+
+
+update_status(Name, Status) ->
+ #cdata{status_ets = Ets} = get(cdata),
+ ets:insert(Ets, {Name, Status}).
+
+
+%% A deprecated object
+definitions_loop([{#mc_object_type{name = ObjName, status = deprecated},
+ Line}|T],
+ false) ->
+ %% May be implemented but the compiler chooses not to.
+ ?vinfo2("object_type ~w is deprecated => ignored", [ObjName], Line),
+ update_status(ObjName, deprecated),
+ definitions_loop(T, false);
+
+%% A obsolete object
+definitions_loop([{#mc_object_type{name = ObjName, status = obsolete},
+ Line}|T],
+ Deprecated) ->
+ ?vlog2("object_type ~w is obsolete => ignored", [ObjName], Line),
+ %% No need to implement a obsolete object
+ update_status(ObjName, obsolete),
+ ensure_macro_imported('OBJECT-TYPE', Line),
+ definitions_loop(T, Deprecated);
+
+%% Defining a table
+definitions_loop([{#mc_object_type{name = NameOfTable,
+ syntax = {{sequence_of, SeqName}, _},
+ max_access = Taccess,
+ kind = Kind,
+ status = Tstatus,
+ description = Desc1,
+ units = Tunits,
+ reference = Ref,
+ name_assign = Tindex},
+ Tline},
+ {#mc_object_type{name = NameOfEntry,
+ syntax = {{type, SeqName}, TEline},
+ max_access = 'not-accessible',
+ kind = {table_entry, IndexingInfo},
+ status = Estatus,
+ description = Desc2,
+ units = Eunits,
+ name_assign = {NameOfTable,[1]}},
+ Eline},
+ {#mc_sequence{name = SeqName,
+ fields = FieldList},
+ Sline}|ColsEtc],
+ Deprecated) ->
+ ?vlog("defloop -> "
+ "[object_type(sequence_of),object_type(type,[1]),sequence]:"
+ "~n NameOfTable: ~p"
+ "~n SeqName: ~p"
+ "~n Taccess: ~p"
+ "~n Kind: ~p"
+ "~n Tstatus: ~p"
+ "~n Tindex: ~p"
+ "~n Tunits: ~p"
+ "~n Tline: ~p"
+ "~n NameOfEntry: ~p"
+ "~n TEline: ~p"
+ "~n IndexingInfo: ~p"
+ "~n Estatus: ~p"
+ "~n Eunits: ~p"
+ "~n Ref: ~p"
+ "~n Eline: ~p"
+ "~n FieldList: ~p"
+ "~n Sline: ~p",
+ [NameOfTable,SeqName,Taccess,Kind,Tstatus,
+ Tindex,Tunits,Tline,
+ NameOfEntry,TEline,IndexingInfo,Estatus,Eunits,Ref,Eline,
+ FieldList,Sline]),
+ update_status(NameOfTable, Tstatus),
+ update_status(NameOfEntry, Estatus),
+ update_status(SeqName, undefined),
+ ensure_macro_imported('OBJECT-TYPE', Tline),
+ test_table(NameOfTable,Taccess,Kind,Tindex,Tline),
+ {Tfather,Tsubindex} = Tindex,
+ snmpc_lib:register_oid(Tline,NameOfTable,Tfather,Tsubindex),
+ Description1 = make_description(Desc1),
+ TableME = #me{aliasname = NameOfTable,
+ entrytype = table,
+ access = 'not-accessible',
+ description = Description1,
+ units = Tunits},
+ snmpc_lib:register_oid(TEline,NameOfEntry,NameOfTable,[1]),
+ Description2 = make_description(Desc2),
+ TableEntryME = #me{aliasname = NameOfEntry,
+ entrytype = table_entry,
+ assocList = [{table_entry_with_sequence, SeqName}],
+ access = 'not-accessible',
+ description = Description2,
+ units = Eunits},
+ {ColMEs, RestObjs} =
+ define_cols(ColsEtc, 1, FieldList, NameOfEntry, NameOfTable, []),
+ TableInfo = snmpc_lib:make_table_info(Eline, NameOfTable,
+ IndexingInfo, ColMEs),
+ snmpc_lib:add_cdata(#cdata.mes,
+ [TableEntryME,
+ TableME#me{assocList=[{table_info,
+ TableInfo} | make_reference(Ref)]} |
+ ColMEs]),
+ definitions_loop(RestObjs, Deprecated);
+
+definitions_loop([{#mc_object_type{name = NameOfTable,
+ syntax = {{sequence_of, SeqName},_},
+ max_access = Taccess,
+ kind = Kind,
+ status = Tstatus,
+ description = Desc1,
+ units = Tunits,
+ reference = Ref,
+ name_assign = Tindex}, Tline},
+ {#mc_object_type{name = NameOfEntry,
+ syntax = {{type, SeqName},_},
+ max_access = 'not-accessible',
+ kind = {table_entry,IndexingInfo},
+ status = Estatus,
+ description = Desc2,
+ units = Eunits,
+ name_assign = BadOID}, Eline},
+ {#mc_sequence{name = SeqName,
+ fields = FieldList}, Sline}|ColsEtc],
+ Deprecated) ->
+ ?vlog("defloop -> "
+ "[object_type(sequence_of),object_type(type),sequence(fieldList)]:"
+ "~n NameOfTable: ~p"
+ "~n SeqName: ~p"
+ "~n Taccess: ~p"
+ "~n Kind: ~p"
+ "~n Tstatus: ~p"
+ "~n Tindex: ~p"
+ "~n Tunits: ~p"
+ "~n Tline: ~p"
+ "~n NameOfEntry: ~p"
+ "~n IndexingInfo: ~p"
+ "~n Estatus: ~p"
+ "~n BadOID: ~p"
+ "~n Eunits: ~p"
+ "~n Ref: ~p"
+ "~n Eline: ~p"
+ "~n FieldList: ~p"
+ "~n Sline: ~p",
+ [NameOfTable,SeqName,Taccess,Kind,Tstatus,
+ Tindex,Tunits,Tline,
+ NameOfEntry,IndexingInfo,Estatus,BadOID,Eunits,Ref,Eline,
+ FieldList,Sline]),
+ update_status(NameOfTable, Tstatus),
+ update_status(NameOfEntry, Estatus),
+ update_status(SeqName, undefined),
+ ensure_macro_imported('OBJECT-TYPE', Tline),
+ snmpc_lib:print_error("Bad TableEntry OID definition (~w)",
+ [BadOID],Eline),
+ test_table(NameOfTable,Taccess,Kind,Tindex,Tline),
+ {Tfather,Tsubindex} = Tindex,
+ snmpc_lib:register_oid(Tline,NameOfTable,Tfather,Tsubindex),
+ Description1 = make_description(Desc1),
+ TableME = #me{aliasname = NameOfTable,
+ entrytype = table,
+ access = 'not-accessible',
+ description = Description1,
+ units = Tunits},
+ Description2 = make_description(Desc2),
+ TableEntryME = #me{aliasname = NameOfEntry,
+ entrytype = table_entry,
+ access = 'not-accessible',
+ assocList = [{table_entry_with_sequence,SeqName}],
+ description = Description2,
+ units = Eunits},
+ {ColMEs, RestObjs} =
+ define_cols(ColsEtc, 1, FieldList, NameOfEntry, NameOfTable, []),
+ TableInfo = snmpc_lib:make_table_info(Eline, NameOfTable,
+ IndexingInfo, ColMEs),
+ snmpc_lib:add_cdata(#cdata.mes,
+ [TableEntryME,
+ TableME#me{assocList=[{table_info,
+ TableInfo} | make_reference(Ref)]} |
+ ColMEs]),
+ definitions_loop(RestObjs, Deprecated);
+
+definitions_loop([{#mc_new_type{name = NewTypeName,
+ macro = Macro,
+ syntax = OldType,
+ display_hint = DisplayHint},Line}|T],
+ Deprecated) ->
+ ?vlog2("defloop -> new_type:"
+ "~n Macro: ~p"
+ "~n NewTypeName: ~p"
+ "~n OldType: ~p"
+ "~n DisplayHint: ~p",
+ [Macro, NewTypeName, OldType, DisplayHint], Line),
+ ensure_macro_imported(Macro,Line),
+ Types = (get(cdata))#cdata.asn1_types,
+ case lists:keysearch(NewTypeName, #asn1_type.aliasname, Types) of
+ {value,_} ->
+ snmpc_lib:print_error("Type ~w already defined.",
+ [NewTypeName],Line);
+ false ->
+ %% NameOfOldType = element(2,OldType),
+ ASN1 = snmpc_lib:make_ASN1type(OldType),
+ snmpc_lib:add_cdata(#cdata.asn1_types,
+ [ASN1#asn1_type{aliasname = NewTypeName,
+ imported = false,
+ display_hint = DisplayHint}])
+ end,
+ definitions_loop(T, Deprecated);
+
+%% Plain variable
+definitions_loop([{#mc_object_type{name = NewVarName,
+ syntax = Type,
+ max_access = Access,
+ kind = {variable, DefVal},
+ status = Status,
+ description = Desc1,
+ units = Units,
+ name_assign = {Parent,SubIndex}},Line} |T],
+ Deprecated) ->
+ ?vlog2("defloop -> object_type (variable):"
+ "~n NewVarName: ~p"
+ "~n Type: ~p"
+ "~n Access: ~p"
+ "~n DefVal: ~p"
+ "~n Status: ~p"
+ "~n Units: ~p"
+ "~n Parent: ~p"
+ "~n SubIndex: ~p",
+ [NewVarName, Type, Access, DefVal,
+ Status, Units, Parent, SubIndex], Line),
+ update_status(NewVarName, Status),
+ snmpc_lib:test_father(Parent, NewVarName, SubIndex, Line),
+ ASN1type = snmpc_lib:make_ASN1type(Type),
+ snmpc_lib:register_oid(Line, NewVarName, Parent, SubIndex),
+ Description1 = make_description(Desc1),
+ NewME = #me{aliasname = NewVarName,
+ asn1_type = ASN1type,
+ entrytype = variable,
+ access = Access,
+ description = Description1,
+ units = Units,
+ assocList = DefVal},
+ NewME2 = snmpc_lib:resolve_defval(NewME),
+ %% hmm, should this be done in resolve_defval?
+ VI = snmpc_lib:make_variable_info(NewME2),
+ snmpc_lib:add_cdata(#cdata.mes,
+ [NewME2#me{assocList = [{variable_info, VI}]}]),
+ definitions_loop(T, Deprecated);
+
+definitions_loop([{#mc_module_identity{name = NewVarName,
+ last_updated = LU,
+ organization = Org,
+ contact_info = CI,
+ description = Desc,
+ revisions = Revs0,
+ name_assign = {Parent, SubIndex}},
+ Line}|T],
+ Deprecated) ->
+ ?vlog2("defloop -> module-identity: "
+ "~n NewVarName: ~p"
+ "~n LU: ~p"
+ "~n Org: ~p"
+ "~n CI: ~p"
+ "~n Desc: ~p"
+ "~n Revs0: ~p"
+ "~n Parent: ~p"
+ "~n SubIndex: ~p",
+ [NewVarName, LU, Org, CI, Desc, Revs0, Parent, SubIndex], Line),
+ ensure_macro_imported('MODULE-IDENTITY', Line),
+ snmpc_lib:register_oid(Line, NewVarName, Parent, SubIndex),
+ Revs = [{R,D}||#mc_revision{revision = R,description = D} <- Revs0],
+ MI = #module_identity{last_updated = LU,
+ organization = Org,
+ contact_info = CI,
+ description = Desc,
+ revisions = Revs},
+ CDATA = get(cdata),
+ put(cdata, CDATA#cdata{module_identity = MI}),
+ snmpc_lib:add_cdata(
+ #cdata.mes,
+ [snmpc_lib:makeInternalNode2(false, NewVarName)]),
+ definitions_loop(T, Deprecated);
+
+definitions_loop([{#mc_internal{name = NewVarName,
+ macro = Macro,
+ parent = Parent,
+ sub_index = SubIndex},Line}|T],
+ Deprecated) ->
+ ?vlog2("defloop -> internal:"
+ "~n NewVarName: ~p"
+ "~n Macro: ~p"
+ "~n Parent: ~p"
+ "~n SubIndex: ~p",
+ [NewVarName, Macro, Parent, SubIndex], Line),
+ ensure_macro_imported(Macro, Line),
+ snmpc_lib:register_oid(Line, NewVarName, Parent, SubIndex),
+ snmpc_lib:add_cdata(
+ #cdata.mes,
+ [snmpc_lib:makeInternalNode2(false, NewVarName)]),
+ definitions_loop(T, Deprecated);
+
+%% A trap message
+definitions_loop([{#mc_trap{name = TrapName,
+ enterprise = EnterPrise,
+ vars = Variables,
+ description = Desc1,
+ num = SpecificCode}, Line}|T],
+ Deprecated) ->
+ ?vlog2("defloop -> trap:"
+ "~n TrapName: ~p"
+ "~n EnterPrise: ~p"
+ "~n Variables: ~p"
+ "~n SpecificCode: ~p",
+ [TrapName, EnterPrise, Variables, SpecificCode], Line),
+ update_status(TrapName, undefined),
+ CDATA = get(cdata),
+ snmpc_lib:check_trap_name(EnterPrise, Line, CDATA#cdata.mes),
+ Descriptions = make_description(Desc1),
+ Trap = #trap{trapname = TrapName,
+ enterpriseoid = EnterPrise,
+ specificcode = SpecificCode,
+ %% oidobjects: Store Variables temporary here.
+ %% This will be replaced later in the
+ %% get_final_mib function by a call to
+ %% the update_trap_objects function.
+ oidobjects = Variables,
+ description = Descriptions},
+ lists:foreach(fun(Trap2) -> snmpc_lib:check_trap(Trap2, Trap, Line) end,
+ CDATA#cdata.traps),
+ snmpc_lib:add_cdata(#cdata.traps, [Trap]),
+ definitions_loop(T, Deprecated);
+
+definitions_loop([{#mc_object_type{name = NameOfEntry,
+ syntax = Type,
+ max_access = Eaccess,
+ kind = {table_entry, Index},
+ status = Estatus,
+ name_assign = SubIndex},Eline}|T],
+ Deprecated) ->
+ ?vlog("defloop -> object_type (table_entry):"
+ "~n NameOfEntry: ~p"
+ "~n Type: ~p"
+ "~n Eaccess: ~p"
+ "~n Index: ~p"
+ "~n Estatus: ~p"
+ "~n SubIndex: ~p"
+ "~n SubIndex: ~p"
+ "~n Eline: ~p",
+ [NameOfEntry, Type, Eaccess, Index, Estatus, SubIndex, Eline]),
+ update_status(NameOfEntry, Estatus),
+ snmpc_lib:print_error("Misplaced TableEntry definition (~w)",
+ [NameOfEntry], Eline),
+ definitions_loop(T, Deprecated);
+
+definitions_loop([{#mc_notification{name = TrapName,
+ status = deprecated}, Line}|T],
+ false) ->
+ ?vinfo2("defloop -> notification ~w is deprecated => ignored",
+ [TrapName], Line),
+ update_status(TrapName, deprecated),
+ ensure_macro_imported('NOTIFICATION-TYPE', Line),
+ definitions_loop(T, false);
+
+definitions_loop([{#mc_notification{name = TrapName,
+ status = obsolete}, Line}|T],
+ Deprecated) ->
+ ?vlog2("defloop -> notification ~w is obsolete => ignored",
+ [TrapName], Line),
+ update_status(TrapName, obsolete),
+ ensure_macro_imported('NOTIFICATION-TYPE', Line),
+ definitions_loop(T, Deprecated);
+
+definitions_loop([{#mc_notification{name = TrapName,
+ vars = Variables,
+ status = Status,
+ description = Desc,
+ name_assign = {Parent, SubIndex}},Line}|T],
+ Deprecated) ->
+ ?vlog2("defloop -> notification:"
+ "~n TrapName: ~p"
+ "~n Variables: ~p"
+ "~n Status: ~p"
+ "~n Parent: ~p"
+ "~n SubIndex: ~p",
+ [TrapName, Variables, Status, Parent, SubIndex], Line),
+ update_status(TrapName, Status),
+ ensure_macro_imported('NOTIFICATION-TYPE', Line),
+ CDATA = get(cdata),
+ snmpc_lib:register_oid(Line, TrapName, Parent, SubIndex),
+ Descriptions = make_description(Desc),
+ Notif = #notification{trapname = TrapName,
+ description = Descriptions,
+ %% oidobjects: Store Variables temporary here.
+ %% This will be replaced later in the
+ %% get_final_mib function by a call to
+ %% the update_trap_objects function.
+ oidobjects = Variables},
+ snmpc_lib:check_notification(Notif, Line, CDATA#cdata.traps),
+ snmpc_lib:add_cdata(#cdata.traps, [Notif]),
+ definitions_loop(T, Deprecated);
+
+definitions_loop([{#mc_module_compliance{name = Name},Line}|T], Deprecated) ->
+ ?vlog2("defloop -> module_compliance:"
+ "~n Name: ~p", [Name], Line),
+ ensure_macro_imported('MODULE-COMPLIANCE', Line),
+ definitions_loop(T, Deprecated);
+
+definitions_loop([{#mc_object_group{name = Name,
+ objects = GroupObjects,
+ status = Status,
+ description = Desc,
+ reference = Ref,
+ name_assign = {Parent,SubIndex}}, Line}|T],
+ Deprecated) ->
+ ?vlog2("defloop -> object_group ~p:"
+ "~n GroupObjects: ~p"
+ "~n Status: ~p"
+ "~n Desc: ~p"
+ "~n Ref: ~p"
+ "~n Parent: ~p"
+ "~n SubIndex: ~p",
+ [Name, GroupObjects, Status, Desc, Ref, Parent, SubIndex], Line),
+ ensure_macro_imported('OBJECT-GROUP', Line),
+ GroupBool = get_group_check(get(options)),
+ case GroupBool of
+ true ->
+ snmpc_lib:add_cdata(#cdata.objectgroups,
+ [{Name,GroupObjects,Line}]),
+ %% Check that the group members has been defined
+ %% and that they have the correct status
+ snmpc_lib:check_object_group(Name, GroupObjects,
+ Line, Status);
+ _ ->
+ ok
+ end,
+
+ update_status(Name, Status),
+ snmpc_lib:test_father(Parent, Name, SubIndex, Line),
+ snmpc_lib:register_oid(Line, Name, Parent, SubIndex),
+ Description = make_description(Desc),
+ NewME = #me{aliasname = Name,
+ entrytype = group,
+ access = 'not-accessible',
+ description = Description,
+ assocList = [{kind, object},
+ {objects, GroupObjects}]},
+ snmpc_lib:add_cdata(#cdata.mes, [NewME]),
+
+ definitions_loop(T, Deprecated);
+
+definitions_loop([{#mc_notification_group{name = Name,
+ objects = GroupObjects,
+ status = Status,
+ description = Desc,
+ reference = Ref,
+ name_assign = {Parent,SubIndex}},
+ Line}
+ |T], Deprecated) ->
+ ?vlog2("defloop -> notification_group ~p:"
+ "~n GroupObjects: ~p"
+ "~n Status: ~p"
+ "~n Desc: ~p"
+ "~n Ref: ~p"
+ "~n Parent: ~p"
+ "~n SubIndex: ~p",
+ [Name, GroupObjects, Status, Desc, Ref, Parent, SubIndex], Line),
+ ensure_macro_imported('NOTIFICATION-GROUP', Line),
+ GroupBool = get_group_check(get(options)),
+ case GroupBool of
+ true ->
+ snmpc_lib:add_cdata(#cdata.notificationgroups,
+ [{Name,GroupObjects,Line}]),
+
+ %% Check that the group members has been defined
+ %% and that they have the correct status
+ snmpc_lib:check_notification_group(Name, GroupObjects,
+ Line, Status);
+ _ ->
+ ok
+ end,
+
+ update_status(Name, Status),
+ snmpc_lib:test_father(Parent, Name, SubIndex, Line),
+ snmpc_lib:register_oid(Line, Name, Parent, SubIndex),
+ Description = make_description(Desc),
+ NewME = #me{aliasname = Name,
+ entrytype = group,
+ access = 'not-accessible',
+ description = Description,
+ assocList = [{kind, notification},
+ {objects, GroupObjects}]},
+ snmpc_lib:add_cdata(#cdata.mes, [NewME]),
+
+ definitions_loop(T, Deprecated);
+
+definitions_loop([{#mc_object_type{name = NameOfTable,
+ syntax = {{sequence_of, SeqName},_},
+ status = Tstatus},Tline},
+ Entry, Seq|T],
+ Deprecated) ->
+ ?vlog("defloop -> object_type (sequence_of): "
+ "~n NameOfTable: ~p"
+ "~n SeqName: ~p"
+ "~n Tline: ~p"
+ "~n Entry: ~p"
+ "~n Seq: ~p",
+ [NameOfTable, SeqName, Tline, Entry, Seq]),
+ update_status(NameOfTable, Tstatus),
+ case Entry of
+ {#mc_object_type{syntax = {{type, SeqName},_line},
+ max_access = 'not-accessible',
+ kind = {table_entry, _IndexingInfo},
+ name_assign = {_NameOfTable,[1]}}, _Eline} ->
+ case Seq of
+ {#mc_sequence{name = SeqName}, Sline} ->
+ snmpc_lib:error("Internal error. Correct incorrect "
+ "table (~p,~w).",[SeqName,Sline],
+ Tline);
+ _ ->
+ ?vinfo("defloop -> Invalid sequence: ~p", [Seq]),
+ snmpc_lib:print_error(
+ "Invalid SEQUENCE OF '~p'.",
+ [safe_elem(1,safe_elem(2,Seq))],Tline)
+ end;
+ Else ->
+ ?vinfo("defloop -> Invalid table entry: "
+ "~n ~p", [Else]),
+ snmpc_lib:print_error(
+ "Invalid TableEntry '~p' (check STATUS, Sequence name, Oid)",
+ [safe_elem(1,safe_elem(2,Entry))],Tline)
+ end,
+ definitions_loop(T, Deprecated);
+
+definitions_loop([{#mc_object_type{name = NameOfTable,
+ syntax = {{sequence_of, SeqName},_},
+ status = Tstatus},Tline}|T],
+ Deprecated) ->
+ ?vlog("defloop -> object_type (sequence_of):"
+ "~n object_type: ~p"
+ "~n sequence_of: ~p"
+ "~n Tline: ~p", [NameOfTable, SeqName, Tline]),
+ update_status(NameOfTable, Tstatus),
+ snmpc_lib:print_error("Invalid statements following table ~p.",
+ [NameOfTable],Tline),
+ definitions_loop(T, Deprecated);
+
+definitions_loop([{#mc_sequence{name = SeqName,
+ fields = _FieldList},Line}|T],
+ Deprecated) ->
+ ?vwarning2("Unexpected SEQUENCE ~w => ignoring", [SeqName], Line),
+ definitions_loop(T, Deprecated);
+
+definitions_loop([{Obj,Line}|T], Deprecated) ->
+ ?vinfo2("defloop -> unknown error"
+ "~n Obj: ~p", [Obj], Line),
+ snmpc_lib:print_error("Unknown Error in MIB. "
+ "Can't describe the error better than this: ~999p ignored."
+ " Please send a trouble report to [email protected].",
+ [Obj], Line),
+ definitions_loop(T, Deprecated);
+
+definitions_loop([], _Deprecated) ->
+ ?vlog("defloop -> done", []),
+ ok.
+
+safe_elem(N,T) ->
+ case catch(element(N,T)) of
+ {'EXIT',_} ->
+ "no more information available";
+ X -> X
+ end.
+
+%% A correct column
+define_cols([{#mc_object_type{name = NameOfCol,
+ syntax = Type1,
+ max_access = Access,
+ kind = {variable,Defval},
+ status = Status,
+ description = Desc,
+ units = Units,
+ name_assign = {NameOfEntry,[SubIndex]}},
+ Oline}|Rest],
+ SubIndex,
+ [{NameOfCol,Type2}|Fields], NameOfEntry, TableName, ColMEs) ->
+ ?vlog("defcols -> object_type (variable):"
+ "~n NameOfCol: ~p"
+ "~n Type1: ~p"
+ "~n Access: ~p"
+ "~n Defval: ~p"
+ "~n Status ~p"
+ "~n Units ~p"
+ "~n NameOfEntry ~p"
+ "~n Oline: ~p",
+ [NameOfCol, Type1, Access, Defval, Status, Units,
+ NameOfEntry, Oline]),
+ update_status(NameOfCol, Status),
+ Deprecated = get_deprecated(get(options)),
+ ASN1type = snmpc_lib:make_ASN1type(Type1),
+ case (snmpc_lib:make_ASN1type(Type2))#asn1_type.bertype of
+ T2 when T2 == ASN1type#asn1_type.bertype -> ok;
+ _Else ->
+ snmpc_lib:error(
+ "Types for ~p differs from the SEQUENCE definition. ",
+ [NameOfCol],Oline)
+ end,
+ NewAccess = % a simple way to get the obsolete behaviour
+ if
+ Status =:= obsolete ->
+ %% Be quiet and don't implement
+ 'not-accessible';
+ (Status =:= deprecated) andalso (Deprecated =:= false) ->
+ %% The compiler chooses not to implement the column.
+ ?vinfo2("object_type ~w is deprecated => ignored",
+ [NameOfCol], Oline),
+ 'not-accessible';
+ true -> Access
+ end,
+ snmpc_lib:register_oid(Oline,NameOfCol,NameOfEntry,[SubIndex]),
+ Description = make_description(Desc),
+ ColumnME = snmpc_lib:resolve_defval(
+ #me{oid = SubIndex,
+ aliasname = NameOfCol,
+ asn1_type = ASN1type,
+ entrytype = table_column,
+ access = NewAccess,
+ description = Description,
+ units = Units, %% Propably not usefull
+ assocList = [{table_name,TableName} | Defval]}),
+ define_cols(Rest,SubIndex+1,Fields,NameOfEntry,TableName,
+ [ColumnME|ColMEs]);
+
+%% A "hole" (non-consecutive columns) in the table.
+%% Implemented as a not-accessible column so Col always is index in
+%% row tuple.
+define_cols([{#mc_object_type{name = NameOfCol,
+ syntax = Type1,
+ max_access = Access,
+ kind = Kind,
+ status = Status,
+ name_assign = {NameOfEntry,[SubIndex]}},
+ Oline}|Rest],
+ ExpectedSubIndex, Fields, NameOfEntry, TableName, ColMEs)
+ when SubIndex > ExpectedSubIndex ->
+ ?vlog("defcols -> object_type (non consecutive cols):"
+ "~n NameOfCol: ~p"
+ "~n Type1: ~p"
+ "~n Access: ~p"
+ "~n Status ~p"
+ "~n NameOfEntry ~p"
+ "~n Oline: ~p",
+ [NameOfCol, Type1, Access, Status, NameOfEntry, Oline]),
+ update_status(NameOfCol, Status),
+ Int = {{type, 'INTEGER'},Oline},
+ GeneratedColumn =
+ %% be sure to use an invalid column name here!
+ {#mc_object_type{name = '$no_name$',
+ syntax = Int,
+ max_access = 'not-accessible',
+ kind = {variable, [{defval,0}]},
+ status = current,
+ description = undefined,
+ name_assign = {NameOfEntry, [ExpectedSubIndex]}},
+ Oline},
+ define_cols([GeneratedColumn,
+ {#mc_object_type{name = NameOfCol,
+ syntax = Type1,
+ max_access = Access,
+ kind = Kind,
+ status = Status,
+ description = undefined,
+ name_assign = {NameOfEntry,[SubIndex]}},
+ Oline}|Rest], ExpectedSubIndex,
+ [{'$no_name$', Int}|Fields], NameOfEntry, TableName,ColMEs) ;
+
+%% Ok. done. All fields are eaten.
+define_cols(Rest, _SubIndex, [], _NameOfEntry, _TableName, ColMEs) ->
+ {ColMEs, Rest};
+
+
+%% Error Handling
+
+%% The name of the field and object is the same
+define_cols([{#mc_object_type{name = NameOfCol,
+ kind = Kind,
+ name_assign = SubIndex}, Oline}|Rest],
+ SubIndex2, [{NameOfCol, _Type2}|Fields],
+ NameOfEntry, TableName, ColMEs) ->
+ ?vlog("defcols -> object_type (name of field and object is the same):"
+ "~n NameOfCol: ~p"
+ "~n Kind: ~p"
+ "~n SubIndex: ~p"
+ "~n Oline: ~p"
+ "~n SubIndex2: ~p"
+ "~n NameOfEntry ~p"
+ "~n TableName ~p",
+ [NameOfCol,Kind,SubIndex,Oline,SubIndex2,NameOfEntry,TableName]),
+ SIok = case SubIndex of
+ {Parent,[_SI]} when Parent =/= NameOfEntry ->
+ snmpc_lib:print_error(
+ "Invalid parent ~p for table column ~p (should be ~p).",
+ [Parent,NameOfCol,NameOfEntry],Oline),
+ error;
+ {NameOfEntry,[SubIndex2]} ->
+ ok;
+ {NameOfEntry,[SI]} ->
+ snmpc_lib:print_error(
+ "Invalid column number ~p for column ~p.",
+ [SI, NameOfCol], Oline),
+ error;
+ _Q ->
+ snmpc_lib:print_error(
+ "Invalid parent for column ~p.",[NameOfCol],Oline),
+ error
+ end,
+ Kok = case Kind of
+ {variable,_} ->
+ ok;
+ _Q2 ->
+ snmpc_lib:print_error(
+ "Expected a table column.",[],Oline),
+ error
+ end,
+ case {SIok, Kok} of
+ {ok, ok} ->
+ snmpc_lib:print_error("Invalid table column definition for"
+ " ~p.",[NameOfCol],Oline);
+ _Q4 ->
+ done % already reported
+ end,
+ define_cols(Rest,SubIndex2+1,Fields,NameOfEntry,TableName,ColMEs);
+
+%% It's an object-type but everything else is wrong
+define_cols([{#mc_object_type{name = NameOfCol},Oline}|Rest],SubIndex2,Fields,
+ NameOfEntry,TableName,ColMEs) ->
+ snmpc_lib:print_error(
+ "Number of columns differs from SEQUENCE definition (object:~p).",
+ [NameOfCol],Oline),
+ define_cols(Rest,SubIndex2+1,Fields,NameOfEntry,TableName,ColMEs);
+
+define_cols([{Obj,Line}|Tl], _SubIndex,_,_,_,ColMEs) ->
+ snmpc_lib:print_error("Corrupt table definition.",[],Line),
+ {ColMEs,[{Obj,Line}|Tl]};
+define_cols(Rest, _SubIndex,_,_,_,ColMEs) ->
+ snmpc_lib:print_error("Corrupt table definition.",[]),
+ {ColMEs,Rest}.
+
+ensure_macro_imported(dummy, _Line) -> ok;
+ensure_macro_imported(Macro, Line) ->
+ Macros = (get(cdata))#cdata.imported_macros,
+ case lists:member(Macro, Macros) of
+ true -> ok;
+ false ->
+ snmpc_lib:print_error("Macro ~p not imported.", [Macro],
+ Line)
+ end.
+
+test_table(NameOfTable, Taccess, Kind, _Tindex, Tline) ->
+ if
+ Taccess =/= 'not-accessible' ->
+ snmpc_lib:print_error(
+ "Table ~w must have STATUS not-accessible",
+ [NameOfTable],Tline),
+ error;
+ Kind =/= {variable,[]} ->
+ snmpc_lib:print_error(
+ "Bad table definition (~w).",
+ [NameOfTable],Tline),
+ error;
+ true ->
+ ok
+ end.
+
+save(Filename, MibName, Options) ->
+ R = filename:rootname(Filename),
+ File1 = filename:basename(R),
+ File3 = snmpc_misc:to_upper(File1),
+ case snmpc_misc:to_upper(atom_to_list(MibName)) of
+ File3 ->
+ {value, OutDirr} = snmpc_misc:assq(outdir, Options),
+ OutDir = snmpc_misc:ensure_trailing_dir_delimiter(OutDirr),
+ File2 = (OutDir ++ File1) ++ ".bin",
+ {ok, MIB} = snmpc_lib:get_final_mib(File1, Options),
+ case get(errors) of
+ undefined ->
+ case file:write_file(File2, term_to_binary(MIB)) of
+ ok ->
+ {ok, File2};
+ _Err ->
+ snmpc_lib:error(
+ "Couldn't write file \"~s\".",[File2])
+ end;
+ E ->
+ ?vlog("save failed: "
+ "~n ~p", [E]),
+ {'EXIT',error}
+ end;
+ MibNameL ->
+ snmpc_lib:error("Mibname (~s) differs from filename (~s).",
+ [MibNameL, File1])
+ end.
+
+%% parse takes a text file as a input and the output is a list of tokens.
+%% Input: FileName (file of mibs)
+%% Output: {ok, Mib} where MIB is a tuple of Tokens.
+%% {error, {LineNbr, Mod, Msg} an error on line number LineNb.
+
+
+parse(FileName) ->
+ case snmpc_tok:start_link(reserved_words(),
+ [{file, FileName ++ ".mib"},
+ {forget_stringdata, true}]) of
+ {error,ReasonStr} ->
+ snmpc_lib:error(lists:flatten(ReasonStr),[]);
+ {ok, TokPid} ->
+ Toks = snmpc_tok:get_all_tokens(TokPid),
+ set_version(Toks),
+ %% io:format("parse -> lexical analysis: ~n~p~n", [Toks]),
+ %% t("parse -> lexical analysis: ~n~p", [Toks]),
+ CDataArg =
+ case lists:keysearch(module, 1, get(options)) of
+ {value, {module, M}} -> {module, M};
+ _ -> {file, FileName ++ ".funcs"}
+ end,
+ put(cdata,snmpc_lib:make_cdata(CDataArg)),
+ snmpc_tok:stop(TokPid),
+ Res = if
+ is_list(Toks) ->
+ snmpc_mib_gram:parse(Toks);
+ true ->
+ Toks
+ end,
+ %% t("parse -> parsed: ~n~p", [Res]),
+ case Res of
+ {ok, PData} ->
+ {ok, PData};
+ {error, {LineNbr, Mod, Msg}} ->
+ case catch format_yecc_error(LineNbr, Msg) of
+ {Line, Format, Data} ->
+ snmpc_lib:error(Format,Data,Line);
+ _Q -> % sorry, have to use ugly yecc printouts
+ Str = apply(Mod, format_error, [Msg]),
+ snmpc_lib:error("~s",[Str],LineNbr)
+ end
+ end
+ end.
+
+set_version(Toks) when is_list(Toks) ->
+%% MODULE-IDENTITY _must_ be invoked in SNMPv2 according to RFC1908
+ case lists:keymember('MODULE-IDENTITY',1,Toks) of
+ true ->
+ put(snmp_version,2);
+ false ->
+ put(snmp_version,1)
+ end;
+set_version(_) ->
+ put(snmp_version,1).
+
+
+%% YeccGeneratedFile:format_error/1 is bad.
+format_yecc_error(Line, [ErrMsg, [${,Category, $,, _LineStr,$,, Value, $}]]) ->
+ {Line, "~s \"~s\" (~s).", [ErrMsg, Value, Category]}.
+
+%% The same as the (quoted) Terminals in the snmpc_mib_gram.yrl
+reserved_words() ->
+ [
+ 'ACCESS',
+ 'BEGIN',
+ 'BIT',
+ 'CONTACT-INFO',
+ 'Counter',
+ 'DEFINITIONS',
+ 'DEFVAL',
+ 'DESCRIPTION',
+ 'DISPLAY-HINT',
+ 'END',
+ 'ENTERPRISE',
+ 'FROM',
+ 'Gauge',
+ 'IDENTIFIER',
+ 'IDENTIFIER',
+ 'IMPORTS',
+ 'INDEX',
+ 'INTEGER',
+ 'IpAddress',
+ 'LAST-UPDATED',
+ 'NetworkAddress',
+ 'OBJECT',
+ 'OBJECT',
+ 'OBJECT-TYPE',
+ 'OCTET',
+ 'OF',
+ 'Opaque',
+ 'REFERENCE',
+ 'SEQUENCE',
+ 'SIZE',
+ 'STATUS',
+ 'STRING',
+ 'SYNTAX',
+ 'TRAP-TYPE',
+ 'TimeTicks',
+ 'VARIABLES',
+
+ %% v2
+ 'LAST-UPDATED',
+ 'ORGANIZATION',
+ 'CONTACT-INFO',
+ 'MODULE-IDENTITY',
+ 'NOTIFICATION-TYPE',
+ 'MODULE-COMPLIANCE',
+ 'OBJECT-GROUP',
+ 'NOTIFICATION-GROUP',
+ 'REVISION',
+ 'OBJECT-IDENTITY',
+ 'MAX-ACCESS',
+ 'UNITS',
+ 'AUGMENTS',
+ 'IMPLIED',
+ 'OBJECTS',
+ 'TEXTUAL-CONVENTION',
+ 'OBJECT-GROUP',
+ 'NOTIFICATION-GROUP',
+ 'NOTIFICATIONS',
+ 'MODULE-COMPLIANCE',
+ 'MODULE',
+ 'MANDATORY-GROUPS',
+ 'GROUP',
+ 'WRITE-SYNTAX',
+ 'MIN-ACCESS',
+ 'BITS'
+ ]
+.
diff --git a/lib/snmp/src/compile/snmpc.hrl b/lib/snmp/src/compile/snmpc.hrl
new file mode 100644
index 0000000000..eb896cde6b
--- /dev/null
+++ b/lib/snmp/src/compile/snmpc.hrl
@@ -0,0 +1,153 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-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%
+%%
+
+%% Parser output
+-record(pdata, {mib_version,
+ mib_name,
+ imports,
+ defs}).
+
+%% compilation information record
+-record(cdata, {module_identity,
+ asn1_types = [],
+ mes = [],
+ traps = [],
+ mibfuncs,
+ sequences = [],
+ imported_macros = [],
+ objectgroups = [],
+ notificationgroups = [],
+ imports,
+ oid_ets,
+ status_ets}).
+
+
+-record(mc_module_identity,
+ {name,
+ last_updated,
+ organization,
+ contact_info,
+ description,
+ revisions = [], %% A list of mc_revision
+ name_assign
+ }
+ ).
+
+-record(mc_revision,
+ {revision,
+ description
+ }
+ ).
+
+-record(mc_object_type,
+ {name,
+ syntax,
+ units,
+ max_access,
+ status,
+ description,
+ reference,
+ kind,
+ name_assign
+ }
+ ).
+
+
+-record(mc_new_type,
+ {name,
+ macro,
+ status,
+ description,
+ reference,
+ display_hint,
+ syntax
+ }
+ ).
+
+
+-record(mc_trap,
+ {name,
+ enterprise,
+ vars,
+ description,
+ reference,
+ num
+ }
+ ).
+
+
+-record(mc_notification,
+ {name,
+ vars,
+ status,
+ description,
+ reference,
+ name_assign
+ }
+ ).
+
+
+-record(mc_module_compliance,
+ {name,
+ status,
+ description,
+ reference,
+ module,
+ name_assign
+ }
+ ).
+
+
+-record(mc_object_group,
+ {name,
+ objects,
+ status,
+ description,
+ reference,
+ name_assign
+ }
+ ).
+
+
+-record(mc_notification_group,
+ {name,
+ objects,
+ status,
+ description,
+ reference,
+ name_assign
+ }
+ ).
+
+
+-record(mc_sequence,
+ {name,
+ fields
+ }
+ ).
+
+
+-record(mc_internal,
+ {name,
+ macro,
+ parent,
+ sub_index
+ }
+ ).
+
diff --git a/lib/snmp/src/compile/snmpc_lib.erl b/lib/snmp/src/compile/snmpc_lib.erl
new file mode 100644
index 0000000000..b7e84e7d6b
--- /dev/null
+++ b/lib/snmp/src/compile/snmpc_lib.erl
@@ -0,0 +1,1819 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-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%
+%%
+
+-module(snmpc_lib).
+
+%% API
+-export([test_father/4, make_ASN1type/1, import/1, makeInternalNode2/2,
+ is_consistent/1, resolve_defval/1, make_variable_info/1,
+ check_trap_name/3, make_table_info/4, get_final_mib/2, set_dir/2,
+ look_at/1, add_cdata/2,
+ check_object_group/4, check_notification_group/4,
+ check_notification/3,
+ register_oid/4,
+ error/2, error/3,
+ %% warning/2, warning/3,
+ print_error/2, print_error/3,
+ make_cdata/1,
+ key1search/2, key1search/3]).
+
+%% internal exports
+-export([check_of/1, check_trap/2, check_trap/3, get_elem/2]).
+
+%% debug exports
+-export([vvalidate/1, vprint/6]).
+
+
+-include("snmp_types.hrl").
+-include("snmpc.hrl").
+-include("snmpc_lib.hrl").
+
+
+%%----------------------------------------------------------------------------
+%% Some basic types
+%%----------------------------------------------------------------------------
+
+-type severity() :: 'silence' | 'warning' | 'info' | 'log' | 'debug' | 'trace'.
+
+
+test_father(FatherName, NewVarName, SubIndex, Line) ->
+ CDATA = get(cdata),
+ case lists:keysearch(FatherName, #me.aliasname, CDATA#cdata.mes) of
+ {value, #me{entrytype = table, aliasname = TableName}} ->
+ print_error("Variable '~w' (sub-index '~w') cannot "
+ "be defined under table '~w'.",
+ [NewVarName, SubIndex, TableName],Line);
+ {value, #me{entrytype = table_entry, aliasname = TableName}} ->
+ print_error("Variable '~w' (sub-index '~w') cannot "
+ "be defined under table entry '~w'.",
+ [NewVarName, SubIndex, TableName], Line);
+
+ _X -> %% internal or variable
+ case lists:last(SubIndex) of
+ 0 ->
+ print_error("'~w'. A zero-valued final subidentifier is reserved for future use. (RFC1902, 7.10)",[NewVarName],Line);
+ _ -> ok
+ end
+ end.
+
+make_ASN1type({{type,Type},Line}) ->
+ case lookup_vartype(Type) of
+ {value, ASN1type} ->
+ ASN1type;
+ false ->
+ print_error("Undefined type '~w'",[Type],Line),
+ guess_integer_type()
+ end;
+make_ASN1type({{type_with_size,Type,{range,Lo,Hi}},Line}) ->
+ case lookup_vartype(Type) of
+ {value,ASN1type} ->
+ case allow_size_rfc1902(BaseType = ASN1type#asn1_type.bertype) of
+ true ->
+ ok;
+ false ->
+ print_error(
+ "Size refinement is not allowed for subclass from ~w.",
+ [BaseType],Line)
+ end,
+ ASN1type#asn1_type{lo = Lo, hi = Hi};
+ false ->
+ print_error("Undefined type '~w'",[Type],Line),
+ guess_string_type()
+ end;
+make_ASN1type({{integer_with_enum,Type,Enums},Line}) ->
+ case lookup_vartype(Type) of
+ {value,ASN1type} -> ASN1type#asn1_type{assocList = [{enums, Enums}]};
+ false ->
+ print_error("Undefined type '~w'",[Type],Line),
+ guess_integer_type()
+ end;
+make_ASN1type({{bits,Kibbles},Line}) ->
+ case get(snmp_version) of
+ 2 ->
+ {value,Bits} = lookup_vartype('BITS'),
+ Kibbles2 = test_kibbles(Kibbles, Line),
+ Bits#asn1_type{assocList = [{kibbles, Kibbles2}]};
+ _ ->
+ guess_integer_type()
+ end;
+make_ASN1type({{sequence_of, _Type},Line}) ->
+ print_error("Use of SEQUENCE OF in non-table context.",[],Line),
+ guess_integer_type().
+
+test_kibbles([], Line) ->
+ print_error("No kibbles found.",[],Line),
+ [];
+test_kibbles(Kibbles,Line) ->
+ test_kibbles2(R = lists:keysort(2,Kibbles),0,Line),
+ R.
+
+test_kibbles2([],_,_) ->
+ ok;
+test_kibbles2([{_KibbleName,BitNo}|Ks],BitNo,Line) ->
+ test_kibbles2(Ks,BitNo+1,Line);
+test_kibbles2([{_KibbleName,BitNo}|_Ks],ExpectBitNo,Line) ->
+ print_error("Expected kibble no ~p but got ~p.",[ExpectBitNo,BitNo],Line).
+
+
+allow_size_rfc1902('INTEGER') -> true;
+allow_size_rfc1902('Integer32') -> true;
+allow_size_rfc1902('Unsigned32') -> true;
+allow_size_rfc1902('OCTET STRING') -> true;
+allow_size_rfc1902('Gauge32') -> true;
+allow_size_rfc1902(_) -> false.
+
+guess_integer_type() ->
+ {value,ASN1int} = lookup_vartype('INTEGER'),
+ ASN1int.
+
+guess_string_type() ->
+ {value,ASN1str} = lookup_vartype('OCTET STRING'),
+ ASN1str.
+
+lookup_vartype(Type) ->
+ CDATA = get(cdata),
+ lists:keysearch(Type, #asn1_type.aliasname, CDATA#cdata.asn1_types).
+
+
+
+
+%%--------------------------------------------------
+%% Reads the oid-function files.
+%% Out: A list of {oid, entry}.
+%% oid is here either a Oid with integers, or
+%% with symbolic names.
+%% entry is {M,F,A}.
+%%--------------------------------------------------
+read_funcs(FileName) ->
+ case snmpc_misc:read_noexit(FileName, fun check_of/1) of
+ {ok, Res} -> Res;
+ {error, LineNo, Reason} ->
+ print_error("~p: ~w: Syntax error: ~p",
+ [FileName, LineNo, Reason]),
+ [];
+ {error, open_file} -> []
+ end.
+
+check_of({module, M}) when is_atom(M) ->
+ {ok, {module, M}};
+check_of({Oid, {M, F, A}}) when is_atom(M) andalso is_atom(F) andalso is_list(A) ->
+ {ok, {Oid, {M, F, A}}};
+check_of({_Oid, {M, F, A}}) ->
+ {invalid_argument, {M, F, A}};
+check_of(X) ->
+ {invalid_func, X}.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Section for IMPORT implementation
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+import(ImportList) ->
+ %% Register some well known top-nodes (directly under the root)
+ WellKnownNodes = [ makeInternalNode(ccitt, [0]),
+ makeInternalNode(iso, [1]),
+ makeInternalNode('joint-iso-ccitt', [2]) ],
+ lists:foreach(
+ fun(#me{aliasname = AliasName, oid = Oid}) ->
+ register_oid(undef, AliasName, root, Oid)
+ end,
+ WellKnownNodes),
+ lists:foreach(fun import_mib/1, ImportList).
+
+
+%%----------------------------------------------------------------------
+%% Returns: <nothing> only side effect stuff.
+%%----------------------------------------------------------------------
+import_mib({{'SNMPv2-SMI', ImportsFromMib},Line}) ->
+ Nodes = [makeInternalNode(internet, [1,3,6,1]),
+ makeInternalNode(directory, [1,3,6,1,1]),
+ makeInternalNode(mgmt, [1,3,6,1,2]),
+ makeInternalNode('mib-2', [1,3,6,1,2,1]),
+ makeInternalNode(transmission, [1,3,6,1,2,1,10]),
+ makeInternalNode(experimental, [1,3,6,1,3]),
+ makeInternalNode(private, [1,3,6,1,4]),
+ makeInternalNode(enterprises, [1,3,6,1,4,1]),
+ makeInternalNode(zeroDotZero, [0,0]),
+ makeInternalNode(security, [1,3,6,1,5]),
+ makeInternalNode(snmpV2, [1,3,6,1,6]),
+ makeInternalNode(snmpDomains, [1,3,6,1,6,1]),
+ makeInternalNode(snmpProxys, [1,3,6,1,6,2]),
+ makeInternalNode(snmpModules, [1,3,6,1,6,3])],
+ Types = [#asn1_type{bertype = 'Integer32',
+ aliasname = 'Integer32',
+ lo = -2147483648, hi = 2147483647},
+ #asn1_type{bertype = 'IpAddress',
+ aliasname = 'IpAddress',
+ lo = 4, hi = 4},
+ #asn1_type{bertype = 'Counter32',
+ aliasname = 'Counter32',
+ lo = 0, hi = 4294967295},
+ #asn1_type{bertype = 'Gauge32',
+ aliasname = 'Gauge32',
+ lo = 0, hi = 4294967295},
+ #asn1_type{bertype = 'Unsigned32',
+ aliasname = 'Unsigned32',
+ lo = 0, hi = 4294967295},
+ #asn1_type{bertype = 'TimeTicks',
+ aliasname = 'TimeTicks',
+ lo = 0, hi=4294967295},
+ #asn1_type{bertype = 'Opaque',
+ aliasname = 'Opaque'},
+ #asn1_type{bertype = 'Counter64',
+ aliasname = 'Counter64',
+ lo = 0, hi = 18446744073709551615}],
+ Macros = ['MODULE-IDENTITY','OBJECT-IDENTITY','OBJECT-TYPE',
+ 'NOTIFICATION-TYPE'],
+ import_built_in_loop(ImportsFromMib,Nodes,Types,Macros,'SNMPv2-SMI',Line);
+import_mib({{'RFC-1215', ImportsFromMib},Line}) ->
+ Macros = ['TRAP-TYPE'],
+ import_built_in_loop(ImportsFromMib, [],[],Macros,'RFC-1215', Line);
+import_mib({{'RFC-1212', ImportsFromMib},Line}) ->
+ Macros = ['OBJECT-TYPE'],
+ import_built_in_loop(ImportsFromMib, [],[],Macros,'RFC-1212', Line);
+import_mib({{'SNMPv2-TC', ImportsFromMib},Line}) ->
+ Nodes = [],
+ Types = [#asn1_type{aliasname = 'DisplayString',
+ bertype = 'OCTET STRING',
+ lo = 0, hi = 255},
+ #asn1_type{aliasname = 'PhysAddress',
+ bertype = 'OCTET STRING'},
+ #asn1_type{aliasname = 'MacAddress',
+ bertype = 'OCTET STRING',
+ lo = 6, hi = 6},
+ #asn1_type{aliasname = 'TruthValue',
+ bertype = 'INTEGER',
+ assocList = [{enums,[{false,2},{true,1}]}]},
+ #asn1_type{aliasname = 'TestAndIncr',
+ bertype = 'INTEGER',
+ lo = 0, hi = 2147483647},
+ #asn1_type{aliasname = 'AutonomousType',
+ bertype = 'OBJECT IDENTIFIER'},
+ #asn1_type{aliasname = 'InstancePointer',
+ bertype = 'OBJECT IDENTIFIER'},
+ #asn1_type{aliasname = 'VariablePointer',
+ bertype = 'OBJECT IDENTIFIER'},
+ #asn1_type{aliasname = 'RowPointer',
+ bertype = 'OBJECT IDENTIFIER'},
+ #asn1_type{aliasname = 'RowStatus',
+ bertype = 'INTEGER',
+ assocList = [{enums,[{destroy, 6},
+ {createAndWait, 5},
+ {createAndGo, 4},
+ {notReady, 3},
+ {notInService, 2},
+ {active, 1}]}]},
+ #asn1_type{aliasname = 'TimeStamp',
+ bertype = 'TimeTicks'},
+ #asn1_type{aliasname = 'TimeInterval',
+ bertype = 'INTEGER',
+ lo = 0, hi = 2147483647},
+ #asn1_type{aliasname = 'DateAndTime',
+ bertype = 'OCTET STRING',
+ lo = 8, hi = 11}, %% Actually 8 | 11
+ #asn1_type{aliasname = 'StorageType',
+ bertype = 'INTEGER',
+ assocList = [{enums,[{readOnly, 5},
+ {permanent, 4},
+ {nonVolatile, 3},
+ {volatile, 2},
+ {other, 1}]}]},
+ #asn1_type{aliasname = 'TDomain',
+ bertype = 'OBJECT IDENTIFIER'},
+ #asn1_type{aliasname = 'TAddress',
+ bertype = 'OCTET STRING',
+ lo = 1, hi = 255}
+ ],
+ Macros = ['TEXTUAL-CONVENTION'],
+ import_built_in_loop(ImportsFromMib,Nodes,Types,Macros,'SNMPv2-TC',Line);
+import_mib({{'SNMPv2-CONF', ImportsFromMib},Line}) ->
+ Macros = ['OBJECT-GROUP','NOTIFICATION-GROUP','MODULE-COMPLIANCE'],
+ import_built_in_loop(ImportsFromMib,[],[],Macros,'SNMPv2-CONF',Line);
+import_mib({{'RFC1155-SMI', ImportsFromMib},Line}) ->
+ Nodes = [makeInternalNode(internet, [1,3,6,1]),
+ makeInternalNode(directory, [1,3,6,1,1]),
+ makeInternalNode(mgmt, [1,3,6,1,2]),
+ makeInternalNode(experimental, [1,3,6,1,3]),
+ makeInternalNode(private, [1,3,6,1,4]),
+ makeInternalNode(enterprises, [1,3,6,1,4,1])],
+ Types = [#asn1_type{bertype = 'NetworkAddress',
+ aliasname = 'NetworkAddress', lo = 4, hi = 4},
+ #asn1_type{bertype='Counter',aliasname='Counter',
+ lo=0,hi=4294967295},
+ #asn1_type{bertype='Gauge',aliasname='Gauge',
+ lo = 0, hi = 4294967295},
+ #asn1_type{bertype='IpAddress',aliasname='IpAddress',lo=4,hi=4},
+ #asn1_type{bertype = 'TimeTicks', aliasname = 'TimeTicks',
+ lo = 0, hi=4294967295},
+ #asn1_type{bertype = 'Opaque', aliasname = 'Opaque'}],
+ Macros = ['OBJECT-TYPE'],
+ import_built_in_loop(ImportsFromMib,Nodes,Types,Macros,'RFC1155-SMI',Line);
+import_mib({{MibName, ImportsFromMib},Line}) ->
+ import_from_file({{MibName, ImportsFromMib},Line}).
+
+import_built_in_loop(Objs, Nodes, Types, Macros, MibName, Line) ->
+ lists:foreach(fun (Obj) ->
+ import_built_in(Obj,Nodes,Types,Macros,MibName,Line)
+ end, Objs).
+
+import_from_file({{_MibName, []}, _Line}) ->
+ done;
+import_from_file({{MibName, ImportsFromMib},Line}) ->
+ Filename = atom_to_list(MibName) ++ ".bin",
+ {value, Path} = snmpc_misc:assq(i, get(options)),
+ {value, LibPath} = snmpc_misc:assq(il,get(options)),
+ LibPath2 = include_lib(LibPath),
+ Path2 = Path++LibPath2++[filename:join(code:priv_dir(snmp),"mibs"),
+ "./"],
+ ImportedMib = case read_mib(Line,Filename, Path2) of
+ error ->
+ error("Could not import ~p from mib ~s. "
+ "File not found. "
+ "Check that the MIB to be IMPORTED "
+ "is compiled and present in the import path.",
+ [ImportsFromMib, Filename], Line);
+ Mib ->
+ Mib
+ end,
+ lists:foreach(fun (ImpObj) -> import(ImpObj,ImportedMib) end,
+ ImportsFromMib).
+
+import_built_in({_tag,Obj}, Nodes, Types, Macros, MibName, Line) ->
+ case lookup(Obj, Nodes) of
+ {value, ME} ->
+ register_oid(undef, ME#me.aliasname, root, ME#me.oid),
+ add_cdata(#cdata.mes, [ME#me{imported = true, oid = undefined}]);
+ false ->
+ case lists:keysearch(Obj, #asn1_type.aliasname, Types) of
+ {value, ASN1Type} ->
+ add_cdata(#cdata.asn1_types,
+ [ASN1Type#asn1_type{imported=true}]);
+ false ->
+ case lists:member(Obj, Macros) of
+ true ->
+ add_cdata(#cdata.imported_macros,[Obj]);
+ false ->
+ print_error("Cannot find '~w' in mib '~s'.",
+ [Obj, MibName], Line)
+ end
+ end
+ end.
+
+include_lib([]) -> [];
+include_lib([Dir|Dirs]) ->
+ [Appl|Path] = filename:split(Dir),
+ case code:lib_dir(list_to_atom(Appl)) of
+ {error, _Reason} ->
+ include_lib(Dirs);
+ DirPath ->
+ [filename:join(DirPath,filename:join(Path))|include_lib(Dirs)]
+ end.
+
+
+%%----------------------------------------------------------------------
+%% Returns: #mib
+%%----------------------------------------------------------------------
+read_mib(_Line, _Filename, []) ->
+ error;
+read_mib(Line, Filename, [Dir|Path]) ->
+ Dir2 = snmpc_misc:ensure_trailing_dir_delimiter(Dir),
+ case snmpc_misc:read_mib(AbsFile=lists:append(Dir2, Filename)) of
+ {ok, MIB} -> MIB;
+ {error, enoent} ->
+ read_mib(Line, Filename, Path);
+ {error, Reason} ->
+ ?vwarning("~s found but not imported: "
+ "~n Reason: ~p", [AbsFile,Reason]),
+ read_mib(Line, Filename, Path)
+ end.
+
+
+%%----------------------------------------------------------------------
+%% imports ME or Type from other Mib into current compilation data.
+%%----------------------------------------------------------------------
+import({node, NodeName}, #mib{mes = IMES, name = MibName}) ->
+ case lookup(NodeName, IMES) of
+ {value, ME} when ME#me.imported == false ->
+ register_oid(undef, ME#me.aliasname, root, ME#me.oid),
+ add_cdata(#cdata.mes, [ME#me{imported = true}]);
+ _ ->
+ print_error("Cannot find '~w' among the objects in the mib '~s'.",
+ [NodeName, MibName])
+ end;
+import({type, TypeName}, #mib{asn1_types = Types, name = MibName}) ->
+ case lists:keysearch(TypeName, #asn1_type.aliasname, Types) of
+ {value, ASN1Type} when is_record(ASN1Type, asn1_type) andalso
+ (ASN1Type#asn1_type.imported =:= false) ->
+ add_cdata(#cdata.asn1_types, [ASN1Type#asn1_type{imported=true,
+ aliasname=TypeName}]);
+ _X ->
+ print_error("Cannot find '~w' among the types in the mib '~s'.",
+ [TypeName, MibName])
+ end;
+import({builtin, Obj}, #mib{}) ->
+ print_error("~p should be imported from a standard mib.",[Obj]).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Section for initialisation
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Types defined in rfc1155 (SMI) are hard coded.
+init_types() ->
+ VerDep = case get(snmp_version) of
+ 1 -> [];
+ 2 ->
+ [#asn1_type{imported=true,bertype='BITS',aliasname='BITS'}]
+ end,
+ [#asn1_type{imported = true, bertype = 'INTEGER', aliasname = 'INTEGER'},
+ #asn1_type{imported=true,bertype='OCTET STRING',aliasname='OCTET STRING'},
+ #asn1_type{imported=true,bertype='BIT STRING',aliasname='BIT STRING'},
+ #asn1_type{imported = true, bertype = 'OBJECT IDENTIFIER',
+ aliasname = 'OBJECT IDENTIFIER'} | VerDep].
+
+makeInternalNode(Name, Oid) ->
+ makeInternalNode3(false, Name, Oid).
+
+makeInternalNode2(Imported, Name) ->
+ #me{imported = Imported, aliasname = Name, entrytype = internal}.
+
+makeInternalNode3(Imported, Name, Oid) ->
+ #me{imported = Imported, oid = Oid, aliasname = Name, entrytype = internal}.
+
+make_cdata(CDataArg) ->
+ MibFuncs =
+ case CDataArg of
+ {module, _Mod} -> [CDataArg];
+ {file, MibFuncsFile} -> read_funcs(MibFuncsFile)
+ end,
+ #cdata{mibfuncs = MibFuncs,
+ asn1_types = init_types(),
+ oid_ets = ets:new(oid_ets, [set, private]),
+ status_ets = ets:new(status_ets, [set, private])}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Section for Intermib consistency checking
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+is_consistent(Filenames) ->
+ case catch check_all_consistency(Filenames) of
+ ok ->
+ ok;
+ {undef, Format, Data} ->
+ ok = io:format(Format, Data),
+ io:format("~n"),
+ {error, inconsistent}
+ end.
+
+check_all_consistency(Filenames) ->
+ MIBs = [load_mib(Filename) || Filename <- Filenames],
+ check_oid_conflicts(MIBs),
+ check_trap_conflicts(MIBs),
+ ok.
+
+check_oid_conflicts(MIBs) ->
+ MEs = lists:append( [get_elem(MIB, #mib.mes) || MIB <- MIBs] ),
+ SortedMEs = lists:keysort(#me.oid, MEs),
+ search_for_dublettes2(#me{aliasname=dummy_init}, SortedMEs).
+
+check_trap_conflicts(MIBs) ->
+ Traps = lists:append( [get_elem(MIB, #mib.traps) || MIB <- MIBs] ),
+ [check_trap(Trap, Traps) || Trap <- Traps].
+
+check_trap(Trap, Traps) ->
+ %% check_trap/3 -> error | ok
+ Checked = [check_trap(T, Trap, undef) || T <- lists:delete(Trap, Traps)],
+ case lists:member(error, Checked) of
+ true ->
+ throw({undef,"",[]});
+ false ->
+ ok
+ end.
+
+
+%%----------------------------------------------------------------------
+%% Returns: {Oid, ASN1Type}
+%%----------------------------------------------------------------------
+trap_variable_info(Variable, Type, MEs) ->
+ case lookup(Variable, MEs) of
+ false ->
+ error("Error in ~s definition. Cannot find object '~w'.",
+ [Type, Variable]);
+ {value, ME} when ME#me.entrytype == variable ->
+ {{variable, ME#me.aliasname}, ME#me.asn1_type};
+ {value, ME} ->
+ {{column, ME#me.aliasname}, ME#me.asn1_type}
+ end.
+
+get_elem(MIB, Idx) ->
+ element(Idx, MIB).
+
+load_mib(Filename) ->
+ F1 = snmpc_misc:strip_extension_from_filename(Filename, ".mib"),
+ F2 = lists:append(F1, ".bin"),
+ case snmpc_misc:read_mib(F2) of
+ {error, Reason} ->
+ throw({undef, "Error reading file: ~w. Reason:~w", [F1, Reason]});
+ {ok, Mib} ->
+ Mib
+ end.
+
+search_for_dublettes2(_PrevME, []) -> ok;
+search_for_dublettes2(PrevME, [ME|MEs])
+ when ME#me.imported==true ->
+ search_for_dublettes2(PrevME, MEs);
+search_for_dublettes2(PrevME, [ME|MEs])
+ when PrevME#me.oid == ME#me.oid ->
+ if PrevME#me.entrytype == internal, ME#me.entrytype == internal,
+ PrevME#me.aliasname == ME#me.aliasname ->
+ search_for_dublettes2(ME, MEs);
+ true ->
+ throw({undef,"Multiple used object with OBJECT IDENTIFIER '~w'"
+ " Used by '~w' and '~w' ", [PrevME#me.oid,
+ PrevME#me.aliasname,
+ ME#me.aliasname]})
+ end;
+search_for_dublettes2(_PrevME, [ME|MEs]) ->
+ search_for_dublettes2(ME, MEs).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Section for handling of default value resolving
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+resolve_defval(ME) ->
+ case has_complex_defval(ME) of
+ true ->
+ CDATA = get(cdata),
+ resolve_complex_defval(ME, CDATA#cdata.mes);
+ false -> ME
+ end.
+
+has_complex_defval(#me{aliasname = N,
+ assocList = AssocList,
+ asn1_type = #asn1_type{bertype = BT}})
+ when is_list(AssocList) ->
+ case snmpc_misc:assq(defval, AssocList) of
+ {value, Int} when is_integer(Int) ->
+ false;
+ {value, Val} when is_atom(Val) andalso (BT =:= 'OBJECT IDENTIFIER') ->
+ false; % resolved in update_me_oids
+ {value, Val} when is_atom(Val) andalso (BT =:= 'INTEGER') ->
+ true;
+ {value, Bits} when is_list(Bits) andalso (BT =:= 'BITS') ->
+ true;
+ {value, Str} when is_list(Str) andalso (BT =:= 'OCTET STRING') ->
+ false; % but ok
+ {value, Str} when is_list(Str) andalso (BT =:= 'Opaque') ->
+ false; % but ok
+ {value, Str} when is_list(Str) andalso
+ (length(Str) =:= 4) andalso
+ (BT =:= 'IpAddress') ->
+ false; % but ok
+ {value, Shit} ->
+ print_error("Bad default value for ~p: ~p [~p]",[N,Shit,BT]),
+ false;
+ false -> %% no defval (or strings nyi)
+ false
+ end;
+has_complex_defval(_) -> false.
+
+resolve_complex_defval(ME, _AllMEs)
+ when (ME#me.asn1_type)#asn1_type.bertype == 'INTEGER' ->
+ #me{aliasname = MEName, assocList = AssocList} = ME,
+ {value, DefVal} = snmpc_misc:assq(defval, AssocList),
+ #asn1_type{bertype = TypeName,
+ assocList = AssocListForASN1Type} = ME#me.asn1_type,
+ case snmpc_misc:assq(enums, AssocListForASN1Type) of
+ false ->
+ print_error("Type '~w' has no defined enums. "
+ "Used in DEFVAL for '~w'.", [TypeName, MEName]),
+ ME;
+ {value, Enums} ->
+ case snmpc_misc:assq(DefVal, Enums) of
+ false ->
+ print_error("Enum '~w' not found. "
+ "Used in DEFVAL for '~w'.", [DefVal, MEName]),
+ ME;
+ {value, IntVal} when is_integer(IntVal) ->
+ ME#me{assocList = lists:keyreplace(defval, 1, AssocList,
+ {defval, IntVal})}
+ end
+ end;
+
+resolve_complex_defval(ME, _AllMEs)
+ when (ME#me.asn1_type)#asn1_type.bertype =:= 'BITS' ->
+ #me{aliasname = MEName, assocList = AssocList} = ME,
+ {value, DefVal} = snmpc_misc:assq(defval, AssocList),
+ #asn1_type{assocList = AssocListForASN1Type} = ME#me.asn1_type,
+ {value, Kibbles} = snmpc_misc:assq(kibbles, AssocListForASN1Type),
+ case snmpc_misc:bits_to_int(DefVal,Kibbles) of
+ error->
+ print_error("Invalid default value ~w for ~w.",[DefVal, MEName]),
+ ME;
+ IntVal when is_integer(IntVal) ->
+ ME#me{assocList = lists:keyreplace(defval, 1, AssocList,
+ {defval, IntVal})}
+ end.
+
+
+make_variable_info(#me{asn1_type = Asn1Type, assocList = Alist}) ->
+ Defval =
+ case snmpc_misc:assq(defval, Alist) of
+ {value, Val} ->
+ Val;
+ _ ->
+ get_def(Asn1Type)
+ end,
+ #variable_info{defval = Defval}.
+
+get_def(#asn1_type{bertype = BT, lo = LO, assocList = AL}) ->
+ ?vtrace("get_def -> entry with"
+ "~n BT: ~p"
+ "~n LO: ~p"
+ "~n AL: ~p", [BT, LO, AL]),
+ get_def(BT, LO, AL).
+
+get_def('INTEGER', Lo, _) when is_integer(Lo) -> Lo;
+get_def('INTEGER', _, AL) ->
+ case snmpc_misc:assq(enums, AL) of
+ {value, Enums} ->
+ case lists:keysort(2, Enums) of
+ [{_, Val}|_] ->
+ Val;
+ _ ->
+ 0
+ end;
+ _ ->
+ 0
+ end;
+get_def('Counter', _, _) -> 0;
+get_def('Gauge', _, _) -> 0;
+get_def('TimeTicks', _, _) -> 0;
+get_def('OCTET STRING', _, _) -> "";
+get_def('IpAddress', _, _) -> [0,0,0,0];
+get_def('NetworkAddress', _, _) -> [0,0,0,0];
+get_def('OBJECT IDENTIFIER', _, _) -> [0, 0];
+get_def('Opaque', _, _) -> "";
+%v2
+get_def('Integer32',Lo, _) when is_integer(Lo) -> Lo;
+get_def('Integer32',_, _) -> 0;
+get_def('Counter32',_, _) -> 0;
+get_def('Gauge32',_, _) -> 0;
+get_def('Unsigned32',_, _) -> 0;
+get_def('BITS',_, _) -> 0;
+get_def('Counter64',_, _) -> 0.
+
+check_trap_name(EnterpriseName, Line, MEs) ->
+ case lists:keysearch(EnterpriseName, #me.aliasname, MEs) of
+ false ->
+ error("Error in trap definition. Cannot find object '~w'.",
+ [EnterpriseName],Line);
+ {value, _} ->
+ true
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Section for table functions
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%----------------------------------------------------------------------
+%% This information is needed to be able to create default instrumentation
+%% functions for tables.
+%%----------------------------------------------------------------------
+make_table_info(Line, _TableName, {augments,SrcTableEntry}, ColumnMEs) ->
+ ColMEs = lists:keysort(#me.oid, ColumnMEs),
+ %% Nbr_of_Cols = length(ColMEs),
+ MEs = ColMEs ++ (get(cdata))#cdata.mes,
+ Aug = case lookup(SrcTableEntry,MEs) of
+ false ->
+ print_error("Cannot AUGMENT the non-existing table entry ~p",
+ [SrcTableEntry],Line),
+ {augments, error};
+ {value,ME} ->
+ {augments, {SrcTableEntry,translate_type(ME#me.asn1_type)}}
+ end,
+ #table_info{index_types = Aug};
+make_table_info(Line, TableName, {indexes,[]}, _ColumnMEs) ->
+ print_error("Table ~w lacks indexes.", [TableName],Line),
+ #table_info{};
+make_table_info(Line, TableName, {indexes,Indexes}, ColumnMEs) ->
+ ColMEs = lists:keysort(#me.oid, ColumnMEs),
+ NonImpliedIndexes = lists:map(fun non_implied_name/1, Indexes),
+ test_read_create_access(ColMEs, Line, dummy),
+ NonIndexCol = test_index_positions(Line, NonImpliedIndexes, ColMEs),
+ Nbr_of_Cols = length(ColMEs),
+ ASN1Indexes = find_asn1_types_for_indexes(Indexes, ColMEs, Line),
+ FA = first_accessible(TableName, ColMEs),
+ StatCol = find_status_col(Line, TableName, ColMEs),
+ NoAccs = list_not_accessible(NonIndexCol,ColMEs),
+ case lists:member(StatCol,NoAccs) of
+ true ->
+ print_error("Status column cannot be not-accessible. In table ~p.",
+ [TableName],Line);
+ false -> ok
+ end,
+ #table_info{nbr_of_cols = Nbr_of_Cols,
+ first_own_index = find_first_own_index(NonImpliedIndexes,
+ ColMEs, 1),
+ status_col = StatCol,
+ first_accessible = FA,
+ not_accessible = NoAccs,
+ index_types = ASN1Indexes}.
+
+%% Perkins p110
+test_read_create_access([#me{aliasname = N, access = 'read-create'}|_ColMEs],
+ Line, 'read-write') ->
+ print_error("Column ~p cannot be read-create when another is read-write.",
+ [N], Line);
+test_read_create_access([#me{aliasname = N, access = 'read-write'}|_ColMEs],
+ Line, 'read-create') ->
+ print_error("Column ~p cannot be read-write when another is read-create.",
+ [N], Line);
+test_read_create_access([#me{access = 'read-write'}|ColMEs],
+ Line, _OtherStat) ->
+ test_read_create_access(ColMEs,Line,'read-write');
+test_read_create_access([#me{access = 'read-create'}|ColMEs],
+ Line, _OtherStat) ->
+ test_read_create_access(ColMEs,Line,'read-create');
+test_read_create_access([_ME|ColMEs],Line,OtherStat) ->
+ test_read_create_access(ColMEs,Line,OtherStat);
+test_read_create_access([], _Line, _) ->
+ ok.
+
+find_status_col(_Line, _TableName, []) ->
+ undefined;
+find_status_col(_Line, _TableName,
+ [#me{asn1_type=#asn1_type{aliasname='RowStatus'}}|_]) ->
+ 1;
+find_status_col(Line, TableName, [_ShitME | MEs]) ->
+ case find_status_col(Line, TableName, MEs) of
+ undefined -> undefined;
+ N -> 1+N
+ end.
+
+list_not_accessible(none,_) -> [];
+list_not_accessible(NonIndexCol, ColMEs) when is_integer(NonIndexCol) ->
+ list_not_accessible(lists:nthtail(NonIndexCol - 1,ColMEs)).
+
+list_not_accessible([#me{access='not-accessible', oid=Col}|ColMEs]) ->
+ [Col | list_not_accessible(ColMEs)];
+list_not_accessible([_ColME|ColMEs]) ->
+ list_not_accessible(ColMEs);
+list_not_accessible([]) ->
+ [].
+
+%%----------------------------------------------------------------------
+%% See definition of first_own_index in the table_info record definition.
+%%----------------------------------------------------------------------
+find_first_own_index([], _ColMEs, _FOI) -> 0;
+find_first_own_index([NameOfIndex | Indexes], ColMEs, FOI) ->
+ case lists:keysearch(NameOfIndex, #me.aliasname, ColMEs) of
+ {value, _ME} ->
+ FOI;
+ false ->
+ find_first_own_index(Indexes, ColMEs, FOI + 1)
+ end.
+
+first_accessible(TableName, []) ->
+ error("Table '~w' must have at least one accessible column.",[TableName]);
+first_accessible(TableName, [#me{access = 'not-accessible'} | T]) ->
+ first_accessible(TableName, T);
+first_accessible(_TableName, [#me{oid = Col} | _]) ->
+ Col.
+
+get_defvals(ColMEs) ->
+ lists:keysort(1,
+ lists:filter(fun drop_undefined/1,
+ lists:map(fun column_and_defval/1, ColMEs))).
+
+find_asn1_types_for_indexes(Indexes, ColMEs,Line) ->
+ MEs = ColMEs ++ (get(cdata))#cdata.mes,
+ test_implied(Indexes, Line),
+ lists:map(fun (ColumnName) ->
+ translate_type(get_asn1_type(ColumnName, MEs,Line))
+ end,
+ Indexes).
+
+test_implied([],_) -> ok;
+test_implied([{implied, _Type}, _OtherIndexElem|_], Line) ->
+ print_error("Implied must be last.", [], Line);
+test_implied([{implied, _Type}], _Line) ->
+ ok;
+test_implied([_H|T], Line) ->
+ test_implied(T, Line).
+
+drop_undefined({_X, undefined}) -> false;
+drop_undefined({_X, _Y}) -> true;
+drop_undefined(undefined) -> false;
+drop_undefined(_X) -> true.
+
+%% returns: {ColumnNo, Defval}
+column_and_defval(#me{oid = Oid, assocList = AssocList}) ->
+ ColumnNo = lists:last(Oid),
+ case snmpc_misc:assq(defval, AssocList) of
+ false -> {ColumnNo, undefined};
+ {value, DefVal} -> {ColumnNo, DefVal}
+ end.
+
+%% returns: an asn1_type if ColME is an indexfield, otherwise undefined.
+get_asn1_type({implied,ColumnName}, MEs, Line) ->
+ case lookup(ColumnName, MEs) of
+ {value,#me{asn1_type=A}} when A#asn1_type.bertype =:=
+ 'OCTET STRING' ->
+ A#asn1_type{implied = true};
+ {value,#me{asn1_type=A}} when A#asn1_type.bertype =:=
+ 'OBJECT IDENTIFIER' ->
+ A#asn1_type{implied = true};
+ Shit ->
+ print_error("Only OCTET STRINGs and OIDs can be IMPLIED.(~w)",
+ [Shit], Line)
+ end;
+get_asn1_type(ColumnName, MEs, Line) ->
+ case lookup(ColumnName, MEs) of
+ {value,ME} -> ME#me.asn1_type;
+ false -> error("Can't find object ~p. Used as INDEX in table.",
+ [ColumnName],Line)
+ end.
+
+test_index_positions(Line, Indexes, ColMEs) ->
+ TLI = lists:filter(fun (IndexName) ->
+ is_table_local_index(IndexName,ColMEs) end,
+ Indexes),
+ test_index_positions_impl(Line, TLI, ColMEs).
+
+%% Returns the first non-index column | none
+test_index_positions_impl(_Line, [], []) -> none;
+test_index_positions_impl(_Line, [], [#me{oid=Col}|_ColMEs]) ->
+ Col;
+test_index_positions_impl(Line, Indexes,
+ [#me{aliasname = Name,
+ asn1_type = Asn1} | ColMEs]) ->
+ case lists:member(Name, Indexes) of
+ true ->
+ if
+ Asn1#asn1_type.bertype =:= 'BITS' ->
+ print_error("Invalid data type 'BITS' for index '~w'.",
+ [Name],Line);
+ true -> true
+ end,
+ test_index_positions_impl(Line,
+ lists:delete(Name, Indexes), ColMEs);
+ false ->
+ ?vwarning2("Index columns must be first for "
+ "the default functions to work properly. "
+ "~w is no index column.", [Name], Line),
+ none
+ end.
+
+is_table_local_index(IndexName, ColMEs) ->
+ case lists:keysearch(IndexName, #me.aliasname, ColMEs) of
+ false -> false;
+ _Q -> true
+ end.
+
+non_implied_name({implied, IndexColumnName}) -> IndexColumnName;
+non_implied_name(IndexColumnName) -> IndexColumnName.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Section for generationg the final mib
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% returns: {ok,
+% {snmp_mib, MEs, traps, list of {TrapOid, list of oids (objects)}}}
+get_final_mib(Name, Options) ->
+ ?vdebug("get_final_mib -> entry", []),
+ CDATA = get(cdata),
+ #cdata{mes = MEs,
+ mibfuncs = MibFuncs,
+ asn1_types = Types,
+ traps = Traps0,
+ oid_ets = OidEts} = CDATA,
+
+ ?vdebug("get_final_mib -> resolve oids", []),
+ resolve_oids(OidEts),
+ %% Reverse so that we get report on objects earlier in the file
+ %% before later objects.
+ UMEs = update_me_oids(lists:reverse(MEs), OidEts, []),
+ ?vtrace("get_final_mib -> "
+ "~n UMEs: ~p", [UMEs]),
+
+ Traps1 = update_trap_objects(Traps0, MEs, []),
+ Traps2 = update_trap_oids(Traps1, OidEts, []),
+ ?vtrace("get_final_mib -> "
+ "~n Traps2: ~p", [Traps2]),
+
+ SortedMEs = lists:keysort(#me.oid,UMEs),
+ ?vdebug("get_final_mib -> search for dublettes", []),
+ search_for_dublettes(#me{aliasname=dummy_init}, SortedMEs),
+
+ ?vdebug("get_final_mib -> search for oid conflicts", []),
+ search_for_oid_conflicts(Traps2, SortedMEs),
+
+ ?vdebug("get_final_mib -> resolve oid", []),
+ %% FIXME: use list comprehension instead
+ MibFs = lists:keysort(1,
+ lists:zf(fun({module, _Mod}) -> false;
+ (MF) -> {true, resolve_oid(MF,SortedMEs)}
+ end, MibFuncs)),
+ ?vtrace("get_final_mib -> "
+ "~n MibFs: ~p", [MibFs]),
+ {value, DBName} = snmpc_misc:assq(db, Options),
+ Module = key1search(module, MibFuncs, undefined),
+ MEsWithMFA = insert_mfa(MibFs, SortedMEs, DBName, Module),
+ Misc = [{snmp_version,get(snmp_version)}
+ | case lists:member(no_symbolic_info,Options) of
+ true -> [no_symbolic_info];
+ false -> []
+ end],
+ {value, GroupBool} = snmpc_misc:assq(group_check, Options),
+ case GroupBool of
+ true ->
+ case get(snmp_version) =:= 2 of
+ true ->
+ ?vdebug("get_final_mib -> check object groups:"
+ "~n ~p", [CDATA#cdata.objectgroups]),
+ check_group(CDATA#cdata.mes,
+ CDATA#cdata.objectgroups),
+ ?vdebug("get_final_mib -> check notifications group:"
+ "~n ~p", [CDATA#cdata.notificationgroups]),
+ check_notification(Traps2,
+ CDATA#cdata.notificationgroups);
+ false ->
+ ok
+ end;
+ false ->
+ ok
+ end,
+ MI = module_identity(CDATA),
+ Mib = #mib{name = Name,
+ misc = Misc,
+ module_identity = MI,
+ mes = lists:map(fun(ME) -> translate_me_type(ME) end,
+ MEsWithMFA),
+ variable_infos = extract_variable_infos(MEsWithMFA),
+ table_infos = extract_table_infos(MEsWithMFA),
+ traps = lists:map(fun(T) -> translate_trap_type(T) end,
+ Traps2),
+ asn1_types = lists:map(fun(T) -> translate_type(T) end,
+ Types),
+ imports = CDATA#cdata.imports},
+ ?vdebug("get_final_mib -> done", []),
+ {ok, Mib}.
+
+
+module_identity(#cdata{module_identity = MI}) ->
+ case lists:member(module_identity, get(options)) of
+ true ->
+ MI;
+ false ->
+ undefined
+ end.
+
+
+update_trap_objects([], _MEs, Acc) ->
+ ?vtrace("update_trap_objects -> done", []),
+ lists:reverse(Acc);
+update_trap_objects([#trap{trapname = Name,
+ oidobjects = Variables} = Trap|Traps], MEs, Acc) ->
+ ?vtrace("update_trap_objects -> update objects for trap ~p:"
+ "~n ~p", [Name, Variables]),
+ OidObjects =
+ [trap_variable_info(Var, "trap", MEs) || Var <- Variables],
+ UpdTrap = Trap#trap{oidobjects = OidObjects},
+ update_trap_objects(Traps, MEs, [UpdTrap|Acc]);
+update_trap_objects([#notification{trapname = Name,
+ oidobjects = Variables} = Notif|Traps],
+ MEs, Acc) ->
+ ?vtrace("update_trap_objects -> update objects for notification ~p:"
+ "~n ~p", [Name, Variables]),
+ OidObjects =
+ [trap_variable_info(Var, "notification", MEs) || Var <- Variables],
+ UpdNotif = Notif#notification{oidobjects = OidObjects},
+ update_trap_objects(Traps, MEs, [UpdNotif|Acc]);
+update_trap_objects([_|Traps], MEs, Acc) ->
+ update_trap_objects(Traps, MEs, Acc).
+
+
+%% We don't want a zillion aliases for INTEGER (etc),
+%% and since they are encoded with the same tag we can treat them as
+%% equivalent.
+%% The reason for having them at compile time is for example that
+%% Unsigned32 is allowed as INDEX but not Gauge.
+%% The compiler might want to ensure this and more...
+translate_me_type(ME) ->
+ ME#me{asn1_type = translate_type(ME#me.asn1_type)}.
+
+translate_trap_type(Trap) when is_record(Trap, notification) ->
+ translate_trap_type_notif(Trap);
+translate_trap_type(Trap) when is_record(Trap, trap) ->
+ translate_trap_type_trap(Trap).
+
+translate_trap_type_notif(Trap)->
+ NewOidobjects =
+ lists:map(fun({Oid,ASN1type}) ->{Oid,translate_type(ASN1type)} end,
+ Trap#notification.oidobjects),
+ Trap#notification{oidobjects=NewOidobjects}.
+
+translate_trap_type_trap(Trap)->
+ NewOidobjects =
+ lists:map(fun({Oid,ASN1type}) ->
+ {Oid, translate_type(ASN1type)}
+ end,
+ Trap#trap.oidobjects),
+ Trap#trap{oidobjects = NewOidobjects}.
+
+translate_type(ASN1type) when ASN1type#asn1_type.bertype =:= 'NetworkAddress' ->
+ ASN1type#asn1_type{bertype = 'IpAddress'};
+translate_type(ASN1type) when ASN1type#asn1_type.bertype =:= 'Integer32' ->
+ ASN1type#asn1_type{bertype = 'INTEGER'};
+translate_type(ASN1type) when ASN1type#asn1_type.bertype =:= 'Counter' ->
+ ASN1type#asn1_type{bertype = 'Counter32'};
+translate_type(ASN1type) when ASN1type#asn1_type.bertype =:= 'Gauge' ->
+ ASN1type#asn1_type{bertype = 'Unsigned32'};
+translate_type(ASN1type) when ASN1type#asn1_type.bertype =:= 'Gauge32' ->
+ ASN1type#asn1_type{bertype = 'Unsigned32'};
+translate_type(ASN1type) -> ASN1type.
+
+%% Check for both NOTIFICATION-GROUP and OBJECT-GROUP
+
+check_notification_group(Name, GroupObjects, Line, Status) ->
+ #cdata{traps = Traps, status_ets = Ets} = get(cdata),
+ Objects = get_notification_names(Traps),
+ check_def(notification, Name, Line, Status, GroupObjects, Objects, Ets).
+
+get_notification_names(Traps) when is_list(Traps) ->
+ [Name || #notification{trapname = Name} <- Traps].
+
+check_object_group(Name, GroupObjects, Line, Status) ->
+ #cdata{mes = MEs, status_ets = Ets} = get(cdata),
+ Objects = get_object_names(MEs),
+ check_def(object, Name, Line, Status, GroupObjects, Objects, Ets).
+
+get_object_names([])->[];
+get_object_names([#me{access=A, entrytype=T, aliasname=N}|MEs])
+ when (A =/= 'not-accessible') andalso (T =/= 'internal') ->
+ [N|get_object_names(MEs)];
+get_object_names([_ME|Rest]) ->
+ get_object_names(Rest).
+
+%% Strictly we should not need to check more then the status
+%% table, but since error do happen...
+check_def(Type, Name, Line, Status, [GroupObject|GroupObjects], Objects, Ets) ->
+ ?vdebug2("check definition of ~p [~p]: presumed member of ~p [~p]",
+ [GroupObject, Type, Name, Status], Line),
+ case lists:member(GroupObject, Objects) of
+ true ->
+ ?vtrace("~p is a member of ~p", [GroupObject, Name]),
+ %% Lucky so far, now lets check that the status is valid
+ case ets:lookup(Ets, GroupObject) of
+ [{GroupObject, ObjectStatus}] ->
+ ?vtrace("check that the object status (~p) is valid",
+ [ObjectStatus]),
+ check_group_member_status(Name, Status,
+ GroupObject, ObjectStatus);
+ _ ->
+ print_error("group (~w) member ~w not found"
+ " in status table - status check failed",
+ [Name, GroupObject])
+ end;
+ false ->
+ %% Ok, this could be because the status is obsolete or
+ %% deprecated (with the deprecated flag = false)
+ ?vtrace("~p is not a member of ~p "
+ "[object status could be obsolete]",
+ [GroupObject, Name]),
+ case ets:lookup(Ets, GroupObject) of
+ [{GroupObject, ObjectStatus}] ->
+ ?vtrace("check that the object status (~p) is valid",
+ [ObjectStatus]),
+ check_group_member_status(Name, Status,
+ GroupObject, ObjectStatus);
+ _ ->
+ group_member_error(Type, GroupObject, Line)
+ end
+ end,
+ check_def(Type, Name, Line, Status, GroupObjects, Objects, Ets);
+check_def(_, _, _, _, [], _, _) ->
+ ok.
+
+group_member_error(object, Name, Line) ->
+ print_error("OBJECT-TYPE definition missing or "
+ "'not-accessible' for '~w'", [Name],Line);
+group_member_error(notification, Name, Line) ->
+ print_error("NOTIFICATION-TYPE definition missing for '~w'",
+ [Name], Line).
+
+
+check_group_member_status(_GroupName, _GroupStatus, _Member, undefined) ->
+ ok;
+check_group_member_status(_GroupName, current, _Member, current) ->
+ ok;
+check_group_member_status(GroupName, current, Member, MemberStatus) ->
+ group_member_status_error(GroupName, current, Member, MemberStatus,
+ "current");
+check_group_member_status(_GroupName, deprecated, _Member, MemberStatus)
+ when (MemberStatus =:= deprecated) orelse (MemberStatus =:= current) ->
+ ok;
+check_group_member_status(GroupName, deprecated, Member, MemberStatus) ->
+ group_member_status_error(GroupName, deprecated, Member, MemberStatus,
+ "deprecated or current");
+check_group_member_status(_GroupName, obsolete, _Member, MemberStatus)
+ when (MemberStatus =:= obsolete) orelse
+ (MemberStatus =:= deprecated) orelse
+ (MemberStatus =:= current) ->
+ ok;
+check_group_member_status(GroupName, obsolete, Member, MemberStatus) ->
+ group_member_status_error(GroupName, obsolete, Member, MemberStatus,
+ "obsolete, deprecated or current");
+check_group_member_status(_GroupName, _GroupStatus, _Member, _MemberStatus) ->
+ ok.
+
+group_member_status_error(Name, Status, Member, MemberStatus, Expected) ->
+ snmpc_lib:print_error("Invalid status of group member ~p "
+ "in group ~p. "
+ "Group status is ~p "
+ "and member status is ~p "
+ "(should be ~s)",
+ [Member, Name, Status, MemberStatus, Expected]).
+
+
+
+% check_def(Objects,[GroupObject|GroupObjects],Line)->
+% case lists:member(GroupObject,Objects) of
+% false ->
+% print_error("OBJECT-TYPE definition missing or "
+% "'not-accessible' for '~w'", [GroupObject],Line),
+% check_def(Objects,GroupObjects,Line);
+% true ->
+% check_def(Objects,GroupObjects,Line)
+% end;
+% check_def(_Objects,[],_Line) ->
+% ok.
+
+%% Checking the definition of OBJECT-GROUP
+
+%%-----------------------------
+check_group([#me{imported = true} | T],GroupObjects)->
+ ?vtrace("check_group(imported) -> skip", []),
+ check_group(T,GroupObjects);
+check_group([],_GroupObjects) ->
+ ?vtrace("check_group -> done", []),
+ ok;
+check_group([#me{access = A,
+ entrytype = T,
+ aliasname = N}|MEs], GroupObjects)
+ when ((A =/= 'not-accessible') andalso
+ (T =/= 'internal') andalso
+ (T =/= group)) ->
+ ?vtrace("check_group -> "
+ "~n access: ~p"
+ "~n entrytype: ~p"
+ "~n aliasname: ~p", [A, T, N]),
+ check_member_group(N, GroupObjects),
+ check_group(MEs,GroupObjects);
+check_group([_|MEs],GroupObjects) ->
+ check_group(MEs,GroupObjects).
+
+check_member_group(Aliasname, [])->
+ print_error("'~w' missing in OBJECT-GROUP",[Aliasname]);
+check_member_group(Aliasname, [{Name,GroupObject,Line}|Tl])->
+ ?vtrace2("check_member_group -> entry with"
+ "~n Aliasname: ~p"
+ "~n Name: ~p"
+ "~n GroupObject: ~p", [Aliasname, Name, GroupObject], Line),
+ case lists:member(Aliasname,GroupObject) of
+ true ->
+ ok;
+ false ->
+ check_member_group(Aliasname,Tl)
+ end.
+
+
+%% Checking definition in NOTIFICATION-GROUP
+
+%%--------------------------
+check_notification([],_NotificationObjects) ->
+ ok;
+check_notification([#notification{trapname=Aliasname}|Traps],
+ NotificationObjects) ->
+ check_member_notification(Aliasname, NotificationObjects),
+ check_notification(Traps,NotificationObjects);
+check_notification([_|Traps],NotificationObjects) ->
+ check_notification(Traps,NotificationObjects).
+
+check_member_notification(Aliasname,[])->
+ print_error("'~w' missing in NOTIFICATION-GROUP",[Aliasname]);
+check_member_notification(Aliasname,[{_Name,NotificationObject,_Line}|Tl]) ->
+ case lists:member(Aliasname, NotificationObject) of
+ true ->
+ ok;
+ false ->
+ check_member_notification(Aliasname,Tl)
+ end.
+
+
+%%----------------------------------------------------------------------
+%% Purpose: Resolves oids for aliasnames used in .funcs file.
+%% Returns: {Oid, X}
+%%----------------------------------------------------------------------
+resolve_oid({NameOrOid, X}, MEs) ->
+ case lookup(NameOrOid, MEs) of
+ {value, #me{oid=Oid,entrytype=variable}} -> {Oid, X};
+ {value, #me{oid=Oid,entrytype=table}} -> {Oid, X};
+ {value, #me{entrytype=table_entry}} ->
+ error("Cannot associate an instrumentation function with a "
+ "Table Entry: ~w (must be table or variable)",
+ [NameOrOid]);
+ {value, #me{entrytype=table_column}} ->
+ error("Cannot associate an instrumentation function with a "
+ "Table Column: ~w (must be table or variable)",
+ [NameOrOid]);
+ _Q ->
+ error("Cannot find OBJECT-TYPE definition for '~w'.",
+ [NameOrOid])
+ end.
+
+%%----------------------------------------------------------------------
+%% Fs is list of {Oid, {M,F,A}}
+%% returns: MEs with access-functions.
+%% Pre: Fs, MEs are sorted (on Oid) (then we can traverse mib efficiently)
+%%----------------------------------------------------------------------
+insert_mfa(Fs, [ME | MEs], DBName, Mod)
+ when ME#me.imported =:= true ->
+ [ME | insert_mfa(Fs, MEs, DBName, Mod)];
+
+insert_mfa(Fs, [ME | MEs], DBName, Mod)
+ when ME#me.entrytype =:= internal ->
+ [ME | insert_mfa(Fs, MEs, DBName, Mod)];
+
+insert_mfa(Fs, [ME|MEs], DBName, Mod)
+ when ME#me.entrytype =:= group ->
+ [ME | insert_mfa(Fs, MEs, DBName, Mod)];
+
+insert_mfa([X | Fs], [ME | MEs], DBName, Mod)
+ when ME#me.entrytype =:= variable ->
+ {Oid, {M,F,A}} = X,
+ case ME#me.oid of
+ Oid ->
+ [ME#me{mfa = {M,F,A}} | insert_mfa(Fs, MEs, DBName, Mod)];
+ _Q ->
+ [insert_default_mfa(ME, DBName, Mod) |
+ insert_mfa([X | Fs], MEs, DBName, Mod)]
+ end;
+
+insert_mfa([X | Fs], [TableME | MEs], DBName, Mod)
+ when TableME#me.entrytype =:= table ->
+ {Oid, {M,F,A}} = X,
+ {TableMEs, RestMEs} = collect_mes_for_table(TableME, [TableME | MEs]),
+ [TableEntryME | ColMEs] = tl(TableMEs),
+ DefVals = get_defvals(ColMEs),
+ {value,TableInfo} = snmpc_misc:assq(table_info,TableME#me.assocList),
+ NAssocList = [{table_info, TableInfo#table_info{defvals = DefVals}} |
+ lists:keydelete(table_info, 1, TableME#me.assocList)],
+ NTableME = TableME#me{assocList = NAssocList},
+ case is_same_table(Oid, NTableME#me.oid) of
+ true -> % use mfa from .funcs
+ lists:append([NTableME,
+ TableEntryME#me{mfa = {M, F, A}}
+ | ColMEs],
+ insert_mfa(Fs, RestMEs, DBName, Mod));
+ false ->
+ lists:append(insert_default_mfa([NTableME | tl(TableMEs)],
+ DBName, Mod),
+ insert_mfa([X|Fs], RestMEs, DBName, Mod))
+ end;
+
+insert_mfa([], [ME|MEs], DBName, Mod)
+ when ME#me.entrytype =:= variable ->
+ [insert_default_mfa(ME, DBName, Mod) | insert_mfa([], MEs, DBName, Mod)];
+
+insert_mfa([], [ME|MEs], DBName, Mod)
+ when ME#me.entrytype =:= table ->
+ {TableMEs, RestMEs} = collect_mes_for_table(ME, [ME|MEs]),
+ [TableME, _TableEntryME | ColMEs] = TableMEs,
+ DefVals = get_defvals(ColMEs),
+ {value,TableInfo} = snmpc_misc:assq(table_info,TableME#me.assocList),
+ NAssocList = [{table_info, TableInfo#table_info{defvals = DefVals}} |
+ lists:keydelete(table_info, 1, TableME#me.assocList)],
+ NTableME = TableME#me{assocList = NAssocList},
+ NewTableMEs = insert_default_mfa([NTableME | tl(TableMEs)], DBName, Mod),
+ lists:append(NewTableMEs, insert_mfa([], RestMEs, DBName, Mod));
+
+insert_mfa([], [], _DBName, _Mod) ->
+ [];
+insert_mfa([], [ME|_MEs], _DBName, _Mod) ->
+ error("Missing access-functions for '~w'.",[ME#me.aliasname]).
+
+%%----------------------------------------------------------------------
+%% Returns: {[TableME, TableEntryME | ColumnMEs], RestMEs}
+%%----------------------------------------------------------------------
+collect_mes_for_table(_TableME, []) ->
+ {[], []};
+
+collect_mes_for_table(TableME, [ME|MEs]) ->
+ case is_same_table(TableME#me.oid, ME#me.oid) of
+ true ->
+ {TableMEs, RestMEs} = collect_mes_for_table(TableME, MEs),
+ {[ME | TableMEs], RestMEs};
+ false ->
+ {[], [ME | MEs]}
+ end.
+
+%% returns: MibEntry with access-functions.
+insert_default_mfa(ME, DBName, undefined) when is_record(ME, me)->
+ case lists:member(no_defs, get(options)) of
+ true ->
+ error("Missing access function for ~s", [ME#me.aliasname]);
+ false ->
+ ?vinfo("No accessfunction for '~w' => using default",
+ [ME#me.aliasname]),
+ set_default_function(ME, DBName)
+ end;
+
+insert_default_mfa(ME, _DBName, Mod) when is_record(ME, me)->
+ ME#me{mfa = {Mod, ME#me.aliasname, []}};
+
+insert_default_mfa([TableME, EntryME | Columns], DBName, undefined) ->
+ case lists:member(no_defs, get(options)) of
+ true ->
+ error("Missing access function for ~s", [TableME#me.aliasname]);
+ false ->
+ ?vinfo("No accessfunction for '~w' => using default",
+ [TableME#me.aliasname]),
+ set_default_function([TableME, EntryME | Columns], DBName)
+ end;
+
+insert_default_mfa([TableME, EntryME | Columns], _DBName, Mod) ->
+ [TableME,
+ EntryME#me{mfa = {Mod, TableME#me.aliasname, []}} |
+ Columns].
+
+
+%% returns boolean.
+is_same_table(Oid, TableOid) ->
+ lists:prefix(Oid, TableOid).
+
+%% returns false | {value, ME}
+lookup(UniqName, MEs) when is_atom(UniqName) ->
+ lists:keysearch(UniqName, #me.aliasname, MEs);
+lookup(Oid, MEs) when is_list(Oid) ->
+ lists:keysearch(Oid, #me.oid, MEs).
+
+search_for_dublettes(PrevME, [ME|_MEs])
+ when PrevME#me.oid =:= ME#me.oid ->
+ error("Multiple used object with OBJECT IDENTIFIER '~w'. "
+ "Used in '~w' and '~w'.", [PrevME#me.oid,
+ PrevME#me.aliasname,
+ ME#me.aliasname]);
+search_for_dublettes(PrevME, [ME|MEs])
+ when ((PrevME#me.entrytype =:= variable) andalso
+ (ME#me.entrytype =:= variable)) ->
+ case lists:prefix(PrevME#me.oid, ME#me.oid) of
+ true ->
+ error("Variable '~w' (~w) defined below other "
+ "variable '~w' (~w). ",
+ [ME#me.aliasname, ME#me.oid,
+ PrevME#me.aliasname, PrevME#me.oid]);
+ false ->
+ search_for_dublettes(ME, MEs)
+ end;
+search_for_dublettes(_PrevME, [ME|MEs]) ->
+ search_for_dublettes(ME, MEs);
+search_for_dublettes(_PrevME, []) ->
+ ok.
+
+
+search_for_oid_conflicts([Rec|Traps],MEs) when is_record(Rec,notification) ->
+ #notification{oid = Oid, trapname = Name} = Rec,
+ case search_for_oid_conflicts1(Oid,MEs) of
+ {error,ME} ->
+ error("Notification with OBJECT IDENTIFIER '~w'. "
+ "Used in '~w' and '~w'.", [Oid,Name,ME#me.aliasname]);
+ ok ->
+ search_for_oid_conflicts(Traps,MEs)
+ end;
+search_for_oid_conflicts([_Trap|Traps],MEs) ->
+ search_for_oid_conflicts(Traps,MEs);
+search_for_oid_conflicts([],_MEs) ->
+ ok.
+
+search_for_oid_conflicts1(_Oid,[]) ->
+ ok;
+search_for_oid_conflicts1(Oid,[ME|_MEs]) when Oid == ME#me.oid ->
+ {error,ME};
+search_for_oid_conflicts1(Oid,[_ME|MEs]) ->
+ search_for_oid_conflicts1(Oid,MEs).
+
+set_default_function([TableMe, EntryMe | ColMes], DBName) ->
+ #me{aliasname = Name} = TableMe,
+ check_rowstatus(TableMe),
+ [TableMe,
+ EntryMe#me{mfa = {snmp_generic, table_func, [{Name, DBName}]}} |
+ ColMes];
+
+set_default_function(MibEntry,DBName) when MibEntry#me.entrytype == variable ->
+ #me{aliasname = Aname} = MibEntry,
+ MibEntry#me{mfa = {snmp_generic, variable_func, [{Aname, DBName}]}}.
+
+check_rowstatus(TableME) ->
+ {value,TableInfo} = snmpc_misc:assq(table_info,TableME#me.assocList),
+ case TableInfo#table_info.status_col of
+ undefined ->
+ ?vwarning("No RowStatus column in table ~w => "
+ "The default functions won't work properly",
+ [TableME#me.aliasname]);
+ _Q -> ok
+ end.
+
+check_trap(#trap{trapname=N1, specificcode=C, enterpriseoid=E},
+ #trap{trapname=N2, specificcode=C, enterpriseoid=E},Line) ->
+ print_error("Trap code collision. Enterprise: ~w. Trapcode: ~w, "
+ "Name of traps: ~w, ~w.", [E, C, N1, N2],Line),
+ error;
+check_trap(#trap{trapname=N, specificcode=C1, enterpriseoid=E1},
+ #trap{trapname=N, specificcode=C2, enterpriseoid=E2},Line) ->
+ print_error("Trap name collision. Name: ~w Enterprises: ~w, ~w. "
+ "Trapcodes: ~w, ~w", [N, E1, E2, C1, C2],Line),
+ error;
+check_trap(_OldTrap, _ThisTrap, _Line) ->
+ ok.
+
+check_notification(Notif, Line, Notifs) ->
+ lists:map(fun (OtherNotif) ->
+ check_notification1(Notifs,OtherNotif,Line)
+ end, lists:delete(Notif,Notifs)).
+
+check_notification1(#notification{trapname=N},#notification{trapname=N},Line)->
+ print_error("Trap name collision for '~w.",[N],Line);
+check_notification1(#notification{oid=Oid},#notification{oid=Oid},Line)->
+ print_error("Trap oid collision for '~w.",[Oid],Line);
+check_notification1(_T1, _T2, _L) ->
+ ok.
+
+%%----------------------------------------------------------------------
+%% Returns: list of {VariableName, variable_info-record}
+%%----------------------------------------------------------------------
+extract_variable_infos([]) -> [];
+extract_variable_infos([#me{entrytype = variable,
+ assocList = AL,
+ aliasname = Name} | T]) ->
+ {value, VI} = snmpc_misc:assq(variable_info, AL),
+ [{Name, VI} | extract_variable_infos(T)];
+extract_variable_infos([_ME | T]) ->
+ extract_variable_infos(T).
+
+%%----------------------------------------------------------------------
+%% Returns: list of {TableName, table_info-record}
+%%----------------------------------------------------------------------
+extract_table_infos([]) -> [];
+extract_table_infos([#me{entrytype = table,
+ assocList = AL,
+ aliasname = Name} | T]) ->
+ {value, VI} = snmpc_misc:assq(table_info, AL),
+ [{Name, VI} | extract_table_infos(T)];
+extract_table_infos([_ME | T]) ->
+ extract_table_infos(T).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Section for misc useful functions
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% m(M) -> {?MODULE, M}.
+
+set_dir(File, NewDir) ->
+ case string:chr(File, $/) of
+ 0 -> lists:append(NewDir, File);
+ N -> set_dir(lists:nthtail(N,File), NewDir)
+ end.
+
+%% Look up a key in a list, and if found returns the value
+%% or if not found returns the default value
+key1search(Key, List) ->
+ key1search(Key, List, undefined).
+
+key1search(Key, List, Default) ->
+ case lists:keysearch(Key, 1, List) of
+ {value, {Key, Val}} -> Val;
+ _ -> Default
+ end.
+
+
+%% print the compiled mib
+look_at(FileName) ->
+ case file:read_file(FileName) of
+ {ok,Bin} ->
+ binary_to_term(Bin);
+ {error,Reason} ->
+ {error,Reason}
+ end.
+
+
+%% Data is appended to compiler information
+add_cdata(OffsetInRecord, ListOfData) ->
+ CDATA = get(cdata),
+ OldData = element(OffsetInRecord, CDATA),
+ put(cdata, setelement(OffsetInRecord, CDATA, lists:append(ListOfData,
+ OldData))),
+ undefined.
+
+check_sub_ids([H | _T], Name, Line) when H < 0 ->
+ error("OBJECT IDENTIFIER must have all sub "
+ "indexes > 0. Name: '~w'. Illegal sub index: ~w.",
+ [Name, H], Line);
+check_sub_ids([H | _T], Name, Line) when H > 4294967295 ->
+ error("OBJECT IDENTIFIER must have all sub "
+ "indexes < 4294967295. Name: '~w'. Illegal sub index: ~w.",
+ [Name, H], Line);
+check_sub_ids([_H | T], Name, Line) ->
+ check_sub_ids(T, Name, Line);
+check_sub_ids([], _Name, _Line) ->
+ ok.
+
+
+%%-----------------------------------------------------------------
+%% Handle forward references:
+%% This code handles OIDs that are defined in terms of a
+%% parent OID that is defined later in the file. Ex:
+%% x OBJECT IDENTIFIER ::= {y 1}
+%% y OBJECT IDENTIFIER ::= {enterprises 1}
+%% The following alg is used to handle this:
+%% Datastructure:
+%% An ets table, with one entry for each object in the mib:
+%% {Name, FatherName, Line, SubIndex, Children}
+%% Name : aliasname
+%% FatherName : aliasname of parent object
+%% SubIndex : list of subindexes from parent object
+%% Children : list of aliasnames for all objects registered
+%% under this one
+%% FatherName == 'root' => top-level object
+%% 1) When an OID is found in the mib, it is registered using
+%% register_oid/4. This function updates the parent entry,
+%% by adding the new name to its Children list. It also
+%% updates the entry for the object defined.
+%% 2) When all objects are registered, the ets table contains
+%% a directed graph of all objects.
+%% 3) resolve_oids/1 is called. This function traverses the
+%% graph, starting at 'root', and changes each entry to
+%% {Name, Line, Oid}, where Oid is a list of integers.
+%% 4) The list of MibEntries is traversed. Each object is
+%% looked up in the ets table, and the correspsonding oid
+%% is updated. The functions for this is update_me_oids/2
+%% and update_trap_oids/2.
+%%-----------------------------------------------------------------
+register_oid(Line, Name, ParentName, SubIndex) when Name =/= '$no_name$' ->
+ ?vtrace2("register_oid -> entry with"
+ "~n Name: ~p"
+ "~n ParentName: ~p"
+ "~n SubIndex: ~p", [Name, ParentName, SubIndex], Line),
+ check_sub_ids(SubIndex, Name, Line),
+ OidEts = (get(cdata))#cdata.oid_ets,
+ %% Lookup Parent - if it doesn't already exist, create it
+ {_ParentName, ItsParentName, ItsLine, ItsSubIndex, Children} =
+ case ets:lookup(OidEts, ParentName) of
+ [Found] ->
+ ?vtrace("register_oid -> parent found: "
+ "~n ~p", [Found]),
+ Found;
+ [] ->
+ ?vtrace("register_oid -> father not found: "
+ "~n ~p", [ets:tab2list(OidEts)]),
+ {ParentName, undef, undef, [], []}
+ end,
+ %% Update Father with a pointer to us
+ NChildren = case lists:member(Name, Children) of
+ true -> Children;
+ false -> [Name | Children]
+ end,
+ NParent = {ParentName, ItsParentName, ItsLine, ItsSubIndex, NChildren},
+ ?vtrace("register_oid -> NParent: ~n~p", [NParent]),
+ ets:insert(OidEts, NParent),
+ %% Lookup ourselves - if we don't exist, create us
+ MyChildren =
+ case ets:lookup(OidEts, Name) of
+ [Found2] ->
+ ?vtrace("register_oid -> children found: "
+ "~n ~p", [Found2]),
+ element(5, Found2);
+ [] ->
+ ?vtrace("register_oid -> no children found", []),
+ []
+ end,
+ %% Update ourselves
+ NSelf = {Name, ParentName, Line, SubIndex, MyChildren},
+ ?vtrace("register_oid -> NSelf: "
+ "~n ~p", [NSelf]),
+ ets:insert(OidEts, NSelf);
+register_oid(_Line, _Name, _ParentName, _SubIndex) ->
+ ok.
+
+
+resolve_oids(OidEts) ->
+ case ets:lookup(OidEts, root) of
+ [{_, _, _, _, RootChildren}] ->
+ resolve_oids(RootChildren, [], OidEts);
+ [] ->
+ ok
+ end.
+
+resolve_oids([Name | T], FatherOid, OidEts) ->
+ {MyOid, MyChildren, MyLine} =
+ case ets:lookup(OidEts, Name) of
+ [{Name, Oid, Line}] ->
+ print_error("Circular OBJECT IDENTIFIER definitions "
+ "involving ~w\n", [Name], Line),
+ {Oid, [], Line};
+ [{Name, _Father, Line, SubIndex, Children}] ->
+ {FatherOid ++ SubIndex, Children, Line}
+ end,
+ ets:insert(OidEts, {Name, MyOid, MyLine}),
+ resolve_oids(T, FatherOid, OidEts),
+ resolve_oids(MyChildren, MyOid, OidEts);
+resolve_oids([], _, _) ->
+ ok.
+
+
+
+update_me_oids([], _OidEts, Acc) ->
+ lists:reverse(Acc);
+update_me_oids([#me{aliasname = '$no_name$'} | Mes], OidEts, Acc) ->
+ update_me_oids(Mes, OidEts, Acc);
+update_me_oids([Me | Mes], OidEts, Acc) ->
+ ?vtrace("update_me_oids -> entry with"
+ "~n Me: ~p", [Me]),
+ Oid = tr_oid(Me#me.aliasname, OidEts),
+ NMe = resolve_oid_defval(Me, OidEts),
+ update_me_oids(Mes, OidEts, [NMe#me{oid = Oid} | Acc]).
+
+update_trap_oids([], _OidEts, Acc) ->
+ lists:reverse(Acc);
+update_trap_oids([#trap{enterpriseoid = EOid,
+ oidobjects = OidObjs} = Trap | Traps],
+ OidEts, Acc) ->
+ ?vtrace("update_trap_oids -> entry with"
+ "~n EOid: ~p", [EOid]),
+ NEnter = tr_oid(EOid, OidEts),
+ NOidObjs = tr_oid_objs(OidObjs, OidEts),
+ NTrap = Trap#trap{enterpriseoid = NEnter,
+ oidobjects = NOidObjs},
+ update_trap_oids(Traps, OidEts, [NTrap|Acc]);
+update_trap_oids([#notification{trapname = Name,
+ oidobjects = OidObjs} = Notif | Traps],
+ OidEts, Acc) ->
+ ?vtrace("update_trap_oids -> entry with"
+ "~n Name: ~p", [Name]),
+ Oid = tr_oid(Name, OidEts),
+ NOidObjs = tr_oid_objs(OidObjs, OidEts),
+ NNotif = Notif#notification{oid = Oid, oidobjects = NOidObjs},
+ update_trap_oids(Traps, OidEts, [NNotif|Acc]).
+
+tr_oid(Name, OidEts) ->
+ ?vtrace("tr_oid -> entry with"
+ "~n Name: ~p", [Name]),
+ case ets:lookup(OidEts, Name) of
+ [{Name, MyOid, _MyLine}] ->
+ MyOid;
+ [{_Natrap, Parent, Line, SubIndex, _Children}] ->
+ print_error("OBJECT IDENTIFIER [~w] defined in terms "
+ "of undefined parent object. Parent: '~w'."
+ "(Sub-indexes: ~w.)",
+ [Name, Parent, SubIndex], Line),
+ ?vtrace("tr_oid -> ets:tab2list(~w): "
+ "~n ~p", [OidEts, ets:tab2list(OidEts)]),
+ rnd_oid()
+ end.
+
+tr_oid_objs([{{variable, Name}, Type} | T], OidEts) ->
+ ?vtrace("tr_oid_objs(variable) -> entry with"
+ "~n Name: ~p", [Name]),
+ Oid = tr_oid(Name, OidEts) ++ [0],
+ [{Oid, Type} | tr_oid_objs(T, OidEts)];
+tr_oid_objs([{{column, Name}, Type} | T], OidEts) ->
+ ?vtrace("tr_oid_objs(column) -> entry with"
+ "~n Name: ~p", [Name]),
+ Oid = tr_oid(Name, OidEts),
+ [{Oid, Type} | tr_oid_objs(T, OidEts)];
+tr_oid_objs([], _OidEts) ->
+ [].
+
+
+resolve_oid_defval(ME, OidEts)
+ when (ME#me.asn1_type)#asn1_type.bertype == 'OBJECT IDENTIFIER' ->
+ #me{aliasname = MEName, assocList = AssocList} = ME,
+ case snmpc_misc:assq(defval, AssocList) of
+ {value, DefVal} when is_atom(DefVal) ->
+ case ets:lookup(OidEts, DefVal) of
+ [{_, Oid, _}] ->
+ ME#me{assocList = lists:keyreplace(defval, 1, AssocList,
+ {defval, Oid})};
+ _ ->
+ print_error("Can not find OBJECT-TYPE definition for '~w' "
+ "Used in DEFVAL for '~w'.", [DefVal, MEName]),
+ ME
+ end;
+ _ ->
+ ME
+ end;
+resolve_oid_defval(ME, _OidEts) ->
+ ME.
+
+rnd_oid() ->
+ [99,99]. %% '99' means "stop computer" in Y2Kish...
+
+error(FormatStr, Data) when is_list(FormatStr) ->
+ print_error(FormatStr,Data),
+ exit(error).
+
+error(FormatStr, Data, Line) when is_list(FormatStr) ->
+ print_error(FormatStr,Data,Line),
+ exit(error).
+
+print_error(FormatStr, Data) when is_list(FormatStr) ->
+ ok = io:format("~s: Error: " ++ FormatStr,[get(filename)|Data]),
+ put(errors,yes),
+ io:format("~n").
+
+print_error(FormatStr, Data,Line) when is_list(FormatStr) ->
+ ok = io:format("~s: ~w: Error: " ++ FormatStr,[get(filename), Line |Data]),
+ put(errors,yes),
+ io:format("~n").
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Section for debug functions
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+vprint(Severity, Mod, Line, MibLine, F, A) ->
+ case printable(Severity) of
+ standard when is_integer(MibLine) ->
+ io:format("[~s:~w][~s]: " ++ F ++ "~n",
+ [get(filename), MibLine, image_of_severity(Severity)|A]);
+ standard ->
+ io:format("[~s][~s]: " ++ F ++ "~n",
+ [get(filename), image_of_severity(Severity)|A]);
+ extended when is_integer(MibLine) ->
+ io:format("[~s:~w][~w:~w][~s]: " ++ F ++ "~n",
+ [get(filename), MibLine, Mod, Line,
+ image_of_severity(Severity)|A]);
+ extended ->
+ io:format("[~s][~w:~w][~s]: " ++ F ++ "~n",
+ [get(filename), Mod, Line,
+ image_of_severity(Severity)|A]);
+ _ ->
+ ok
+ end.
+
+printable(Severity) ->
+ printable(get(verbosity), Severity).
+
+printable(silence, _) -> none;
+printable(warning, warning) -> standard;
+printable(info, info) -> standard;
+printable(info, warning) -> standard;
+printable(log, warning) -> standard;
+printable(log, info) -> standard;
+printable(log, log) -> standard;
+printable(debug, warning) -> standard;
+printable(debug, info) -> standard;
+printable(debug, log) -> standard;
+printable(debug, debug) -> standard;
+printable(trace, _Sev) -> extended;
+printable(_Ver, _Sev) -> none.
+
+-spec image_of_severity(Sev :: severity()) -> string().
+image_of_severity(warning) -> "WAR";
+image_of_severity(info) -> "INF";
+image_of_severity(log) -> "LOG";
+image_of_severity(debug) -> "DBG";
+image_of_severity(trace) -> "TRC";
+image_of_severity(_) -> " - ".
+
+
+vvalidate(silence) -> ok;
+vvalidate(warning) -> ok;
+vvalidate(info) -> ok;
+vvalidate(log) -> ok;
+vvalidate(debug) -> ok;
+vvalidate(trace) -> ok;
+vvalidate(V) -> exit({invalid_verbosity,V}).
+
+
diff --git a/lib/snmp/src/compile/snmpc_lib.hrl b/lib/snmp/src/compile/snmpc_lib.hrl
new file mode 100644
index 0000000000..000486e728
--- /dev/null
+++ b/lib/snmp/src/compile/snmpc_lib.hrl
@@ -0,0 +1,37 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 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%
+%%
+
+-ifndef(snmpc_lib).
+-define(snmpc_lib, true).
+
+-define(vwarning(F, A), ?verbosity(warning, F, A, ignore)).
+-define(vwarning2(F, A, MibLine), ?verbosity(warning, F, A, MibLine)).
+-define(vinfo(F, A), ?verbosity(info, F, A, ignore)).
+-define(vinfo2(F, A, MibLine), ?verbosity(info, F, A, MibLine)).
+-define(vlog(F, A), ?verbosity(log, F, A, ignore)).
+-define(vlog2(F, A, MibLine), ?verbosity(log, F, A, MibLine)).
+-define(vdebug(F, A), ?verbosity(debug, F, A, ignore)).
+-define(vdebug2(F, A, MibLine), ?verbosity(debug, F, A, MibLine)).
+-define(vtrace(F, A), ?verbosity(trace, F, A, ignore)).
+-define(vtrace2(F, A, MibLine), ?verbosity(trace, F, A, MibLine)).
+
+-define(verbosity(Severity, F, A, MibLine),
+ snmpc_lib:vprint(Severity, ?MODULE, ?LINE, MibLine, F, A)).
+
+-endif. % -ifndef(snmpc_lib).
diff --git a/lib/snmp/src/compile/snmpc_mib_gram.yrl b/lib/snmp/src/compile/snmpc_mib_gram.yrl
new file mode 100644
index 0000000000..1957f52936
--- /dev/null
+++ b/lib/snmp/src/compile/snmpc_mib_gram.yrl
@@ -0,0 +1,973 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-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%
+%%
+
+%%----------------------------------------------------------------------
+%% Number of expected shift/reduce warnings
+%% This is ugly but...
+%%----------------------------------------------------------------------
+
+Expect 2.
+
+
+%% ----------------------------------------------------------------------
+Nonterminals
+%% ----------------------------------------------------------------------
+accessv1
+definition
+defvalpart
+description
+descriptionfield
+displaypart
+entry
+namedbits
+fatherobjectname
+fieldname
+fields
+implies
+import
+import_stuff
+imports
+imports_from_one_mib
+index
+indexpartv1
+indextypev1
+indextypesv1
+parentintegers
+listofdefinitions
+listofimports
+mib
+mibname
+nameassign
+newtype
+newtypename
+objectidentifier
+objectname
+objecttypev1
+range_num
+referpart
+size
+sizedescr
+statusv1
+syntax
+tableentrydefinition
+traptype
+type
+usertype
+variables
+varpart
+
+%v2
+moduleidentity
+revisionpart
+revisions
+listofdefinitionsv2
+mibid
+last_updated
+oranization
+contact_info
+revision
+revision_string
+revision_desc
+v1orv2
+objectidentity
+objecttypev2
+unitspart
+indexpartv2
+indextypesv2
+indextypev2
+statusv2
+accessv2
+notification
+objectspart
+objects
+definitionv2
+textualconvention
+objectgroup
+notificationgroup
+modulecompliance
+modulepart
+modules
+module
+modulenamepart
+mandatorypart
+compliancepart
+compliances
+compliance
+compliancegroup
+object
+syntaxpart
+writesyntaxpart
+accesspart
+fsyntax
+defbitsvalue
+defbitsnames
+.
+%% ----------------------------------------------------------------------
+Terminals
+%% ----------------------------------------------------------------------
+integer variable atom string quote '{' '}' '::=' ':' '=' ',' '.' '(' ')' ';' '|'
+'ACCESS'
+'BEGIN'
+'BIT'
+'Counter'
+'DEFINITIONS'
+'DEFVAL'
+'DESCRIPTION'
+'DISPLAY-HINT'
+'END'
+'ENTERPRISE'
+'FROM'
+'Gauge'
+'IDENTIFIER'
+'IMPORTS'
+'INDEX'
+'INTEGER'
+'IpAddress'
+'NetworkAddress'
+'OBJECT'
+'OBJECT-TYPE'
+'OCTET'
+'OF'
+'Opaque'
+'REFERENCE'
+'SEQUENCE'
+'SIZE'
+'STATUS'
+'STRING'
+'SYNTAX'
+'TRAP-TYPE'
+'TimeTicks'
+'VARIABLES'
+
+%v2
+'LAST-UPDATED'
+'ORGANIZATION'
+'CONTACT-INFO'
+'MODULE-IDENTITY'
+'NOTIFICATION-TYPE'
+'MODULE-COMPLIANCE'
+'OBJECT-GROUP'
+'NOTIFICATION-GROUP'
+'REVISION'
+'OBJECT-IDENTITY'
+'MAX-ACCESS'
+'UNITS'
+'AUGMENTS'
+'IMPLIED'
+'OBJECTS'
+'TEXTUAL-CONVENTION'
+'NOTIFICATIONS'
+'MODULE'
+'MANDATORY-GROUPS'
+'GROUP'
+'WRITE-SYNTAX'
+'MIN-ACCESS'
+'BITS'
+'DisplayString'
+'PhysAddress'
+'MacAddress'
+'TruthValue'
+'TestAndIncr'
+'AutonomousType'
+'InstancePointer'
+'VariablePointer'
+'RowPointer'
+'RowStatus'
+'TimeStamp'
+'TimeInterval'
+'DateAndTime'
+'StorageType'
+'TDomain'
+'TAddress'
+.
+
+
+Rootsymbol mib.
+Endsymbol '$end'.
+
+% **********************************************************************
+
+mib -> mibname 'DEFINITIONS' implies 'BEGIN'
+ import v1orv2 'END'
+ : {Version, Defs} = '$6',
+ #pdata{mib_version = Version,
+ mib_name = '$1',
+ imports = '$5',
+ defs = Defs}.
+
+v1orv2 -> moduleidentity listofdefinitionsv2 :
+ {v2_mib, ['$1'|lists:reverse('$2')]}.
+v1orv2 -> listofdefinitions : {v1_mib, lists:reverse('$1')}.
+
+definition -> objectidentifier : '$1'.
+definition -> objecttypev1 : '$1'.
+definition -> newtype : '$1'.
+definition -> tableentrydefinition : '$1'.
+definition -> traptype : '$1'.
+
+listofdefinitions -> definition : ['$1'] .
+listofdefinitions -> listofdefinitions definition : ['$2' | '$1'].
+
+import -> '$empty' : [].
+import -> 'IMPORTS' imports ';' : '$2'.
+
+imports -> imports_from_one_mib : ['$1'].
+imports -> imports_from_one_mib imports : ['$1' | '$2'].
+
+imports_from_one_mib -> listofimports 'FROM' variable :
+ {{val('$3'), lists:reverse('$1')}, line_of('$2')}.
+
+listofimports -> import_stuff : ['$1'].
+listofimports -> listofimports ',' import_stuff : ['$3' | '$1'].
+
+import_stuff -> 'OBJECT-TYPE' : {builtin, 'OBJECT-TYPE'}.
+import_stuff -> 'TRAP-TYPE' : {builtin, 'TRAP-TYPE'}.
+import_stuff -> 'NetworkAddress' : {builtin, 'NetworkAddress'}.
+import_stuff -> 'TimeTicks' : {builtin, 'TimeTicks'}.
+import_stuff -> 'IpAddress' : {builtin, 'IpAddress'}.
+import_stuff -> 'Counter' : {builtin, 'Counter'}.
+import_stuff -> 'Gauge' : {builtin, 'Gauge'}.
+import_stuff -> 'Opaque' : {builtin, 'Opaque'}.
+import_stuff -> variable : filter_v2imports(get(snmp_version), val('$1')).
+import_stuff -> atom : {node, val('$1')}.
+%%v2
+import_stuff -> 'MODULE-IDENTITY'
+ : ensure_ver(2,'$1'), {builtin, 'MODULE-IDENTITY'}.
+import_stuff -> 'NOTIFICATION-TYPE'
+ : ensure_ver(2,'$1'), {builtin, 'NOTIFICATION-TYPE'}.
+import_stuff -> 'MODULE-COMPLIANCE'
+ : ensure_ver(2,'$1'), {builtin, 'MODULE-COMPLIANCE'}.
+import_stuff -> 'NOTIFICATION-GROUP'
+ : ensure_ver(2,'$1'), {builtin, 'NOTIFICATION-GROUP'}.
+import_stuff -> 'OBJECT-GROUP'
+ : ensure_ver(2,'$1'), {builtin, 'OBJECT-GROUP'}.
+import_stuff -> 'OBJECT-IDENTITY'
+ : ensure_ver(2,'$1'), {builtin, 'OBJECT-IDENTITY'}.
+import_stuff -> 'TEXTUAL-CONVENTION'
+ : ensure_ver(2,'$1'), {builtin, 'TEXTUAL-CONVENTION'}.
+import_stuff -> 'DisplayString'
+ : ensure_ver(2,'$1'), {builtin, 'DisplayString'}.
+import_stuff -> 'PhysAddress'
+ : ensure_ver(2,'$1'), {builtin, 'PhysAddress'}.
+import_stuff -> 'MacAddress'
+ : ensure_ver(2,'$1'), {builtin, 'MacAddress'}.
+import_stuff -> 'TruthValue'
+ : ensure_ver(2,'$1'), {builtin, 'TruthValue'}.
+import_stuff -> 'TestAndIncr'
+ : ensure_ver(2,'$1'), {builtin, 'TestAndIncr'}.
+import_stuff -> 'AutonomousType'
+ : ensure_ver(2,'$1'), {builtin, 'AutonomousType'}.
+import_stuff -> 'InstancePointer'
+ : ensure_ver(2,'$1'), {builtin, 'InstancePointer'}.
+import_stuff -> 'VariablePointer'
+ : ensure_ver(2,'$1'), {builtin, 'VariablePointer'}.
+import_stuff -> 'RowPointer'
+ : ensure_ver(2,'$1'), {builtin, 'RowPointer'}.
+import_stuff -> 'RowStatus'
+ : ensure_ver(2,'$1'), {builtin, 'RowStatus'}.
+import_stuff -> 'TimeStamp'
+ : ensure_ver(2,'$1'), {builtin, 'TimeStamp'}.
+import_stuff -> 'TimeInterval'
+ : ensure_ver(2,'$1'), {builtin, 'TimeInterval'}.
+import_stuff -> 'DateAndTime'
+ : ensure_ver(2,'$1'), {builtin, 'DateAndTime'}.
+import_stuff -> 'StorageType'
+ : ensure_ver(2,'$1'), {builtin, 'StorageType'}.
+import_stuff -> 'TDomain'
+ : ensure_ver(2,'$1'), {builtin, 'TDomain'}.
+import_stuff -> 'TAddress'
+ : ensure_ver(2,'$1'), {builtin, 'TAddress'}.
+
+traptype -> objectname 'TRAP-TYPE' 'ENTERPRISE' objectname varpart
+ description referpart implies integer :
+ Trap = make_trap('$1', '$4', lists:reverse('$5'),
+ '$6', '$7', val('$9')),
+ {Trap, line_of('$2')}.
+
+% defines a name to an internal node.
+objectidentifier -> objectname 'OBJECT' 'IDENTIFIER' nameassign :
+ {Parent, SubIndex} = '$4',
+ Int = make_internal('$1', dummy, Parent, SubIndex),
+ {Int, line_of('$2')}.
+
+% defines name, access and type for a variable.
+objecttypev1 -> objectname 'OBJECT-TYPE'
+ 'SYNTAX' syntax
+ 'ACCESS' accessv1
+ 'STATUS' statusv1
+ 'DESCRIPTION' descriptionfield
+ referpart indexpartv1 defvalpart
+ nameassign :
+ Kind = kind('$13', '$12'),
+ OT = make_object_type('$1', '$4', '$6', '$8', '$10',
+ '$11', Kind, '$14'),
+ {OT, line_of('$2')}.
+
+newtype -> newtypename implies syntax :
+ NT = make_new_type('$1', dummy, '$3'),
+ {NT, line_of('$2')}.
+
+tableentrydefinition -> newtypename implies 'SEQUENCE' '{' fields '}' :
+ Seq = make_sequence('$1', lists:reverse('$5')),
+ {Seq, line_of('$3')}.
+
+% returns: list of {<fieldname>, <asn1_type>}
+fields -> fieldname fsyntax :
+ [{val('$1'), '$2'}].
+
+fields -> fields ',' fieldname fsyntax : [{val('$3'), '$4'} | '$1'].
+
+fsyntax -> 'BITS' : {{bits,[{dummy,0}]},line_of('$1')}.
+fsyntax -> syntax : '$1'.
+
+fieldname -> atom : '$1'.
+
+syntax -> usertype : {{type, val('$1')}, line_of('$1')}.
+syntax -> type : {{type, cat('$1')},line_of('$1')}.
+syntax -> type size : {{type_with_size, cat('$1'), '$2'},line_of('$1')}.
+syntax -> usertype size : {{type_with_size,val('$1'), '$2'},line_of('$1')}.
+syntax -> 'INTEGER' '{' namedbits '}' :
+ {{integer_with_enum, 'INTEGER', '$3'}, line_of('$1')}.
+syntax -> 'BITS' '{' namedbits '}' :
+ ensure_ver(2,'$1'),
+ {{bits, '$3'}, line_of('$1')}.
+syntax -> 'SEQUENCE' 'OF' usertype :
+ {{sequence_of,val('$3')},line_of('$1')}.
+
+size -> '(' sizedescr ')' : make_range('$2').
+size -> '(' 'SIZE' '(' sizedescr ')' ')' : make_range('$4').
+
+%% Returns a list of integers describing a range.
+sizedescr -> range_num '.' '.' range_num : ['$1', '$4'].
+sizedescr -> range_num '.' '.' range_num sizedescr :['$1', '$4' |'$5'].
+sizedescr -> range_num : ['$1'].
+sizedescr -> sizedescr '|' sizedescr : ['$1', '$3'].
+
+range_num -> integer : val('$1') .
+range_num -> quote atom : make_range_integer(val('$1'), val('$2')) .
+range_num -> quote variable : make_range_integer(val('$1'), val('$2')) .
+
+namedbits -> atom '(' integer ')' : [{val('$1'), val('$3')}].
+namedbits -> namedbits ',' atom '(' integer ')' :
+ [{val('$3'), val('$5')} | '$1'].
+
+usertype -> variable : '$1'.
+
+type -> 'OCTET' 'STRING' : {'OCTET STRING', line_of('$1')}.
+type -> 'BIT' 'STRING' : {'BIT STRING', line_of('$1')}.
+type -> 'OBJECT' 'IDENTIFIER' : {'OBJECT IDENTIFIER', line_of('$1')}.
+type -> 'INTEGER' : '$1'.
+type -> 'NetworkAddress' : '$1'.
+type -> 'IpAddress' : '$1'.
+type -> 'Counter' : ensure_ver(1,'$1'),'$1'.
+type -> 'Gauge' : ensure_ver(1,'$1'),'$1'.
+type -> 'TimeTicks' : '$1'.
+type -> 'Opaque' : '$1'.
+type -> 'DisplayString' : ensure_ver(2,'$1'), '$1'.
+type -> 'PhysAddress' : ensure_ver(2,'$1'), '$1'.
+type -> 'MacAddress' : ensure_ver(2,'$1'), '$1'.
+type -> 'TruthValue' : ensure_ver(2,'$1'), '$1'.
+type -> 'TestAndIncr' : ensure_ver(2,'$1'), '$1'.
+type -> 'AutonomousType' : ensure_ver(2,'$1'), '$1'.
+type -> 'InstancePointer' : ensure_ver(2,'$1'), '$1'.
+type -> 'VariablePointer' : ensure_ver(2,'$1'), '$1'.
+type -> 'RowPointer' : ensure_ver(2,'$1'), '$1'.
+type -> 'RowStatus' : ensure_ver(2,'$1'), '$1'.
+type -> 'TimeStamp' : ensure_ver(2,'$1'), '$1'.
+type -> 'TimeInterval' : ensure_ver(2,'$1'), '$1'.
+type -> 'DateAndTime' : ensure_ver(2,'$1'), '$1'.
+type -> 'StorageType' : ensure_ver(2,'$1'), '$1'.
+type -> 'TDomain' : ensure_ver(2,'$1'), '$1'.
+type -> 'TAddress' : ensure_ver(2,'$1'), '$1'.
+
+% Returns: {FatherName, SubIndex} (the parent)
+nameassign -> implies '{' fatherobjectname parentintegers '}' : {'$3', '$4' }.
+nameassign -> implies '{' parentintegers '}' : { root, '$3'}.
+
+
+varpart -> '$empty' : [].
+varpart -> 'VARIABLES' '{' variables '}' : '$3'.
+variables -> objectname : ['$1'].
+variables -> variables ',' objectname : ['$3' | '$1'].
+
+implies -> '::=' : '$1'.
+implies -> ':' ':' '=' : w("Sloppy asignment on line ~p", [line_of('$1')]), '$1'.
+descriptionfield -> string : lists:reverse(val('$1')).
+descriptionfield -> '$empty' : undefined.
+description -> 'DESCRIPTION' string : lists:reverse(val('$2')).
+description -> '$empty' : undefined.
+
+displaypart -> 'DISPLAY-HINT' string : display_hint('$2') .
+displaypart -> '$empty' : undefined .
+
+% returns: {indexes, undefined}
+% | {indexes, IndexList} where IndexList is a list of aliasnames.
+indexpartv1 -> 'INDEX' '{' indextypesv1 '}' : {indexes, lists:reverse('$3')}.
+indexpartv1 -> '$empty' : {indexes, undefined}.
+
+indextypesv1 -> indextypev1 : ['$1'].
+indextypesv1 -> indextypesv1 ',' indextypev1 : ['$3' | '$1'].
+
+indextypev1 -> index : '$1'.
+
+index -> objectname : '$1'.
+
+parentintegers -> integer : [val('$1')].
+parentintegers -> atom '(' integer ')' : [val('$3')].
+parentintegers -> integer parentintegers : [val('$1') | '$2'].
+parentintegers -> atom '(' integer ')' parentintegers : [val('$3') | '$5'].
+
+defvalpart -> 'DEFVAL' '{' integer '}' : {defval, val('$3')}.
+defvalpart -> 'DEFVAL' '{' atom '}' : {defval, val('$3')}.
+defvalpart -> 'DEFVAL' '{' '{' defbitsvalue '}' '}' : {defval, '$4'}.
+defvalpart -> 'DEFVAL' '{' quote atom '}'
+ : {defval, make_defval_for_string(line_of('$1'), lists:reverse(val('$3')),
+ val('$4'))}.
+defvalpart -> 'DEFVAL' '{' quote variable '}'
+ : {defval, make_defval_for_string(line_of('$1'), lists:reverse(val('$3')),
+ val('$4'))}.
+defvalpart -> 'DEFVAL' '{' string '}'
+ : {defval, lists:reverse(val('$3'))}.
+defvalpart -> '$empty' : undefined.
+
+defbitsvalue -> defbitsnames : '$1'.
+defbitsvalue -> '$empty' : [].
+
+defbitsnames -> atom : [val('$1')].
+defbitsnames -> defbitsnames ',' atom : [val('$3') | '$1'].
+
+objectname -> atom : val('$1').
+mibname -> variable : val('$1').
+fatherobjectname -> objectname : '$1'.
+newtypename -> variable : val('$1').
+
+accessv1 -> atom: accessv1('$1').
+
+statusv1 -> atom : statusv1('$1').
+
+referpart -> 'REFERENCE' string : lists:reverse(val('$2')).
+referpart -> '$empty' : undefined.
+
+
+%%----------------------------------------------------------------------
+%% SNMPv2 grammatics
+%%v2
+%%----------------------------------------------------------------------
+moduleidentity -> mibid 'MODULE-IDENTITY'
+ 'LAST-UPDATED' last_updated
+ 'ORGANIZATION' oranization
+ 'CONTACT-INFO' contact_info
+ 'DESCRIPTION' descriptionfield
+ revisionpart nameassign :
+ MI = make_module_identity('$1', '$4', '$6', '$8',
+ '$10', '$11', '$12'),
+ {MI, line_of('$2')}.
+
+mibid -> atom : val('$1').
+last_updated -> string : lists:reverse(val('$1')) .
+oranization -> string : lists:reverse(val('$1')) .
+contact_info -> string : lists:reverse(val('$1')) .
+
+revisionpart -> '$empty' : [] .
+revisionpart -> revisions : lists:reverse('$1') .
+
+revisions -> revision : ['$1'] .
+revisions -> revisions revision : ['$2' | '$1'] .
+revision -> 'REVISION' revision_string 'DESCRIPTION' revision_desc :
+ make_revision('$2', '$4') .
+
+revision_string -> string : lists:reverse(val('$1')) .
+revision_desc -> string : lists:reverse(val('$1')) .
+
+definitionv2 -> objectidentifier : '$1'.
+definitionv2 -> objecttypev2 : '$1'.
+definitionv2 -> textualconvention : '$1'.
+definitionv2 -> objectidentity : '$1'.
+definitionv2 -> newtype : '$1'.
+definitionv2 -> tableentrydefinition : '$1'.
+definitionv2 -> notification : '$1'.
+definitionv2 -> objectgroup : '$1'.
+definitionv2 -> notificationgroup : '$1'.
+definitionv2 -> modulecompliance : '$1'.
+
+listofdefinitionsv2 -> '$empty' : [] .
+listofdefinitionsv2 -> listofdefinitionsv2 definitionv2 : ['$2' | '$1'].
+
+textualconvention -> newtypename implies 'TEXTUAL-CONVENTION' displaypart
+ 'STATUS' statusv2 description referpart 'SYNTAX' syntax :
+ NT = make_new_type('$1', 'TEXTUAL-CONVENTION', '$4',
+ '$6', '$7', '$8', '$10'),
+ {NT, line_of('$3')}.
+
+objectidentity -> objectname 'OBJECT-IDENTITY' 'STATUS' statusv2
+ 'DESCRIPTION' string referpart nameassign :
+ {Parent, SubIndex} = '$8',
+ Int = make_internal('$1', 'OBJECT-IDENTITY',
+ Parent, SubIndex),
+ {Int, line_of('$2')}.
+
+objectgroup -> objectname 'OBJECT-GROUP' objectspart
+ 'STATUS' statusv2 description referpart nameassign :
+ OG = make_object_group('$1', '$3', '$5', '$6', '$7', '$8'),
+ {OG, line_of('$2')}.
+
+notificationgroup -> objectname 'NOTIFICATION-GROUP' 'NOTIFICATIONS' '{'
+ objects '}' 'STATUS' statusv2 description referpart
+ nameassign :
+ NG = make_notification_group('$1', '$5', '$8', '$9',
+ '$10', '$11'),
+ {NG, line_of('$2')}.
+
+modulecompliance -> objectname 'MODULE-COMPLIANCE' 'STATUS' statusv2
+ description referpart modulepart nameassign :
+ MC = make_module_compliance('$1', '$4', '$5', '$6',
+ '$7', '$8'),
+ {MC, line_of('$2')}.
+
+modulepart -> '$empty'.
+modulepart -> modules.
+
+modules -> module.
+modules -> modules module.
+
+module -> 'MODULE' modulenamepart mandatorypart compliancepart.
+
+modulenamepart -> mibname.
+modulenamepart -> '$empty'.
+
+mandatorypart -> 'MANDATORY-GROUPS' '{' objects '}'.
+mandatorypart -> '$empty'.
+
+compliancepart -> compliances.
+compliancepart -> '$empty'.
+
+compliances -> compliance.
+compliances -> compliances compliance.
+
+compliance -> compliancegroup.
+compliance -> object.
+
+compliancegroup -> 'GROUP' objectname description.
+
+object -> 'OBJECT' objectname syntaxpart writesyntaxpart accesspart description.
+
+syntaxpart -> 'SYNTAX' syntax.
+syntaxpart -> '$empty'.
+
+writesyntaxpart -> 'WRITE-SYNTAX' syntax.
+writesyntaxpart -> '$empty'.
+
+accesspart -> 'MIN-ACCESS' accessv2.
+accesspart -> '$empty'.
+
+objecttypev2 -> objectname 'OBJECT-TYPE'
+ 'SYNTAX' syntax
+ unitspart
+ 'MAX-ACCESS' accessv2
+ 'STATUS' statusv2
+ 'DESCRIPTION' descriptionfield
+ referpart indexpartv2 defvalpart
+ nameassign :
+ Kind = kind('$14', '$13'),
+ OT = make_object_type('$1', '$4', '$5', '$7', '$9',
+ '$11', '$12', Kind, '$15'),
+ {OT, line_of('$2')}.
+
+indexpartv2 -> 'INDEX' '{' indextypesv2 '}' : {indexes, lists:reverse('$3')}.
+indexpartv2 -> 'AUGMENTS' '{' entry '}' : {augments, '$3'}.
+indexpartv2 -> '$empty' : {indexes, undefined}.
+
+indextypesv2 -> indextypev2 : ['$1'].
+indextypesv2 -> indextypesv2 ',' indextypev2 : ['$3' | '$1'].
+
+indextypev2 -> 'IMPLIED' index : {implied,'$2'}.
+indextypev2 -> index : '$1'.
+
+entry -> objectname : '$1'.
+
+unitspart -> '$empty' : undefined.
+unitspart -> 'UNITS' string : units('$2') .
+
+statusv2 -> atom : statusv2('$1').
+
+accessv2 -> atom: accessv2('$1').
+
+notification -> objectname 'NOTIFICATION-TYPE' objectspart
+ 'STATUS' statusv2 'DESCRIPTION' descriptionfield referpart
+ nameassign :
+ Not = make_notification('$1','$3','$5', '$7', '$8', '$9'),
+ {Not, line_of('$2')}.
+
+objectspart -> 'OBJECTS' '{' objects '}' : lists:reverse('$3').
+objectspart -> '$empty' : [].
+
+objects -> objectname : ['$1'].
+objects -> objects ',' objectname : ['$3'|'$1'].
+
+%%----------------------------------------------------------------------
+Erlang code.
+%%----------------------------------------------------------------------
+
+-include("snmp_types.hrl").
+-include("snmpc_lib.hrl").
+-include("snmpc.hrl").
+
+% value
+val(Token) -> element(3, Token).
+
+line_of(Token) -> element(2, Token).
+
+%% category
+cat(Token) -> element(1, Token).
+
+statusv1(Tok) ->
+ case val(Tok) of
+ mandatory -> mandatory;
+ optional -> optional;
+ obsolete -> obsolete;
+ deprecated -> deprecated;
+ Else -> return_error(line_of(Tok),
+ "syntax error before: " ++ atom_to_list(Else))
+ end.
+
+statusv2(Tok) ->
+ case val(Tok) of
+ current -> current;
+ deprecated -> deprecated;
+ obsolete -> obsolete;
+ Else -> return_error(line_of(Tok),
+ "syntax error before: " ++ atom_to_list(Else))
+ end.
+
+accessv1(Tok) ->
+ case val(Tok) of
+ 'read-only' -> 'read-only';
+ 'read-write' -> 'read-write';
+ 'write-only' -> 'write-only';
+ 'not-accessible' -> 'not-accessible';
+ Else -> return_error(line_of(Tok),
+ "syntax error before: " ++ atom_to_list(Else))
+ end.
+
+accessv2(Tok) ->
+ case val(Tok) of
+ 'not-accessible' -> 'not-accessible';
+ 'accessible-for-notify' -> 'accessible-for-notify';
+ 'read-only' -> 'read-only';
+ 'read-write' -> 'read-write';
+ 'read-create' -> 'read-create';
+ Else -> return_error(line_of(Tok),
+ "syntax error before: " ++ atom_to_list(Else))
+ end.
+
+%% ---------------------------------------------------------------------
+%% Various basic record build functions
+%% ---------------------------------------------------------------------
+
+make_module_identity(Name, LU, Org, CI, Desc, Revs, NA) ->
+ #mc_module_identity{name = Name,
+ last_updated = LU,
+ organization = Org,
+ contact_info = CI,
+ description = Desc,
+ revisions = Revs,
+ name_assign = NA}.
+
+make_revision(Rev, Desc) ->
+ #mc_revision{revision = Rev,
+ description = Desc}.
+
+make_object_type(Name, Syntax, MaxAcc, Status, Desc, Ref, Kind, NA) ->
+ #mc_object_type{name = Name,
+ syntax = Syntax,
+ max_access = MaxAcc,
+ status = Status,
+ description = Desc,
+ reference = Ref,
+ kind = Kind,
+ name_assign = NA}.
+
+make_object_type(Name, Syntax, Units, MaxAcc, Status, Desc, Ref, Kind, NA) ->
+ #mc_object_type{name = Name,
+ syntax = Syntax,
+ units = Units,
+ max_access = MaxAcc,
+ status = Status,
+ description = Desc,
+ reference = Ref,
+ kind = Kind,
+ name_assign = NA}.
+
+make_new_type(Name, Macro, Syntax) ->
+ #mc_new_type{name = Name,
+ macro = Macro,
+ syntax = Syntax}.
+
+make_new_type(Name, Macro, DisplayHint, Status, Desc, Ref, Syntax) ->
+ #mc_new_type{name = Name,
+ macro = Macro,
+ status = Status,
+ description = Desc,
+ reference = Ref,
+ display_hint = DisplayHint,
+ syntax = Syntax}.
+
+make_trap(Name, Ent, Vars, Desc, Ref, Num) ->
+ #mc_trap{name = Name,
+ enterprise = Ent,
+ vars = Vars,
+ description = Desc,
+ reference = Ref,
+ num = Num}.
+
+make_notification(Name, Vars, Status, Desc, Ref, NA) ->
+ #mc_notification{name = Name,
+ vars = Vars,
+ status = Status,
+ description = Desc,
+ reference = Ref,
+ name_assign = NA}.
+
+make_module_compliance(Name, Status, Desc, Ref, Mod, NA) ->
+ #mc_module_compliance{name = Name,
+ status = Status,
+ description = Desc,
+ reference = Ref,
+ module = Mod,
+ name_assign = NA}.
+
+make_object_group(Name, Objs, Status, Desc, Ref, NA) ->
+ #mc_object_group{name = Name,
+ objects = Objs,
+ status = Status,
+ description = Desc,
+ reference = Ref,
+ name_assign = NA}.
+
+make_notification_group(Name, Objs, Status, Desc, Ref, NA) ->
+ #mc_notification_group{name = Name,
+ objects = Objs,
+ status = Status,
+ description = Desc,
+ reference = Ref,
+ name_assign = NA}.
+
+make_sequence(Name, Fields) ->
+ #mc_sequence{name = Name,
+ fields = Fields}.
+
+make_internal(Name, Macro, Parent, SubIdx) ->
+ #mc_internal{name = Name,
+ macro = Macro,
+ parent = Parent,
+ sub_index = SubIdx}.
+
+
+
+%% ---------------------------------------------------------------------
+
+
+%%----------------------------------------------------------------------
+%% Purpose: Find how much room needs to be allocated for the data type
+%% (when sending it in a PDU (the maximum difference will be
+%% the size allocated)).
+%% This is applicable for OCTET STRINGs and OBJECT IDENTIFIERs.
+%%
+%% Or : Find the range of integers in the integer list.
+%% This is applicable for INTEGERs
+%%
+%% Arg: A list of integers.
+%%----------------------------------------------------------------------
+
+make_range_integer(RevHexStr, h) ->
+ erlang:list_to_integer(lists:reverse(RevHexStr), 16);
+make_range_integer(RevHexStr, 'H') ->
+ erlang:list_to_integer(lists:reverse(RevHexStr), 16);
+make_range_integer(RevBitStr, b) ->
+ erlang:list_to_integer(lists:reverse(RevBitStr), 2);
+make_range_integer(RevBitStr, 'B') ->
+ erlang:list_to_integer(lists:reverse(RevBitStr), 2);
+make_range_integer(RevStr, Base) ->
+ throw({error, {invalid_base, Base, lists:reverse(RevStr)}}).
+
+make_range(XIntList) ->
+ IntList = lists:flatten(XIntList),
+ {range, lists:min(IntList), lists:max(IntList)}.
+
+make_defval_for_string(Line, Str, Atom) ->
+ case lists:member(Atom, [h, 'H', b, 'B']) of
+ true ->
+ case catch make_defval_for_string2(Str, Atom) of
+ Defval when is_list(Defval) ->
+ Defval;
+ {error, ErrStr} ->
+ snmpc_lib:print_error("Bad DEFVAL ~w string ~p - ~s",
+ [Atom, Str, ErrStr],
+ Line),
+ "";
+ _Else ->
+ snmpc_lib:print_error("Bad DEFVAL ~w string ~p",
+ [Atom, Str],
+ Line),
+ ""
+ end;
+ false ->
+ snmpc_lib:print_error("Bad DEFVAL string type ~w for ~p",
+ [Atom, Str],
+ Line),
+ ""
+ end.
+
+
+make_defval_for_string2([], h) -> [];
+make_defval_for_string2([X16,X|HexString], h) ->
+ lists:append(hex_to_bytes(snmpc_misc:to_upper([X16,X])),
+ make_defval_for_string2(HexString, h));
+make_defval_for_string2([_Odd], h) ->
+ throw({error, "odd number of bytes in hex string"});
+make_defval_for_string2(HexString, 'H') ->
+ make_defval_for_string2(HexString,h);
+
+make_defval_for_string2(BitString, 'B') ->
+ bits_to_bytes(BitString);
+make_defval_for_string2(BitString, b) ->
+ make_defval_for_string2(BitString, 'B').
+
+bits_to_bytes(BitStr) ->
+ lists:reverse(bits_to_bytes(lists:reverse(BitStr), 1, 0)).
+
+bits_to_bytes([], 1, _Byte) -> % empty bitstring
+ [];
+bits_to_bytes([], 256, _Byte) -> % correct; multiple of 8
+ [];
+% If we are to support arbitrary length of bitstrings. This migth
+% be needed in the new SMI.
+%bits_to_bytes([], N, Byte) ->
+% [Byte];
+bits_to_bytes([], _N, _Byte) ->
+ throw({error, "not a multiple of eight bits in bitstring"});
+bits_to_bytes(Rest, 256, Byte) ->
+ [Byte | bits_to_bytes(Rest, 1, 0)];
+bits_to_bytes([$1 | T], N, Byte) ->
+ bits_to_bytes(T, N*2, N + Byte);
+bits_to_bytes([$0 | T], N, Byte) ->
+ bits_to_bytes(T, N*2, Byte);
+bits_to_bytes([_BadChar | _T], _N, _Byte) ->
+ throw({error, "bad character in bit string"}).
+
+%%----------------------------------------------------------------------
+%% These HEX conversion routines are stolen from module asn1_bits by
+%% I didn't want to ship the entire asn1-compiler so I used cut-and-paste.
+%%----------------------------------------------------------------------
+
+%% hex_to_bytes(HexNumber) when is_atom(HexNumber) ->
+%% hex_to_bytes(atom_to_list(HexNumber));
+
+hex_to_bytes(HexNumber) ->
+ case length(HexNumber) rem 2 of
+ 1 -> %% Odd
+ hex_to_bytes(lists:append(HexNumber,[$0]),[]);
+ 0 -> %% even
+ hex_to_bytes(HexNumber,[])
+ end.
+
+hex_to_bytes([],R) ->
+ lists:reverse(R);
+hex_to_bytes([Hi,Lo|Rest],Res) ->
+ hex_to_bytes(Rest,[hex_to_byte(Hi,Lo)|Res]).
+
+hex_to_four_bits(Hex) ->
+ if
+ Hex == $0 -> 0;
+ Hex == $1 -> 1;
+ Hex == $2 -> 2;
+ Hex == $3 -> 3;
+ Hex == $4 -> 4;
+ Hex == $5 -> 5;
+ Hex == $6 -> 6;
+ Hex == $7 -> 7;
+ Hex == $8 -> 8;
+ Hex == $9 -> 9;
+ Hex == $A -> 10;
+ Hex == $B -> 11;
+ Hex == $C -> 12;
+ Hex == $D -> 13;
+ Hex == $E -> 14;
+ Hex == $F -> 15;
+ true -> throw({error, "bad hex character"})
+ end.
+
+hex_to_byte(Hi,Lo) ->
+ (hex_to_four_bits(Hi) bsl 4) bor hex_to_four_bits(Lo).
+
+kind(DefValPart,IndexPart) ->
+ case DefValPart of
+ undefined ->
+ case IndexPart of
+ {indexes, undefined} -> {variable, []};
+ {indexes, Indexes} ->
+ {table_entry, {indexes, Indexes}};
+ {augments,Table} ->
+ {table_entry,{augments,Table}}
+ end;
+ {defval, DefVal} -> {variable, [{defval, DefVal}]}
+ end.
+
+display_hint(Val) ->
+ case val(Val) of
+ Str when is_list(Str) ->
+ lists:reverse(Str);
+ _ ->
+ throw({error, {invalid_display_hint, Val}})
+ end.
+
+units(Val) ->
+ case val(Val) of
+ Str when is_list(Str) ->
+ lists:reverse(Str);
+ _ ->
+ throw({error, {invalid_units, Val}})
+ end.
+
+ensure_ver(Ver, Line, What) ->
+ case get(snmp_version) of
+ Ver -> ok;
+ _Other ->
+ snmpc_lib:print_error(
+ "~s is only allowed in SNMPv~p.",[What,Ver],Line)
+ end.
+
+
+ensure_ver(Ver,Token) ->
+ ensure_ver(Ver,line_of(Token), atom_to_list(cat(Token))).
+
+filter_v2imports(2,'Integer32') -> {builtin, 'Integer32'};
+filter_v2imports(2,'Counter32') -> {builtin, 'Counter32'};
+filter_v2imports(2,'Gauge32') -> {builtin, 'Gauge32'};
+filter_v2imports(2,'Unsigned32') -> {builtin, 'Unsigned32'};
+filter_v2imports(2,'Counter64') -> {builtin, 'Counter64'};
+filter_v2imports(_,Type) -> {type, Type}.
+
+w(F, A) ->
+ ?vwarning(F, A).
+
+%i(F, A) ->
+% io:format("~w:" ++ F ++ "~n", [?MODULE|A]).
+
diff --git a/lib/snmp/src/compile/snmpc_mib_to_hrl.erl b/lib/snmp/src/compile/snmpc_mib_to_hrl.erl
new file mode 100644
index 0000000000..07bd29231b
--- /dev/null
+++ b/lib/snmp/src/compile/snmpc_mib_to_hrl.erl
@@ -0,0 +1,391 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-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%
+%%
+
+-module(snmpc_mib_to_hrl).
+
+-include_lib("stdlib/include/erl_compile.hrl").
+-include("snmp_types.hrl").
+-include("snmpc_lib.hrl").
+
+%% External exports
+-export([convert/1, compile/3]).
+
+%%-----------------------------------------------------------------
+%% Func: convert/1
+%% Args: MibName = string() without extension.
+%% Purpose: Produce a .hrl file with oid for tables and variables,
+%% column numbers for columns and values for enums.
+%% Writes only the first occurence of a name. Prints a
+%% warning if a duplicate name is found.
+%% Returns: ok | {error, Reason}
+%% Note: The Mib must be compiled.
+%%-----------------------------------------------------------------
+convert(MibName) ->
+ MibFile = MibName ++ ".bin",
+ HrlFile = MibName ++ ".hrl",
+ put(verbosity, trace),
+ convert(MibFile, HrlFile, MibName).
+
+convert(MibFile, HrlFile, MibName) ->
+ ?vtrace("convert -> entry with"
+ "~n MibFile: ~s"
+ "~n HrlFile: ~s"
+ "~n MibName: ~s", [MibFile, HrlFile, MibName]),
+ case snmpc_misc:read_mib(MibFile) of
+ {ok, #mib{asn1_types = Types, mes = MEs, traps = Traps}} ->
+ ?vdebug("mib successfully read", []),
+ resolve(Types, MEs, Traps, HrlFile,
+ filename:basename(MibName)),
+ ok;
+ {error, Reason} ->
+ ?vinfo("failed reading mib: "
+ "~n Reason: ~p", [Reason]),
+ {error, Reason}
+ end.
+
+resolve(Types, MEs, Traps, HrlFile, MibName) ->
+ ?vtrace("resolve -> entry", []),
+ case file:open(HrlFile, [write]) of
+ {ok, Fd} ->
+ insert_header(Fd),
+ insert_begin(Fd, MibName),
+ insert_notifs(Traps, Fd),
+ insert_oids(MEs, Fd),
+ insert_range(MEs, Fd),
+ insert_enums(Types, MEs, Fd),
+ insert_defvals(MEs, Fd),
+ insert_end(Fd),
+ file:close(Fd),
+ ?vlog("~s written", [HrlFile]);
+ {error, Reason} ->
+ ?vinfo("failed opening output file: "
+ "~n Reason: ~p", [Reason]),
+ {error, Reason}
+ end.
+
+insert_header(Fd) ->
+ ?vdebug("insert file header", []),
+ io:format(Fd, "%%% This file was automatically generated by "
+ "snmpc_mib_to_hrl version ~s~n", [?version]),
+ {Y,Mo,D} = date(),
+ {H,Mi,S} = time(),
+ io:format(Fd, "%%% Date: ~2.2.0w-~s-~w::~2.2.0w:~2.2.0w:~2.2.0w~n",
+ [D,month(Mo),Y,H,Mi,S]).
+
+insert_begin(Fd, MibName) ->
+ ?vdebug("insert file begin", []),
+ io:format(Fd,
+ "-ifndef('~s').~n"
+ "-define('~s', true).~n", [MibName, MibName]).
+
+insert_end(Fd) ->
+ ?vdebug("insert file end", []),
+ io:format(Fd, "-endif.~n", []).
+
+insert_oids(MEs, Fd) ->
+ ?vdebug("insert oids", []),
+ io:format(Fd, "~n%% Oids~n", []),
+ insert_oids2(MEs, Fd),
+ io:format(Fd, "~n", []).
+
+insert_oids2([#me{imported = true} | T], Fd) ->
+ insert_oids2(T, Fd);
+insert_oids2([#me{entrytype = table_column, oid = Oid, aliasname = Name} | T],
+ Fd) ->
+ ?vtrace("insert oid [table column]: ~p - ~w", [Name, Oid]),
+ io:format(Fd, "-define(~w, ~w).~n", [Name, lists:last(Oid)]),
+ insert_oids2(T, Fd);
+insert_oids2([#me{entrytype = variable, oid = Oid, aliasname = Name} | T],
+ Fd) ->
+ ?vtrace("insert oid [variable]: ~p - ~w", [Name, Oid]),
+ io:format(Fd, "-define(~w, ~w).~n", [Name, Oid]),
+ io:format(Fd, "-define(~w, ~w).~n", [merge_atoms(Name, instance),
+ Oid ++ [0]]),
+ insert_oids2(T, Fd);
+insert_oids2([#me{oid = Oid, aliasname = Name} | T], Fd) ->
+ ?vtrace("insert oid: ~p - ~w", [Name, Oid]),
+ io:format(Fd, "~n-define(~w, ~w).~n", [Name, Oid]),
+ insert_oids2(T, Fd);
+insert_oids2([], _Fd) ->
+ ok.
+
+
+insert_notifs(Traps, Fd) ->
+ ?vdebug("insert notifications", []),
+ Notifs = [Notif || Notif <- Traps, is_record(Notif, notification)],
+ case Notifs of
+ [] ->
+ ok;
+ _ ->
+ io:format(Fd, "~n%% Notifications~n", []),
+ insert_notifs2(Notifs, Fd)
+ end.
+
+insert_notifs2([], _Fd) ->
+ ok;
+insert_notifs2([#notification{trapname = Name, oid = Oid}|T], Fd) ->
+ ?vtrace("insert notification ~p - ~w", [Name, Oid]),
+ io:format(Fd, "-define(~w, ~w).~n", [Name, Oid]),
+ insert_notifs2(T, Fd).
+
+
+%%-----------------------------------------------------------------
+%% There's nothing strange with this function! Enums can be
+%% defined in types and in mibentries; therefore, we first call
+%% ins_types and then ins_mes to insert enums from different places.
+%%-----------------------------------------------------------------
+insert_enums(Types, MEs, Fd) ->
+ ?vdebug("insert enums", []),
+ T = ins_types(Types, Fd, []),
+ ins_mes(MEs, T, Fd).
+
+%% Insert all types, but not the imported. Ret the names of inserted
+%% types.
+ins_types([#asn1_type{aliasname = Name,
+ assocList = Alist,
+ imported = false} | T],
+ Fd, Res)
+ when is_list(Alist) ->
+ case lists:keysearch(enums, 1, Alist) of
+ {value, {enums, Enums}} when Enums =/= [] ->
+ case Enums of
+ [] -> ins_types(T, Fd, Res);
+ NewEnums ->
+ io:format(Fd, "~n%% Definitions from ~w~n", [Name]),
+ ins_enums(NewEnums, Name, Fd),
+ ins_types(T, Fd, [Name | Res])
+ end;
+ _ -> ins_types(T, Fd, Res)
+ end;
+ins_types([_ | T], Fd, Res) ->
+ ins_types(T, Fd, Res);
+ins_types([], _Fd, Res) -> Res.
+
+ins_mes([#me{entrytype = internal} | T], Types, Fd) ->
+ ins_mes(T, Types, Fd);
+ins_mes([#me{entrytype = table} | T], Types, Fd) ->
+ ins_mes(T, Types, Fd);
+ins_mes([#me{aliasname = Name,
+ asn1_type = #asn1_type{assocList = Alist,
+ aliasname = Aname},
+ imported = false} | T],
+ Types, Fd)
+ when is_list(Alist) ->
+ case lists:keysearch(enums, 1, Alist) of
+ {value, {enums, Enums}} when Enums =/= [] ->
+ case Enums of
+ [] -> ins_mes(T, Types, Fd);
+ NewEnums ->
+ %% Now, check if the type is already inserted
+ %% (by ins_types).
+ case lists:member(Aname, Types) of
+ false ->
+ io:format(Fd, "~n%% Enum definitions from ~w~n",
+ [Name]),
+ ins_enums(NewEnums, Name, Fd),
+ ins_mes(T, Types, Fd);
+ _ -> ins_mes(T, Types, Fd)
+ end
+ end;
+ _ -> ins_mes(T, Types, Fd)
+ end;
+ins_mes([_ | T], Types, Fd) ->
+ ins_mes(T, Types, Fd);
+ins_mes([], _Types, _Fd) -> ok.
+
+ins_enums([{Name, Val} | T], Origin, Fd) ->
+ EnumName = merge_atoms(Origin, Name),
+ io:format(Fd, "-define(~w, ~w).~n", [EnumName, Val]),
+ ins_enums(T, Origin, Fd);
+ins_enums([], _Origin, _Fd) ->
+ ok.
+
+%%----------------------------------------------------------------------
+%% Solves the problem with placing '' around some atoms.
+%% You can't write two atoms using ~w_~w.
+%%----------------------------------------------------------------------
+merge_atoms(TypeOrigin, Name) ->
+ list_to_atom(lists:append([atom_to_list(TypeOrigin), "_",
+ atom_to_list(Name)])).
+
+insert_defvals(Mes, Fd) ->
+ ?vdebug("insert default values", []),
+ io:format(Fd, "~n%% Default values~n", []),
+ insert_defvals2(Mes, Fd),
+ io:format(Fd, "~n", []).
+
+insert_defvals2([#me{imported = true} | T], Fd) ->
+ insert_defvals2(T, Fd);
+insert_defvals2([#me{entrytype = table_column, assocList = Alist,
+ aliasname = Name} | T],
+ Fd) ->
+ case snmpc_misc:assq(defval, Alist) of
+ {value, Val} ->
+ Atom = merge_atoms('default', Name),
+ io:format(Fd, "-define(~w, ~w).~n", [Atom, Val]);
+ _ -> ok
+ end,
+ insert_defvals2(T, Fd);
+insert_defvals2([#me{entrytype = variable, assocList = Alist, aliasname = Name}
+ | T],
+ Fd) ->
+ case snmpc_misc:assq(variable_info, Alist) of
+ {value, VarInfo} ->
+ case VarInfo#variable_info.defval of
+ undefined -> ok;
+ Val ->
+ Atom = merge_atoms('default', Name),
+ io:format(Fd, "-define(~w, ~w).~n", [Atom, Val])
+ end;
+ _ -> ok
+ end,
+ insert_defvals2(T, Fd);
+insert_defvals2([_ | T], Fd) ->
+ insert_defvals2(T, Fd);
+insert_defvals2([], _Fd) -> ok.
+
+insert_range(Mes, Fd) ->
+ ?vdebug("insert range", []),
+ io:format(Fd, "~n%% Range values~n", []),
+ insert_range2(Mes, Fd),
+ io:format(Fd, "~n", []).
+
+insert_range2([#me{imported = true} | T], Fd)->
+ insert_range2(T,Fd);
+insert_range2([#me{asn1_type=#asn1_type{bertype='OCTET STRING',lo=Low,hi=High},aliasname=Name}|T],Fd)->
+ case Low =:= undefined of
+ true->
+ insert_range2(T,Fd);
+ false->
+ AtomLow = merge_atoms('low', Name),
+ AtomHigh = merge_atoms('high', Name),
+ io:format(Fd,"-define(~w, ~w).~n",[AtomLow,Low]),
+ io:format(Fd,"-define(~w, ~w).~n",[AtomHigh,High]),
+ insert_range2(T,Fd)
+ end;
+insert_range2([#me{asn1_type=#asn1_type{bertype='Unsigned32',lo=Low,hi=High},aliasname=Name}|T],Fd)->
+ AtomLow = merge_atoms('low', Name),
+ AtomHigh = merge_atoms('high', Name),
+ io:format(Fd,"-define(~w, ~w).~n",[AtomLow,Low]),
+ io:format(Fd,"-define(~w, ~w).~n",[AtomHigh,High]),
+ insert_range2(T,Fd);
+insert_range2([#me{asn1_type=#asn1_type{bertype='Counter32',lo=Low,hi=High},aliasname=Name}|T],Fd)->
+ AtomLow = merge_atoms('low', Name),
+ AtomHigh = merge_atoms('high', Name),
+ io:format(Fd,"-define(~w, ~w).~n",[AtomLow,Low]),
+ io:format(Fd,"-define(~w, ~w).~n",[AtomHigh,High]),
+ insert_range2(T,Fd);
+insert_range2([#me{asn1_type=#asn1_type{bertype='INTEGER',lo=Low,hi=High},aliasname=Name}|T],Fd)->
+ case Low =:= undefined of
+ true->
+ insert_range2(T,Fd);
+ false->
+ AtomLow = merge_atoms('low', Name),
+ AtomHigh = merge_atoms('high', Name),
+ io:format(Fd,"-define(~w, ~w).~n",[AtomLow,Low]),
+ io:format(Fd,"-define(~w, ~w).~n",[AtomHigh,High]),
+ insert_range2(T,Fd)
+ end;
+insert_range2([_|T],Fd) ->
+ insert_range2(T,Fd);
+insert_range2([],_Fd) ->
+ ok.
+
+month(1) -> "Jan";
+month(2) -> "Feb";
+month(3) -> "Mar";
+month(4) -> "Apr";
+month(5) -> "May";
+month(6) -> "Jun";
+month(7) -> "Jul";
+month(8) -> "Aug";
+month(9) -> "Sep";
+month(10) -> "Oct";
+month(11) -> "Nov";
+month(12) -> "Dec".
+
+%%%-----------------------------------------------------------------
+%%% Interface for erl_compile.
+%%%-----------------------------------------------------------------
+
+%% Opts#options.specific
+compile(Input, Output, Opts) ->
+ set_verbosity(Opts),
+ set_filename(Input),
+ ?vtrace("compile -> entry with"
+ "~n Input: ~s"
+ "~n Output: ~s"
+ "~n Opts: ~p", [Input, Output, Opts]),
+ case convert(Input++".bin", Output++".hrl", Input) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ io:format("~p", [Reason]),
+ error
+ end.
+
+set_verbosity(#options{verbose = Verbose, specific = Spec}) ->
+ set_verbosity(Verbose, Spec).
+
+set_verbosity(Verbose, Spec) ->
+ Verbosity =
+ case lists:keysearch(verbosity, 1, Spec) of
+ {value, {verbosity, V}} ->
+ case (catch snmpc_lib:vvalidate(V)) of
+ ok ->
+ case Verbose of
+ true ->
+ case V of
+ silence ->
+ log;
+ info ->
+ log;
+ _ ->
+ V
+ end;
+ _ ->
+ V
+ end;
+ _ ->
+ case Verbose of
+ true ->
+ log;
+ false ->
+ silence
+ end
+ end;
+ false ->
+ case Verbose of
+ true ->
+ log;
+ false ->
+ silence
+ end
+ end,
+ put(verbosity, Verbosity).
+
+
+set_filename(Filename) ->
+ Rootname = filename:rootname(Filename),
+ Basename = filename:basename(Rootname ++ ".mib"),
+ put(filename, Basename).
+
+
+
+
diff --git a/lib/snmp/src/compile/snmpc_misc.erl b/lib/snmp/src/compile/snmpc_misc.erl
new file mode 100644
index 0000000000..557f3e0f6b
--- /dev/null
+++ b/lib/snmp/src/compile/snmpc_misc.erl
@@ -0,0 +1,173 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2003-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%
+%%
+
+-module(snmpc_misc).
+
+%% need definition of mib record
+-include("snmp_types.hrl").
+-include("snmpc_misc.hrl").
+
+
+-export([assq/2,
+ bits_to_int/2,
+ ensure_trailing_dir_delimiter/1,
+ foreach/3,
+ is_string/1,
+ read_mib/1,
+ read_noexit/2,
+ strip_extension_from_filename/2,
+ to_upper/1]).
+
+
+%%--------------------------------------------------
+%% Not a real assq, but what the heck, it's useful.
+%%--------------------------------------------------
+assq(Key, List) ->
+ case lists:keysearch(Key, 1, List) of
+ {value, {Key, Val}} -> {value, Val};
+ _ -> false
+ end.
+
+
+%%----------------------------------------------------------------------
+%% Converts a list of named bits to the integer value.
+%% Returns: integer()|error
+%%----------------------------------------------------------------------
+bits_to_int(Val,Kibbles) ->
+ bits_to_int(Val,Kibbles,0).
+
+bits_to_int([],_Kibbles,Res) -> Res;
+bits_to_int([Kibble|Ks],Kibbles,Res) ->
+ case snmp_misc:assq(Kibble,Kibbles) of
+ {value,V} ->
+ bits_to_int(Ks,Kibbles,Res + round(math:pow(2,V)));
+ _ ->
+ error
+ end.
+
+
+ensure_trailing_dir_delimiter([]) -> "/";
+ensure_trailing_dir_delimiter(DirSuggestion) ->
+ case lists:last(DirSuggestion) of
+ $/ -> DirSuggestion;
+ _ -> lists:append(DirSuggestion,"/")
+ end.
+
+
+strip_extension_from_filename(FileName, Ext) when is_atom(FileName) ->
+ strip_extension_from_filename(atom_to_list(FileName), Ext);
+
+strip_extension_from_filename(FileName, Ext) when is_list(FileName) ->
+ case lists:suffix(Ext, FileName) of
+ true -> lists:sublist(FileName, 1, length(FileName) - length(Ext));
+ false -> FileName
+ end.
+
+
+to_upper([C|Cs]) when (C >= $a) andalso (C =< $z) -> [C-($a-$A)|to_upper(Cs)];
+to_upper([C|Cs]) -> [C|to_upper(Cs)];
+to_upper([]) -> [].
+
+
+is_string([]) -> true;
+is_string([Tkn | Str])
+ when is_integer(Tkn) andalso (Tkn >= 0) andalso (Tkn =< 255) ->
+ is_string(Str);
+is_string(_) -> false.
+
+
+foreach(Function, ExtraArgs, [H | T]) ->
+ apply(Function, [H | ExtraArgs]),
+ foreach(Function, ExtraArgs, T);
+foreach(_Function, _ExtraArgs, []) ->
+ true.
+
+
+
+%%----------------------------------------------------------------------
+%% Returns: {ok, Mib}|{error, Reason}
+%% The reason for having the function if this module is:
+%% The compiler package and the agent package are separated, this is
+%% the only common module.
+%%----------------------------------------------------------------------
+read_mib(FileName) ->
+ (catch do_read_mib(FileName)).
+
+do_read_mib(FileName) ->
+ ?read_mib(FileName).
+
+
+%% Ret. {ok, Res} | {error, Line, Error} | {error, open_file}
+read_noexit(File, CheckFunc) ->
+ case file:open(File, [read]) of
+ {ok, Fd} ->
+ case loop(Fd, [], CheckFunc, 1, File) of
+ {error, Line, R} ->
+ file:close(Fd),
+ {error, Line, R};
+ Res ->
+ file:close(Fd),
+ {ok, Res}
+ end;
+ _Error ->
+ {error, open_file}
+ end.
+
+
+%%-----------------------------------------------------------------
+%% Ret: {error, Line, Reason} | Row
+%%-----------------------------------------------------------------
+loop(Fd, Res, Func, StartLine, File) ->
+ case read(Fd, "", StartLine) of
+ {ok, Row, EndLine} ->
+ case (catch apply(Func, [Row])) of
+ {ok, NewRow} ->
+ loop(Fd, [NewRow | Res], Func, EndLine, File);
+ true ->
+ loop(Fd, [Row | Res], Func, EndLine, File);
+ Error ->
+ {error, EndLine, Error}
+ end;
+ {error, EndLine, Error} ->
+ {error, EndLine, Error};
+ eof ->
+ Res
+ end.
+
+
+%%-----------------------------------------------------------------
+%% io:read modified to give us line numbers.
+%%-----------------------------------------------------------------
+read(Io, Prompt, StartLine) ->
+ case io:request(Io, {get_until, Prompt, erl_scan, tokens, [StartLine]}) of
+ {ok, Toks, EndLine} ->
+ case erl_parse:parse_term(Toks) of
+ {ok, Term} ->
+ {ok, Term, EndLine};
+ {error, {Line, erl_parse, Error}} ->
+ {error, Line, {parse_error, Error}}
+ end;
+ {error, E, EndLine} ->
+ {error, EndLine, E};
+ {eof, _EndLine} ->
+ eof;
+ Other ->
+ Other
+ end.
+
diff --git a/lib/snmp/src/compile/snmpc_misc.hrl b/lib/snmp/src/compile/snmpc_misc.hrl
new file mode 100644
index 0000000000..c29f2eb9e5
--- /dev/null
+++ b/lib/snmp/src/compile/snmpc_misc.hrl
@@ -0,0 +1,74 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-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%
+%%
+
+-define(verify_format_version(VFV_Ver1,VFV_Ver2),
+ fun(VFV_V,VFV_V) ->
+ ok;
+ (VFV_V1,VFV_V2) when is_list(VFV_V1) andalso is_list(VFV_V2) ->
+ Toks1 = string:tokens(VFV_V1, [$.]),
+ [Major1|_] = case (catch [list_to_integer(I) || I <- Toks1]) of
+ Nums when is_list(Nums) ->
+ Nums;
+ _ ->
+ {error, {invalid_version_format, VFV_V1}}
+ end,
+ Toks2 = string:tokens(VFV_V2, [$.]),
+ case (catch [list_to_integer(I) || I <- Toks2]) of
+ [Major1|_] ->
+ ok;
+ [_Major2|_] ->
+ {error, wrong_version};
+ _ ->
+ {error, {invalid_version_format, VFV_V2}}
+ end;
+ (VFV_V1,VFV_V2) ->
+ {error, {invalid_format, VFV_V1, VFV_V2}}
+ end(VFV_Ver1,VFV_Ver2)).
+
+-define(read_mib(RM_FN),
+ RM_Bin = case file:read_file(RM_FN) of
+ {ok, RM_B} ->
+ RM_B;
+ RM_Error ->
+ throw(RM_Error)
+ end,
+ RM_Mib = case (catch binary_to_term(RM_Bin)) of
+ RM_M when is_record(RM_M, mib) ->
+ RM_M;
+ _ ->
+ throw({error, bad_mib_format})
+ end,
+ #mib{mib_format_version = RM_V1} = #mib{},
+ case RM_Mib of
+ #mib{mib_format_version = RM_V2,
+ misc = RM_X} when is_integer(RM_X) ->
+ case (catch ?verify_format_version(RM_V1, RM_V2)) of
+ ok ->
+ {ok, RM_Mib#mib{misc = []}};
+ _ ->
+ throw({error, {wrong_mib_format_version_tag, RM_V2}})
+ end;
+ #mib{mib_format_version = RM_V2} ->
+ case (catch ?verify_format_version(RM_V1, RM_V2)) of
+ ok ->
+ {ok, RM_Mib#mib{misc = []}};
+ _ ->
+ throw({error, {wrong_mib_format_version_tag, RM_V2}})
+ end
+ end).
diff --git a/lib/snmp/src/compile/snmpc_tok.erl b/lib/snmp/src/compile/snmpc_tok.erl
new file mode 100644
index 0000000000..6b99e7ae43
--- /dev/null
+++ b/lib/snmp/src/compile/snmpc_tok.erl
@@ -0,0 +1,357 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-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%
+%%
+
+-module(snmpc_tok).
+
+%% c(snmpc_tok).
+
+%%----------------------------------------------------------------------
+%% Generic (?) Tokenizer.
+%%----------------------------------------------------------------------
+%% Token: {Category, Line, Value}|{Category, Line}
+%% (Category == <KeyWord> ==> 2-tuple (otherwise 3-tuple)
+%% Category: integer | quote | string
+%% | variable | atom | <keyword> | <single_char>
+%%----------------------------------------------------------------------
+
+%% API
+-export([start_link/2, stop/1, get_token/1, get_all_tokens/1, tokenize/2]).
+
+%% Internal exports
+-export([null_get_line/0, format_error/1, terminate/2, handle_call/3, init/1,
+ test/0]).
+
+
+%%----------------------------------------------------------------------
+%% Reserved_words: list of KeyWords. Example: ['IF', 'BEGIN', ..., 'GOTO']
+%% Options: list of Option
+%% Option: {file, <filename>},
+%% or: {get_line_function, {Mod, Func, Arg} (default io, get_line, [Fid])},
+%% get_line_function shall behave as io:get_line.
+%% {print_lineno, L}
+%% Returns: {ok, Pid} | {error, Reason}
+%%----------------------------------------------------------------------
+start_link(Reserved_words, Options) ->
+ case lists:keysearch(file, 1, Options) of
+ {value, {file, Filename}} ->
+ case file:open(Filename, [read]) of
+ {ok, Fid} ->
+ gen_server:start_link(?MODULE,
+ {Reserved_words, Options,
+ {io, get_line, [Fid, prompt]}}, []);
+ Error ->
+ Str = format_error({"Cannot open file '~s' (~800p).~n",
+ [Filename, Error]}),
+ {error,Str}
+ end;
+ false ->
+ MFA = case lists:keysearch(get_line_function, 1, Options) of
+ {value, {get_line_function, {M, F, A}}} ->
+ {M,F,A};
+ false -> {?MODULE, null_get_line, []}
+ end,
+ gen_server:start_link(?MODULE,{Reserved_words,Options,MFA}, [])
+ end.
+
+%%--------------------------------------------------
+%% Returns:
+%% {ok, [Token], LineNo} | {eof, LineNo} | {error, Error_description, Endline}
+%% For more information, see manual page for yecc (and its requirements on a
+%% tokenizer).
+%%--------------------------------------------------
+get_token(TokPid) ->
+ V = gen_server:call(TokPid, get_token, infinity),
+ %% io:format("tok:~w~n", [V]),
+ V.
+
+get_all_tokens(TokPid) ->
+ V = gen_server:call(TokPid, get_all_tokens, infinity),
+ %% io:format("tok:~w~n", [V]),
+ V.
+
+
+
+%%--------------------------------------------------
+%% Returns: {ok, Tokens, EndLine} | {error, Error_description, Endline}
+%% Comment: Tokeniser must be started since all options reside in
+%% the process dictionary of the tokeniser process.
+%%--------------------------------------------------
+tokenize(TokPid, String) ->
+ gen_server:call(TokPid, {tokenize, String}, infinity).
+
+stop(TokPid) ->
+ gen_server:call(TokPid,stop, infinity).
+
+%%----------------------------------------------------------------------
+%% Implementation
+%%----------------------------------------------------------------------
+insert_keywords_into_ets(_DB, []) -> done;
+insert_keywords_into_ets(DB, [Word | T]) ->
+ ets:insert(DB, {Word, reserved_word}),
+ insert_keywords_into_ets(DB, T).
+
+reserved_word(X) ->
+ case ets:lookup(get(db), X) of
+ [{X, reserved_word}] ->
+ true;
+ _ ->
+ false
+ end.
+
+%% If you only need to tokenize strings
+null_get_line() -> eof.
+
+format_error({Format, Data}) ->
+ io_lib:format(lists:append("Tokeniser error: ", Format), Data).
+
+test() ->
+ start_link(['AUGMENTS','BEGIN','CONTACT-INFO','DEFINITIONS','DEFVAL',
+ 'DESCRIPTION','DISPLAY-HINT','END','IDENTIFIER','IMPLIED',
+ 'INDEX','INTEGER','LAST-UPDATED','MAX-ACCESS','MODULE-IDENTITY',
+ 'NOTIFICATION-TYPE','OBJECT','OBJECT-IDENTITY','OBJECT-TYPE',
+ 'OBJECTS','ORGANIZATION','REFERENCE','REVISION',
+ 'SIZE','STATUS','SYNTAX','TEXTUAL-CONVENTION','UNITS',
+ 'current','deprecated','not-accessible','obsolete',
+ 'read-create','read-only','read-write', 'IMPORTS', 'FROM',
+ 'MODULE-COMPLIANCE',
+ 'DisplayString',
+ 'PhysAddress',
+ 'MacAddress',
+ 'TruthValue',
+ 'TestAndIncr',
+ 'AutonomousType',
+ 'InstancePointer',
+ 'VariablePointer',
+ 'RowPointer',
+ 'RowStatus',
+ 'TimeStamp',
+ 'TimeInterval',
+ 'DateAndTime',
+ 'StorageType',
+ 'TDomain',
+ 'TAddress'],
+ [{file, "modemmib.mib"}]).
+
+init({Reserved_words, Options, GetLineMFA}) ->
+ put(get_line_function,GetLineMFA),
+ put(print_lineno,
+ case lists:keysearch(print_lineno, 1, Options) of
+ {value, {print_lineno, L}} -> L;
+ false -> undefined
+ end),
+ DB = ets:new(reserved_words, [set, private]),
+ insert_keywords_into_ets(DB, Reserved_words),
+ put(db, DB),
+ put(line, 0),
+ {ok, ""}.
+
+getLine() ->
+ OldLine = put(line, 1 + get(line)),
+ {M,F,A} = get(get_line_function),
+ case get(print_lineno) of
+ undefined -> true;
+ X -> case OldLine rem X of
+ 0 -> io:format('~w..',[OldLine]);
+ _ -> true
+ end
+ end,
+ apply(M,F,A).
+
+%% You can only do this when no file is open.
+handle_call({tokenizeString, String}, _From, "") ->
+ {reply, safe_tokenize_whole_string(String), ""};
+
+handle_call(get_token, _From, String) ->
+ {ReplyToken, RestChars} = safe_tokenise(String),
+ {reply, ReplyToken, RestChars};
+
+handle_call(get_all_tokens, _From, String) ->
+ Toks = get_all_tokens(String,[]),
+ {reply, Toks, []};
+
+
+handle_call(stop, _From, String) ->
+ {stop, normal, ok, String}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+%% ErrorInfo = {ErrorLine, Module, ErrorDescriptor}
+%% will be used as
+%% apply(Module, format_error, [ErrorDescriptor]). shall return a string.
+
+%% Returns a reply
+tokenize_whole_string(eof) -> [];
+tokenize_whole_string(String) ->
+ {Token, RestChars} = tokenise(String),
+ [Token | tokenize_whole_string(RestChars)].
+
+safe_tokenize_whole_string(String) ->
+ case catch tokenize_whole_string(String) of
+ {error, ErrorInfo} -> {error, ErrorInfo, get(line)};
+ Tokens -> {ok, Tokens, get(line)}
+ end.
+
+%% throw({error, {get(line), ?MODULE, "Unexpected eof~n"}}).
+
+%% Returns: {ReplyToken, NewState}
+safe_tokenise(eof) -> {{eof, get(line)}, eof};
+safe_tokenise(Chars) when is_list(Chars) ->
+ case catch tokenise(Chars) of
+ {error, ErrorInfo} -> {{error, ErrorInfo, get(line)}, {[], eof}};
+ {Token, RestChars} when is_tuple(Token) ->
+ {{ok, [Token], get(line)}, RestChars}
+ end.
+
+get_all_tokens(eof,Toks) ->
+ lists:reverse(Toks);
+get_all_tokens(Str,Toks) ->
+ case catch tokenise(Str) of
+ {error, ErrorInfo} -> {error, ErrorInfo};
+ {Token, RestChars} when is_tuple(Token) ->
+ get_all_tokens(RestChars, [Token|Toks])
+ end.
+
+
+
+%%--------------------------------------------------
+%% Returns: {Token, Rest}
+%%--------------------------------------------------
+tokenise([H|T]) when ($a =< H) andalso (H =< $z) ->
+ get_name(atom, [H], T);
+
+tokenise([H|T]) when ($A =< H) andalso (H =< $Z) ->
+ get_name(variable, [H], T);
+
+tokenise([$:,$:,$=|T]) ->
+ {{'::=', get(line)}, T};
+
+tokenise([$-,$-|T]) ->
+ tokenise(skip_comment(T));
+
+tokenise([$-,H|T]) when ($0 =< H ) andalso (H =< $9) ->
+ {Val, Rest} = get_integer(T, [H]),
+ {{integer, get(line), -1 * Val}, Rest};
+
+tokenise([H|T]) when ($0 =< H) andalso (H =< $9) ->
+ {Val, Rest} = get_integer(T, [H]),
+ {{integer, get(line), Val}, Rest};
+
+tokenise([$"|T]) ->
+ collect_string($", T, []);
+
+tokenise([$'|T]) ->
+ collect_string($', T, []);
+
+%% Read away white spaces
+tokenise([9| T]) -> tokenise(T);
+tokenise([10| T]) -> tokenise(T);
+tokenise([13| T]) -> tokenise(T);
+tokenise([32| T]) -> tokenise(T);
+
+%% Handle singe characters like { } [ ] + = ...
+tokenise([Ch | T]) ->
+ Atm = list_to_atom([Ch]),
+ {{Atm, get(line)}, T};
+
+tokenise([]) ->
+ tokenise(getLine());
+
+tokenise(eof) ->
+ {{'$end', get(line)}, eof}.
+
+collect_string($", [$"|T],BackwardsStr) ->
+ {{string, get(line), BackwardsStr}, T};
+
+collect_string($', [$'|T],BackwardsStr) ->
+ {{quote, get(line), BackwardsStr}, T};
+
+collect_string(StopChar, [Ch|T], Str) ->
+ collect_string(StopChar,T,[Ch|Str]);
+
+collect_string(StopChar, [],Str) ->
+ collect_string(StopChar, getLine(), Str);
+
+collect_string(StopChar, eof, Str) ->
+ throw({error, {get(line), ?MODULE,
+ {"Missing ~s in string:~n \"~s\"~n",
+ [[StopChar], lists:reverse(Str)]}}}).
+
+get_name(Category, Name, [Char|T]) ->
+ case isInName(Char) of
+ true ->
+ get_name(Category, [Char|Name], T);
+ false ->
+ makeNameRespons(Category, Name, [Char | T])
+ end;
+get_name(Category, Name, []) ->
+ makeNameRespons(Category, Name, []).
+
+makeNameRespons(Category, Name, RestChars) ->
+ Atm = list_to_atom(lists:reverse(Name)),
+ case reserved_word(Atm) of
+ true -> {{Atm, get(line)}, RestChars};
+ false -> {{Category, get(line), Atm}, RestChars}
+ end.
+
+isInName($-) -> true;
+isInName(Ch) -> isalnum(Ch).
+
+isalnum(H) when ($A =< H) andalso (H =< $Z) ->
+ true;
+isalnum(H) when ($a =< H) andalso (H =< $z) ->
+ true;
+isalnum(H) when ($0 =< H) andalso (H =< $9) ->
+ true;
+isalnum(_) ->
+ false.
+
+isdigit(H) when ($0 =< H) andalso (H =< $9) ->
+ true;
+isdigit(_) ->
+ false.
+
+get_integer([H|T], "0") ->
+ case isdigit(H) of
+ true ->
+ throw({error, {get(line), ?MODULE,
+ {"Unexpected ~w~n",
+ [list_to_atom([H])]}}});
+ false ->
+ {0, [H|T]}
+ end;
+get_integer([H|T], L) ->
+ case isdigit(H) of
+ true ->
+ get_integer(T, [H|L]);
+ false ->
+ {list_to_integer(lists:reverse(L)), [H|T]}
+ end;
+get_integer([], L) ->
+ {list_to_integer(lists:reverse(L)), []}.
+
+%%--------------------------------------------------
+%% ASN.1 type of comments. "--" is comment to eoln or next "--"
+%%--------------------------------------------------
+skip_comment([]) ->
+ [];
+skip_comment([$-,$-|T]) ->
+ T;
+skip_comment([_|T]) ->
+ skip_comment(T).