~ubuntu-branches/ubuntu/trusty/ejabberd/trusty-proposed

« back to all changes in this revision

Viewing changes to src/dynamic_compile.erl

  • Committer: Bazaar Package Importer
  • Author(s): Gerfried Fuchs, Konstantin Khomoutov, Gerfried Fuchs
  • Date: 2009-12-04 18:22:49 UTC
  • mfrom: (1.1.11 upstream)
  • Revision ID: james.westby@ubuntu.com-20091204182249-6jfmdz8878h7oaos
Tags: 2.1.0-1
[ Konstantin Khomoutov ]
* New upstream release (Closes: #519858).
  This also adds support for LDAPS upstream (Closes: #526145).
* Do not depend on cdbs anymore, port debian/rules to dh+quilt,
  remove build dependency on patchutils, use erlang-depends.
* Bump debhelper version to 7, standards base to 3.8.3
* Depend on erlang R13B.
* Recommend imagemagick (for captcha support).
* Remove deprecated patches (ssl.patch patch, dynamic_compile_loglevel.patch,
  ldaps.patch, update.patch, proxy.patch, caps.patch, convert.patch,
  s2s.patch).
* Replace mod_ctlextra with mod_admin_extra.
* Use upstream inetrc file.
* Bring debian/ejabberd.cfg and ejabberdctl in sync with upstream.
* Update ejabberdctl manual page.
* Provide NEWS file.
* Rework README.Debian:
  * Group all information into sections.
  * Describe issues with epam binary (Closes: #502791).
  * Discuss how to use DBMS backends (Closes: #540915, #507144).
  * Discuss upgrading from 2.0.x series.
* Implement PID file management (Closes: #519858).
* Make logrotate process all files matching "*.log".
* Improve init script:
  * Make init script LSB-compliant.
  * Implement "live" target which allows to run ejabberd in foreground.
* Make captcha.sh use bash explicitly.
* Rework node-generation for ejabberdctl to fix ejabberd's atom table
  overflows while preserving the possibility to run several versions
  of ejabberdctl concurrently as before.
* Add webadmin patch restoring compatibility with Erlang/OTP <= R12B-4.
* Integrate upstream patch for EJAB-1106.
* Add upstream patch for EJAB-1098.
* Add upstream patch for EJAB-1045.
* Add Konstantin Khomoutov to uploaders.
* Add Japanese debconf translation (thanks to Hideki Yamane)
  (Closes: #558071).

[ Gerfried Fuchs ]
* Build-Depend on po-debconf so po2debconf can be called.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
%% Copyright (c) 2007
 
2
%%          Mats Cronqvist <mats.cronqvist@ericsson.com>
 
3
%%          Chris Newcombe <chris.newcombe@gmail.com> 
 
4
%%          Jacob Vorreuter <jacob.vorreuter@gmail.com>
 
5
%% 
 
6
%% Permission is hereby granted, free of charge, to any person
 
7
%% obtaining a copy of this software and associated documentation
 
8
%% files (the "Software"), to deal in the Software without
 
9
%% restriction, including without limitation the rights to use,
 
10
%% copy, modify, merge, publish, distribute, sublicense, and/or sell
 
11
%% copies of the Software, and to permit persons to whom the
 
12
%% Software is furnished to do so, subject to the following
 
13
%% conditions:
 
14
%% 
 
15
%% The above copyright notice and this permission notice shall be
 
16
%% included in all copies or substantial portions of the Software.
 
17
%% 
 
18
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 
19
%% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 
20
%% OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 
21
%% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 
22
%% HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 
23
%% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 
24
%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 
25
%% OTHER DEALINGS IN THE SOFTWARE.
 
26
 
 
27
%%%-------------------------------------------------------------------
 
28
%%% File : dynamic_compile.erl
 
29
%%% Description :
 
30
%%% Authors : Mats Cronqvist <mats.cronqvist@ericsson.com>
 
31
%%%           Chris Newcombe <chris.newcombe@gmail.com> 
 
32
%%%           Jacob Vorreuter <jacob.vorreuter@gmail.com>
 
33
%%% TODO :
 
34
%%% - add support for limit include-file depth (and prevent circular references)
 
35
%%%   prevent circular macro expansion set FILE correctly when -module() is found
 
36
%%% -include_lib support $ENVVAR in include filenames
 
37
%%%  substitute-stringize (??MACRO)
 
38
%%% -undef/-ifdef/-ifndef/-else/-endif
 
39
%%% -file(File, Line)
 
40
%%%-------------------------------------------------------------------
 
41
-module(dynamic_compile).
 
42
 
 
43
%% API
 
44
-export([from_string/1, from_string/2]).
 
45
 
 
46
-import(lists, [reverse/1, keyreplace/4]).
 
47
 
 
48
%%====================================================================
 
49
%% API
 
50
%%====================================================================
 
51
%%--------------------------------------------------------------------
 
52
%% Function:
 
53
%% Description:
 
54
%%   Returns a binary that can be used with
 
55
%%           code:load_binary(Module, ModuleFilenameForInternalRecords, Binary).
 
56
%%--------------------------------------------------------------------
 
57
from_string(CodeStr) ->
 
58
    from_string(CodeStr, []).
 
59
 
 
60
% takes Options as for compile:forms/2
 
61
from_string(CodeStr, CompileFormsOptions) ->
 
62
    %% Initialise the macro dictionary with the default predefined macros,
 
63
    %% (adapted from epp.erl:predef_macros/1
 
64
    Filename = "compiled_from_string",
 
65
    %%Machine  = list_to_atom(erlang:system_info(machine)),
 
66
    Ms0    = dict:new(),
 
67
    % Ms1    = dict:store('FILE',          {[], "compiled_from_string"}, Ms0),
 
68
    % Ms2    = dict:store('LINE',          {[], 1}, Ms1),  % actually we might add special code for this
 
69
    % Ms3    = dict:store('MODULE',        {[], undefined},              Ms2),
 
70
    % Ms4    = dict:store('MODULE_STRING', {[], undefined},              Ms3),
 
71
    % Ms5    = dict:store('MACHINE',       {[], Machine},                Ms4),
 
72
    % InitMD = dict:store(Machine,         {[], true},                   Ms5),
 
73
    InitMD = Ms0,
 
74
 
 
75
    %% From the docs for compile:forms:
 
76
    %%    When encountering an -include or -include_dir directive, the compiler searches for header files in the following directories:
 
77
    %%      1. ".", the current working directory of the file server;
 
78
    %%      2. the base name of the compiled file;
 
79
    %%      3. the directories specified using the i option. The directory specified last is searched first.
 
80
    %% In this case, #2 is meaningless.
 
81
    IncludeSearchPath = ["." | reverse([Dir || {i, Dir} <- CompileFormsOptions])],
 
82
    {RevForms, _OutMacroDict} = scan_and_parse(CodeStr, Filename, 1, [], InitMD, IncludeSearchPath),
 
83
    Forms = reverse(RevForms),
 
84
 
 
85
    %% note: 'binary' is forced as an implicit option, whether it is provided or not.
 
86
    case compile:forms(Forms, CompileFormsOptions) of
 
87
        {ok, ModuleName, CompiledCodeBinary} when is_binary(CompiledCodeBinary) ->
 
88
            {ModuleName, CompiledCodeBinary};
 
89
        {ok, ModuleName, CompiledCodeBinary, []} when is_binary(CompiledCodeBinary) ->  % empty warnings list
 
90
            {ModuleName, CompiledCodeBinary};
 
91
        {ok, _ModuleName, _CompiledCodeBinary, Warnings} ->
 
92
            throw({?MODULE, warnings, Warnings});
 
93
        Other ->
 
94
            throw({?MODULE, compile_forms, Other})
 
95
    end.
 
96
 
 
97
%%====================================================================
 
98
%% Internal functions
 
99
%%====================================================================
 
100
%%% Code from Mats Cronqvist
 
101
%%% See http://www.erlang.org/pipermail/erlang-questions/2007-March/025507.html
 
102
%%%## 'scan_and_parse'
 
103
%%%
 
104
%%% basically we call the OTP scanner and parser (erl_scan and
 
105
%%% erl_parse) line-by-line, but check each scanned line for (or
 
106
%%% definitions of) macros before parsing.
 
107
%% returns {ReverseForms, FinalMacroDict}
 
108
scan_and_parse([], _CurrFilename, _CurrLine, RevForms, MacroDict, _IncludeSearchPath) ->
 
109
    {RevForms, MacroDict};
 
110
 
 
111
scan_and_parse(RemainingText, CurrFilename, CurrLine, RevForms, MacroDict, IncludeSearchPath) ->
 
112
    case scanner(RemainingText, CurrLine, MacroDict) of
 
113
            {tokens, NLine, NRemainingText, Toks} ->
 
114
                {ok, Form} = erl_parse:parse_form(Toks),
 
115
                scan_and_parse(NRemainingText, CurrFilename, NLine, [Form | RevForms], MacroDict, IncludeSearchPath);
 
116
            {macro, NLine, NRemainingText, NMacroDict} ->
 
117
                scan_and_parse(NRemainingText, CurrFilename, NLine, RevForms,NMacroDict, IncludeSearchPath);
 
118
        {include, NLine, NRemainingText, IncludeFilename} ->
 
119
            IncludeFileRemainingTextents = read_include_file(IncludeFilename, IncludeSearchPath),
 
120
            %%io:format("include file ~p contents: ~n~p~nRemainingText = ~p~n", [IncludeFilename,IncludeFileRemainingTextents, RemainingText]),
 
121
            %% Modify the FILE macro to reflect the filename
 
122
            %%IncludeMacroDict = dict:store('FILE', {[],IncludeFilename}, MacroDict),
 
123
            IncludeMacroDict = MacroDict,
 
124
 
 
125
            %% Process the header file (inc. any nested header files)
 
126
            {RevIncludeForms, IncludedMacroDict} = scan_and_parse(IncludeFileRemainingTextents, IncludeFilename, 1, [], IncludeMacroDict, IncludeSearchPath),
 
127
            %io:format("include file results = ~p~n", [R]),
 
128
            %% Restore the FILE macro in the NEW MacroDict (so we keep any macros defined in the header file)
 
129
            %%NMacroDict = dict:store('FILE', {[],CurrFilename}, IncludedMacroDict),
 
130
            NMacroDict = IncludedMacroDict,
 
131
 
 
132
            %% Continue with the original file
 
133
                scan_and_parse(NRemainingText, CurrFilename, NLine, RevIncludeForms ++ RevForms, NMacroDict, IncludeSearchPath);
 
134
        done ->
 
135
                scan_and_parse([], CurrFilename, CurrLine, RevForms, MacroDict, IncludeSearchPath)
 
136
    end.
 
137
 
 
138
scanner(Text, Line, MacroDict) ->
 
139
    case erl_scan:tokens([],Text,Line) of
 
140
        {done, {ok,Toks,NLine}, LeftOverChars} ->
 
141
            case pre_proc(Toks, MacroDict) of
 
142
                {tokens,  NToks}      -> {tokens,  NLine, LeftOverChars, NToks};
 
143
                {macro,   NMacroDict} -> {macro,   NLine, LeftOverChars, NMacroDict};
 
144
                {include, Filename}   -> {include, NLine, LeftOverChars, Filename}
 
145
            end;
 
146
        {more, _Continuation} ->
 
147
            %% This is supposed to mean "term is not yet complete" (i.e. a '.' has
 
148
            %% not been reached yet).
 
149
            %% However, for some bizarre reason we also get this if there is a comment after the final '.' in a file.
 
150
            %% So we check to see if Text only consists of comments.
 
151
            case is_only_comments(Text) of
 
152
                true  ->
 
153
                    done;
 
154
                false ->
 
155
                    throw({incomplete_term, Text, Line})
 
156
            end
 
157
    end.
 
158
 
 
159
is_only_comments(Text) -> is_only_comments(Text, not_in_comment).
 
160
 
 
161
is_only_comments([],       _)              -> true;
 
162
is_only_comments([$   |T], not_in_comment) -> is_only_comments(T, not_in_comment); % skipping whitspace outside of comment
 
163
is_only_comments([$\t |T], not_in_comment) -> is_only_comments(T, not_in_comment); % skipping whitspace outside of comment
 
164
is_only_comments([$\n |T], not_in_comment) -> is_only_comments(T, not_in_comment); % skipping whitspace outside of comment
 
165
is_only_comments([$%  |T], not_in_comment) -> is_only_comments(T, in_comment);     % found start of a comment
 
166
is_only_comments(_,        not_in_comment) -> false;
 
167
% found any significant char NOT in a comment
 
168
is_only_comments([$\n |T], in_comment)     -> is_only_comments(T, not_in_comment); % found end of a comment
 
169
is_only_comments([_   |T], in_comment)     -> is_only_comments(T, in_comment).     % skipping over in-comment chars
 
170
 
 
171
%%%## 'pre-proc'
 
172
%%%
 
173
%%% have to implement a subset of the pre-processor, since epp insists
 
174
%%% on running on a file.
 
175
%%% only handles 2 cases;
 
176
%% -define(MACRO, something).
 
177
%% -define(MACRO(VAR1,VARN),{stuff,VAR1,more,stuff,VARN,extra,stuff}).
 
178
pre_proc([{'-',_},{atom,_,define},{'(',_},{_,_,Name}|DefToks],MacroDict) ->
 
179
    false = dict:is_key(Name, MacroDict),
 
180
    case DefToks of
 
181
        [{',',_} | Macro] ->
 
182
            {macro, dict:store(Name, {[], macro_body_def(Macro, [])},  MacroDict)};
 
183
        [{'(',_} | Macro] ->
 
184
            {macro, dict:store(Name, macro_params_body_def(Macro, []), MacroDict)}
 
185
    end;
 
186
 
 
187
pre_proc([{'-',_}, {atom,_,include}, {'(',_}, {string,_,Filename}, {')',_}, {dot,_}], _MacroDict) ->
 
188
    {include, Filename};
 
189
 
 
190
pre_proc(Toks,MacroDict) ->
 
191
    {tokens, subst_macros(Toks, MacroDict)}.
 
192
 
 
193
macro_params_body_def([{')',_},{',',_} | Toks], RevParams) ->
 
194
    {reverse(RevParams), macro_body_def(Toks, [])};
 
195
macro_params_body_def([{var,_,Param} | Toks], RevParams) ->
 
196
    macro_params_body_def(Toks, [Param | RevParams]);
 
197
macro_params_body_def([{',',_}, {var,_,Param} | Toks], RevParams) ->
 
198
    macro_params_body_def(Toks, [Param | RevParams]).
 
199
 
 
200
macro_body_def([{')',_}, {dot,_}], RevMacroBodyToks) ->
 
201
    reverse(RevMacroBodyToks);
 
202
macro_body_def([Tok|Toks], RevMacroBodyToks) ->
 
203
    macro_body_def(Toks, [Tok | RevMacroBodyToks]).
 
204
 
 
205
subst_macros(Toks, MacroDict) ->
 
206
    reverse(subst_macros_rev(Toks, MacroDict, [])).
 
207
 
 
208
%% returns a reversed list of tokes
 
209
subst_macros_rev([{'?',_}, {_,LineNum,'LINE'} | Toks], MacroDict, RevOutToks) ->
 
210
    %% special-case for ?LINE, to avoid creating a new MacroDict for every line in the source file
 
211
    subst_macros_rev(Toks, MacroDict, [{integer,LineNum,LineNum}] ++ RevOutToks);
 
212
 
 
213
subst_macros_rev([{'?',_}, {_,_,Name}, {'(',_} = Paren | Toks], MacroDict, RevOutToks) ->
 
214
    case dict:fetch(Name, MacroDict) of
 
215
        {[], MacroValue} ->
 
216
            %% This macro does not have any vars, so ignore the fact that the invocation is followed by "(...stuff"
 
217
            %% Recursively expand any macro calls inside this macro's value
 
218
            %% TODO: avoid infinite expansion due to circular references (even indirect ones)
 
219
            RevExpandedOtherMacrosToks = subst_macros_rev(MacroValue, MacroDict, []),
 
220
            subst_macros_rev([Paren|Toks], MacroDict, RevExpandedOtherMacrosToks ++ RevOutToks);
 
221
        ParamsAndBody ->
 
222
            %% This macro does have vars.
 
223
            %% Collect all of the passe arguments, in an ordered list
 
224
            {NToks, Arguments} = subst_macros_get_args(Toks, []),
 
225
            %% Expand the varibles
 
226
            ExpandedParamsToks = subst_macros_subst_args_for_vars(ParamsAndBody, Arguments),
 
227
            %% Recursively expand any macro calls inside this macro's value
 
228
            %% TODO: avoid infinite expansion due to circular references (even indirect ones)
 
229
            RevExpandedOtherMacrosToks = subst_macros_rev(ExpandedParamsToks, MacroDict, []),
 
230
            subst_macros_rev(NToks, MacroDict, RevExpandedOtherMacrosToks ++ RevOutToks)
 
231
    end;
 
232
 
 
233
subst_macros_rev([{'?',_}, {_,_,Name} | Toks], MacroDict, RevOutToks) ->
 
234
    %% This macro invocation does not have arguments.
 
235
    %% Therefore the definition should not have parameters
 
236
    {[], MacroValue} = dict:fetch(Name, MacroDict),
 
237
 
 
238
    %% Recursively expand any macro calls inside this macro's value
 
239
    %% TODO: avoid infinite expansion due to circular references (even indirect ones)
 
240
    RevExpandedOtherMacrosToks = subst_macros_rev(MacroValue, MacroDict, []),
 
241
    subst_macros_rev(Toks, MacroDict, RevExpandedOtherMacrosToks ++ RevOutToks);
 
242
 
 
243
subst_macros_rev([Tok|Toks], MacroDict,  RevOutToks) ->
 
244
subst_macros_rev(Toks, MacroDict, [Tok|RevOutToks]);
 
245
subst_macros_rev([], _MacroDict, RevOutToks) -> RevOutToks.
 
246
 
 
247
subst_macros_get_args([{')',_} | Toks], RevArgs) ->
 
248
    {Toks, reverse(RevArgs)};
 
249
subst_macros_get_args([{',',_}, {var,_,ArgName} | Toks], RevArgs) ->
 
250
    subst_macros_get_args(Toks, [ArgName| RevArgs]);
 
251
subst_macros_get_args([{var,_,ArgName} | Toks], RevArgs) ->
 
252
    subst_macros_get_args(Toks, [ArgName | RevArgs]).
 
253
 
 
254
subst_macros_subst_args_for_vars({[], BodyToks}, []) ->
 
255
    BodyToks;
 
256
subst_macros_subst_args_for_vars({[Param | Params], BodyToks}, [Arg|Args]) ->
 
257
    NBodyToks = keyreplace(Param, 3, BodyToks, {var,1,Arg}),
 
258
    subst_macros_subst_args_for_vars({Params, NBodyToks}, Args).
 
259
 
 
260
read_include_file(Filename, IncludeSearchPath) ->
 
261
    case file:path_open(IncludeSearchPath, Filename, [read, raw, binary]) of
 
262
        {ok, IoDevice, FullName} ->
 
263
            {ok, Data} = file:read(IoDevice, filelib:file_size(FullName)),
 
264
            file:close(IoDevice),
 
265
            binary_to_list(Data);
 
266
        {error, Reason} ->
 
267
            throw({failed_to_read_include_file, Reason, Filename, IncludeSearchPath})
 
268
    end.
 
 
b'\\ No newline at end of file'