Check-in Differences

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Difference From 53d0a1441eb30b2e To db62a9a83664d28d

2016-01-10
05:39
Add LICENSE check-in: 2c089f2fd6 user: nalck tags: trunk
05:34
First commit check-in: 53d0a1441e user: nalck tags: trunk
04:46
initial empty check-in check-in: db62a9a836 user: nalck tags: trunk

Deleted README.md.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Antigone

Antigone is a simple static site server. All files located in
the `www` directory are matched and served.

## Installation

If [available in Hex](https://hex.pm/docs/publish), the package can be installed as:

  1. Add antigone to your list of dependencies in `mix.exs`:

        def deps do
          [{:antigone, "~> 0.0.1"}]
        end

  2. Ensure antigone is started before your application:

        def application do
          [applications: [:antigone]]
        end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<








































Deleted _build/dev/lib/antigone/ebin/Elixir.Antigone.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/antigone/ebin/antigone.app.

1
2
3
4
5
6
7
{application,antigone,
             [{registered,[]},
              {description,"antigone"},
              {applications,[kernel,stdlib,elixir,logger,cowboy,plug]},
              {mod,{'Elixir.Antigone',[]}},
              {vsn,"0.0.1"},
              {modules,['Elixir.Antigone','Elixir.Router']}]}.
<
<
<
<
<
<
<














Deleted _build/dev/lib/cowboy/ebin/cowboy.app.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{application,cowboy,
             [{description,"Small, fast, modular HTTP server."},
              {vsn,"1.0.4"},
              {id,"git"},
              {modules,[cowboy,cowboy_app,cowboy_bstr,cowboy_clock,
                        cowboy_handler,cowboy_http,cowboy_http_handler,
                        cowboy_loop_handler,cowboy_middleware,cowboy_protocol,
                        cowboy_req,cowboy_rest,cowboy_router,cowboy_spdy,
                        cowboy_static,cowboy_sub_protocol,cowboy_sup,
                        cowboy_websocket,cowboy_websocket_handler]},
              {registered,[cowboy_clock,cowboy_sup]},
              {applications,[kernel,stdlib,ranch,cowlib,crypto]},
              {mod,{cowboy_app,[]}},
              {env,[]}]}.
<
<
<
<
<
<
<
<
<
<
<
<
<
<




























Deleted _build/dev/lib/cowboy/ebin/cowboy.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/cowboy/ebin/cowboy_app.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/cowboy/ebin/cowboy_bstr.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/cowboy/ebin/cowboy_clock.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/cowboy/ebin/cowboy_handler.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/cowboy/ebin/cowboy_http.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/cowboy/ebin/cowboy_http_handler.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/cowboy/ebin/cowboy_loop_handler.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/cowboy/ebin/cowboy_middleware.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/cowboy/ebin/cowboy_protocol.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/cowboy/ebin/cowboy_req.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/cowboy/ebin/cowboy_rest.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/cowboy/ebin/cowboy_router.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/cowboy/ebin/cowboy_spdy.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/cowboy/ebin/cowboy_static.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/cowboy/ebin/cowboy_sub_protocol.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/cowboy/ebin/cowboy_sup.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/cowboy/ebin/cowboy_websocket.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/cowboy/ebin/cowboy_websocket_handler.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/cowlib/ebin/cow_cookie.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/cowlib/ebin/cow_date.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/cowlib/ebin/cow_http.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/cowlib/ebin/cow_http_hd.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/cowlib/ebin/cow_http_te.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/cowlib/ebin/cow_mimetypes.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/cowlib/ebin/cow_multipart.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/cowlib/ebin/cow_qs.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/cowlib/ebin/cow_spdy.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/cowlib/ebin/cowlib.app.

1
2
3
4
5
6
7
8
{application,cowlib,
             [{description,"Support library for manipulating Web protocols."},
              {vsn,"1.0.2"},
              {id,"git"},
              {modules,[cow_cookie,cow_date,cow_http,cow_http_hd,cow_http_te,
                        cow_mimetypes,cow_multipart,cow_qs,cow_spdy]},
              {registered,[]},
              {applications,[kernel,stdlib,crypto]}]}.
<
<
<
<
<
<
<
<
















Deleted _build/dev/lib/cowlib/include/cow_inline.hrl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
%% Copyright (c) 2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-ifndef(COW_INLINE_HRL).
-define(COW_INLINE_HRL, 1).

%% INLINE_LOWERCASE(Function, Rest, Acc, ...)
%%
%% To be included at the end of a case block.
%% Defined for up to 10 extra arguments.

-define(INLINE_LOWERCASE(Function, Rest, Acc),
	$A -> Function(Rest, << Acc/binary, $a >>);
	$B -> Function(Rest, << Acc/binary, $b >>);
	$C -> Function(Rest, << Acc/binary, $c >>);
	$D -> Function(Rest, << Acc/binary, $d >>);
	$E -> Function(Rest, << Acc/binary, $e >>);
	$F -> Function(Rest, << Acc/binary, $f >>);
	$G -> Function(Rest, << Acc/binary, $g >>);
	$H -> Function(Rest, << Acc/binary, $h >>);
	$I -> Function(Rest, << Acc/binary, $i >>);
	$J -> Function(Rest, << Acc/binary, $j >>);
	$K -> Function(Rest, << Acc/binary, $k >>);
	$L -> Function(Rest, << Acc/binary, $l >>);
	$M -> Function(Rest, << Acc/binary, $m >>);
	$N -> Function(Rest, << Acc/binary, $n >>);
	$O -> Function(Rest, << Acc/binary, $o >>);
	$P -> Function(Rest, << Acc/binary, $p >>);
	$Q -> Function(Rest, << Acc/binary, $q >>);
	$R -> Function(Rest, << Acc/binary, $r >>);
	$S -> Function(Rest, << Acc/binary, $s >>);
	$T -> Function(Rest, << Acc/binary, $t >>);
	$U -> Function(Rest, << Acc/binary, $u >>);
	$V -> Function(Rest, << Acc/binary, $v >>);
	$W -> Function(Rest, << Acc/binary, $w >>);
	$X -> Function(Rest, << Acc/binary, $x >>);
	$Y -> Function(Rest, << Acc/binary, $y >>);
	$Z -> Function(Rest, << Acc/binary, $z >>);
	C -> Function(Rest, << Acc/binary, C >>)
).

-define(INLINE_LOWERCASE(Function, Rest, A0, Acc),
	$A -> Function(Rest, A0, << Acc/binary, $a >>);
	$B -> Function(Rest, A0, << Acc/binary, $b >>);
	$C -> Function(Rest, A0, << Acc/binary, $c >>);
	$D -> Function(Rest, A0, << Acc/binary, $d >>);
	$E -> Function(Rest, A0, << Acc/binary, $e >>);
	$F -> Function(Rest, A0, << Acc/binary, $f >>);
	$G -> Function(Rest, A0, << Acc/binary, $g >>);
	$H -> Function(Rest, A0, << Acc/binary, $h >>);
	$I -> Function(Rest, A0, << Acc/binary, $i >>);
	$J -> Function(Rest, A0, << Acc/binary, $j >>);
	$K -> Function(Rest, A0, << Acc/binary, $k >>);
	$L -> Function(Rest, A0, << Acc/binary, $l >>);
	$M -> Function(Rest, A0, << Acc/binary, $m >>);
	$N -> Function(Rest, A0, << Acc/binary, $n >>);
	$O -> Function(Rest, A0, << Acc/binary, $o >>);
	$P -> Function(Rest, A0, << Acc/binary, $p >>);
	$Q -> Function(Rest, A0, << Acc/binary, $q >>);
	$R -> Function(Rest, A0, << Acc/binary, $r >>);
	$S -> Function(Rest, A0, << Acc/binary, $s >>);
	$T -> Function(Rest, A0, << Acc/binary, $t >>);
	$U -> Function(Rest, A0, << Acc/binary, $u >>);
	$V -> Function(Rest, A0, << Acc/binary, $v >>);
	$W -> Function(Rest, A0, << Acc/binary, $w >>);
	$X -> Function(Rest, A0, << Acc/binary, $x >>);
	$Y -> Function(Rest, A0, << Acc/binary, $y >>);
	$Z -> Function(Rest, A0, << Acc/binary, $z >>);
	C -> Function(Rest, A0, << Acc/binary, C >>)
).

-define(INLINE_LOWERCASE(Function, Rest, A0, A1, Acc),
	$A -> Function(Rest, A0, A1, << Acc/binary, $a >>);
	$B -> Function(Rest, A0, A1, << Acc/binary, $b >>);
	$C -> Function(Rest, A0, A1, << Acc/binary, $c >>);
	$D -> Function(Rest, A0, A1, << Acc/binary, $d >>);
	$E -> Function(Rest, A0, A1, << Acc/binary, $e >>);
	$F -> Function(Rest, A0, A1, << Acc/binary, $f >>);
	$G -> Function(Rest, A0, A1, << Acc/binary, $g >>);
	$H -> Function(Rest, A0, A1, << Acc/binary, $h >>);
	$I -> Function(Rest, A0, A1, << Acc/binary, $i >>);
	$J -> Function(Rest, A0, A1, << Acc/binary, $j >>);
	$K -> Function(Rest, A0, A1, << Acc/binary, $k >>);
	$L -> Function(Rest, A0, A1, << Acc/binary, $l >>);
	$M -> Function(Rest, A0, A1, << Acc/binary, $m >>);
	$N -> Function(Rest, A0, A1, << Acc/binary, $n >>);
	$O -> Function(Rest, A0, A1, << Acc/binary, $o >>);
	$P -> Function(Rest, A0, A1, << Acc/binary, $p >>);
	$Q -> Function(Rest, A0, A1, << Acc/binary, $q >>);
	$R -> Function(Rest, A0, A1, << Acc/binary, $r >>);
	$S -> Function(Rest, A0, A1, << Acc/binary, $s >>);
	$T -> Function(Rest, A0, A1, << Acc/binary, $t >>);
	$U -> Function(Rest, A0, A1, << Acc/binary, $u >>);
	$V -> Function(Rest, A0, A1, << Acc/binary, $v >>);
	$W -> Function(Rest, A0, A1, << Acc/binary, $w >>);
	$X -> Function(Rest, A0, A1, << Acc/binary, $x >>);
	$Y -> Function(Rest, A0, A1, << Acc/binary, $y >>);
	$Z -> Function(Rest, A0, A1, << Acc/binary, $z >>);
	C -> Function(Rest, A0, A1, << Acc/binary, C >>)
).

-define(INLINE_LOWERCASE(Function, Rest, A0, A1, A2, Acc),
	$A -> Function(Rest, A0, A1, A2, << Acc/binary, $a >>);
	$B -> Function(Rest, A0, A1, A2, << Acc/binary, $b >>);
	$C -> Function(Rest, A0, A1, A2, << Acc/binary, $c >>);
	$D -> Function(Rest, A0, A1, A2, << Acc/binary, $d >>);
	$E -> Function(Rest, A0, A1, A2, << Acc/binary, $e >>);
	$F -> Function(Rest, A0, A1, A2, << Acc/binary, $f >>);
	$G -> Function(Rest, A0, A1, A2, << Acc/binary, $g >>);
	$H -> Function(Rest, A0, A1, A2, << Acc/binary, $h >>);
	$I -> Function(Rest, A0, A1, A2, << Acc/binary, $i >>);
	$J -> Function(Rest, A0, A1, A2, << Acc/binary, $j >>);
	$K -> Function(Rest, A0, A1, A2, << Acc/binary, $k >>);
	$L -> Function(Rest, A0, A1, A2, << Acc/binary, $l >>);
	$M -> Function(Rest, A0, A1, A2, << Acc/binary, $m >>);
	$N -> Function(Rest, A0, A1, A2, << Acc/binary, $n >>);
	$O -> Function(Rest, A0, A1, A2, << Acc/binary, $o >>);
	$P -> Function(Rest, A0, A1, A2, << Acc/binary, $p >>);
	$Q -> Function(Rest, A0, A1, A2, << Acc/binary, $q >>);
	$R -> Function(Rest, A0, A1, A2, << Acc/binary, $r >>);
	$S -> Function(Rest, A0, A1, A2, << Acc/binary, $s >>);
	$T -> Function(Rest, A0, A1, A2, << Acc/binary, $t >>);
	$U -> Function(Rest, A0, A1, A2, << Acc/binary, $u >>);
	$V -> Function(Rest, A0, A1, A2, << Acc/binary, $v >>);
	$W -> Function(Rest, A0, A1, A2, << Acc/binary, $w >>);
	$X -> Function(Rest, A0, A1, A2, << Acc/binary, $x >>);
	$Y -> Function(Rest, A0, A1, A2, << Acc/binary, $y >>);
	$Z -> Function(Rest, A0, A1, A2, << Acc/binary, $z >>);
	C -> Function(Rest, A0, A1, A2, << Acc/binary, C >>)
).

-define(INLINE_LOWERCASE(Function, Rest, A0, A1, A2, A3, Acc),
	$A -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $a >>);
	$B -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $b >>);
	$C -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $c >>);
	$D -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $d >>);
	$E -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $e >>);
	$F -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $f >>);
	$G -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $g >>);
	$H -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $h >>);
	$I -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $i >>);
	$J -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $j >>);
	$K -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $k >>);
	$L -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $l >>);
	$M -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $m >>);
	$N -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $n >>);
	$O -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $o >>);
	$P -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $p >>);
	$Q -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $q >>);
	$R -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $r >>);
	$S -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $s >>);
	$T -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $t >>);
	$U -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $u >>);
	$V -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $v >>);
	$W -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $w >>);
	$X -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $x >>);
	$Y -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $y >>);
	$Z -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $z >>);
	C -> Function(Rest, A0, A1, A2, A3, << Acc/binary, C >>)
).

-define(INLINE_LOWERCASE(Function, Rest, A0, A1, A2, A3, A4, Acc),
	$A -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $a >>);
	$B -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $b >>);
	$C -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $c >>);
	$D -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $d >>);
	$E -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $e >>);
	$F -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $f >>);
	$G -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $g >>);
	$H -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $h >>);
	$I -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $i >>);
	$J -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $j >>);
	$K -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $k >>);
	$L -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $l >>);
	$M -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $m >>);
	$N -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $n >>);
	$O -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $o >>);
	$P -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $p >>);
	$Q -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $q >>);
	$R -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $r >>);
	$S -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $s >>);
	$T -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $t >>);
	$U -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $u >>);
	$V -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $v >>);
	$W -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $w >>);
	$X -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $x >>);
	$Y -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $y >>);
	$Z -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $z >>);
	C -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, C >>)
).

-define(INLINE_LOWERCASE(Function, Rest, A0, A1, A2, A3, A4, A5, Acc),
	$A -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $a >>);
	$B -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $b >>);
	$C -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $c >>);
	$D -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $d >>);
	$E -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $e >>);
	$F -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $f >>);
	$G -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $g >>);
	$H -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $h >>);
	$I -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $i >>);
	$J -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $j >>);
	$K -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $k >>);
	$L -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $l >>);
	$M -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $m >>);
	$N -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $n >>);
	$O -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $o >>);
	$P -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $p >>);
	$Q -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $q >>);
	$R -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $r >>);
	$S -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $s >>);
	$T -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $t >>);
	$U -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $u >>);
	$V -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $v >>);
	$W -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $w >>);
	$X -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $x >>);
	$Y -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $y >>);
	$Z -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $z >>);
	C -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, C >>)
).

-define(INLINE_LOWERCASE(Function, Rest, A0, A1, A2, A3, A4, A5, A6, Acc),
	$A -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $a >>);
	$B -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $b >>);
	$C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $c >>);
	$D -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $d >>);
	$E -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $e >>);
	$F -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $f >>);
	$G -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $g >>);
	$H -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $h >>);
	$I -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $i >>);
	$J -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $j >>);
	$K -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $k >>);
	$L -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $l >>);
	$M -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $m >>);
	$N -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $n >>);
	$O -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $o >>);
	$P -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $p >>);
	$Q -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $q >>);
	$R -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $r >>);
	$S -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $s >>);
	$T -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $t >>);
	$U -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $u >>);
	$V -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $v >>);
	$W -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $w >>);
	$X -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $x >>);
	$Y -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $y >>);
	$Z -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $z >>);
	C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, C >>)
).

-define(INLINE_LOWERCASE(Function, Rest, A0, A1, A2, A3, A4, A5, A6, A7, Acc),
	$A -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $a >>);
	$B -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $b >>);
	$C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $c >>);
	$D -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $d >>);
	$E -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $e >>);
	$F -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $f >>);
	$G -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $g >>);
	$H -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $h >>);
	$I -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $i >>);
	$J -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $j >>);
	$K -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $k >>);
	$L -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $l >>);
	$M -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $m >>);
	$N -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $n >>);
	$O -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $o >>);
	$P -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $p >>);
	$Q -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $q >>);
	$R -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $r >>);
	$S -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $s >>);
	$T -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $t >>);
	$U -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $u >>);
	$V -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $v >>);
	$W -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $w >>);
	$X -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $x >>);
	$Y -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $y >>);
	$Z -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $z >>);
	C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, C >>)
).

-define(INLINE_LOWERCASE(Function, Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, Acc),
	$A -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $a >>);
	$B -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $b >>);
	$C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $c >>);
	$D -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $d >>);
	$E -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $e >>);
	$F -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $f >>);
	$G -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $g >>);
	$H -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $h >>);
	$I -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $i >>);
	$J -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $j >>);
	$K -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $k >>);
	$L -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $l >>);
	$M -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $m >>);
	$N -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $n >>);
	$O -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $o >>);
	$P -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $p >>);
	$Q -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $q >>);
	$R -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $r >>);
	$S -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $s >>);
	$T -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $t >>);
	$U -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $u >>);
	$V -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $v >>);
	$W -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $w >>);
	$X -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $x >>);
	$Y -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $y >>);
	$Z -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $z >>);
	C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, C >>)
).

-define(INLINE_LOWERCASE(Function, Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, Acc),
	$A -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $a >>);
	$B -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $b >>);
	$C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $c >>);
	$D -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $d >>);
	$E -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $e >>);
	$F -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $f >>);
	$G -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $g >>);
	$H -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $h >>);
	$I -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $i >>);
	$J -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $j >>);
	$K -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $k >>);
	$L -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $l >>);
	$M -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $m >>);
	$N -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $n >>);
	$O -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $o >>);
	$P -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $p >>);
	$Q -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $q >>);
	$R -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $r >>);
	$S -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $s >>);
	$T -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $t >>);
	$U -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $u >>);
	$V -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $v >>);
	$W -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $w >>);
	$X -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $x >>);
	$Y -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $y >>);
	$Z -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $z >>);
	C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, C >>)
).

%% INLINE_LOWERCASE_BC(Bin)
%%
%% Lowercase the entire binary string in a binary comprehension.

-define(INLINE_LOWERCASE_BC(Bin),
	<< << case C of
		$A -> $a;
		$B -> $b;
		$C -> $c;
		$D -> $d;
		$E -> $e;
		$F -> $f;
		$G -> $g;
		$H -> $h;
		$I -> $i;
		$J -> $j;
		$K -> $k;
		$L -> $l;
		$M -> $m;
		$N -> $n;
		$O -> $o;
		$P -> $p;
		$Q -> $q;
		$R -> $r;
		$S -> $s;
		$T -> $t;
		$U -> $u;
		$V -> $v;
		$W -> $w;
		$X -> $x;
		$Y -> $y;
		$Z -> $z;
		C -> C
	end >> || << C >> <= Bin >>).

-endif.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<








































































































































































































































































































































































































































































































































































































































































































































































































Deleted _build/dev/lib/plug/ebin/Elixir.Collectable.Plug.Conn.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Inspect.Plug.Conn.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Adapters.Cowboy.Conn.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Adapters.Cowboy.Handler.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Adapters.Cowboy.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Adapters.Test.Conn.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Adapters.Translator.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Builder.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.CSRFProtection.InvalidCSRFTokenError.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.CSRFProtection.InvalidCrossOriginRequestError.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.CSRFProtection.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Conn.Adapter.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Conn.AlreadySentError.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Conn.Cookies.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Conn.InvalidHeaderError.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Conn.NotSentError.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Conn.Query.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Conn.Status.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Conn.Unfetched.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Conn.Utils.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Conn.WrapperError.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Conn.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Crypto.KeyGenerator.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Crypto.MessageEncryptor.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Crypto.MessageVerifier.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Crypto.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Debugger.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.ErrorHandler.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Exception.Any.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Exception.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.HTML.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Head.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Logger.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.MIME.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.MethodOverride.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Parsers.BadEncodingError.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Parsers.JSON.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Parsers.MULTIPART.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Parsers.ParseError.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Parsers.RequestTooLargeError.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Parsers.URLENCODED.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Parsers.UnsupportedMediaTypeError.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Parsers.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.RequestId.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Router.InvalidSpecError.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Router.Utils.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Router.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.SSL.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Session.COOKIE.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Session.ETS.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Session.Store.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Session.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Static.InvalidPathError.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Static.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Supervisor.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Test.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.Upload.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/Elixir.Plug.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/plug/ebin/plug.app.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
{application,plug,
             [{registered,[]},
              {description,"A specification and conveniences for composable modules between web applications"},
              {applications,[kernel,stdlib,elixir,crypto,logger]},
              {mod,{'Elixir.Plug',[]}},
              {vsn,"1.0.3"},
              {modules,['Elixir.Collectable.Plug.Conn',
                        'Elixir.Inspect.Plug.Conn','Elixir.Plug',
                        'Elixir.Plug.Adapters.Cowboy',
                        'Elixir.Plug.Adapters.Cowboy.Conn',
                        'Elixir.Plug.Adapters.Cowboy.Handler',
                        'Elixir.Plug.Adapters.Test.Conn',
                        'Elixir.Plug.Adapters.Translator',
                        'Elixir.Plug.Builder','Elixir.Plug.CSRFProtection',
                        'Elixir.Plug.CSRFProtection.InvalidCSRFTokenError',
                        'Elixir.Plug.CSRFProtection.InvalidCrossOriginRequestError',
                        'Elixir.Plug.Conn','Elixir.Plug.Conn.Adapter',
                        'Elixir.Plug.Conn.AlreadySentError',
                        'Elixir.Plug.Conn.Cookies',
                        'Elixir.Plug.Conn.InvalidHeaderError',
                        'Elixir.Plug.Conn.NotSentError',
                        'Elixir.Plug.Conn.Query','Elixir.Plug.Conn.Status',
                        'Elixir.Plug.Conn.Unfetched','Elixir.Plug.Conn.Utils',
                        'Elixir.Plug.Conn.WrapperError','Elixir.Plug.Crypto',
                        'Elixir.Plug.Crypto.KeyGenerator',
                        'Elixir.Plug.Crypto.MessageEncryptor',
                        'Elixir.Plug.Crypto.MessageVerifier',
                        'Elixir.Plug.Debugger','Elixir.Plug.ErrorHandler',
                        'Elixir.Plug.Exception','Elixir.Plug.Exception.Any',
                        'Elixir.Plug.HTML','Elixir.Plug.Head',
                        'Elixir.Plug.Logger','Elixir.Plug.MIME',
                        'Elixir.Plug.MethodOverride','Elixir.Plug.Parsers',
                        'Elixir.Plug.Parsers.BadEncodingError',
                        'Elixir.Plug.Parsers.JSON',
                        'Elixir.Plug.Parsers.MULTIPART',
                        'Elixir.Plug.Parsers.ParseError',
                        'Elixir.Plug.Parsers.RequestTooLargeError',
                        'Elixir.Plug.Parsers.URLENCODED',
                        'Elixir.Plug.Parsers.UnsupportedMediaTypeError',
                        'Elixir.Plug.RequestId','Elixir.Plug.Router',
                        'Elixir.Plug.Router.InvalidSpecError',
                        'Elixir.Plug.Router.Utils','Elixir.Plug.SSL',
                        'Elixir.Plug.Session','Elixir.Plug.Session.COOKIE',
                        'Elixir.Plug.Session.ETS','Elixir.Plug.Session.Store',
                        'Elixir.Plug.Static',
                        'Elixir.Plug.Static.InvalidPathError',
                        'Elixir.Plug.Supervisor','Elixir.Plug.Test',
                        'Elixir.Plug.Upload']}]}.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
































































































Deleted _build/dev/lib/ranch/ebin/ranch.app.

1
2
3
4
5
6
7
8
9
10
11
12
{application,ranch,
             [{description,"Socket acceptor pool for TCP protocols."},
              {vsn,"1.2.0"},
              {id,"git"},
              {modules,[ranch,ranch_acceptor,ranch_acceptors_sup,ranch_app,
                        ranch_conns_sup,ranch_listener_sup,ranch_protocol,
                        ranch_server,ranch_ssl,ranch_sup,ranch_tcp,
                        ranch_transport]},
              {registered,[ranch_sup,ranch_server]},
              {applications,[kernel,stdlib]},
              {mod,{ranch_app,[]}},
              {env,[]}]}.
<
<
<
<
<
<
<
<
<
<
<
<
























Deleted _build/dev/lib/ranch/ebin/ranch.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/ranch/ebin/ranch_acceptor.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/ranch/ebin/ranch_acceptors_sup.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/ranch/ebin/ranch_app.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/ranch/ebin/ranch_conns_sup.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/ranch/ebin/ranch_listener_sup.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/ranch/ebin/ranch_protocol.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/ranch/ebin/ranch_server.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/ranch/ebin/ranch_ssl.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/ranch/ebin/ranch_sup.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/ranch/ebin/ranch_tcp.beam.

cannot compute difference between binary files

Deleted _build/dev/lib/ranch/ebin/ranch_transport.beam.

cannot compute difference between binary files

Deleted _build/test/lib/antigone/ebin/Elixir.Antigone.beam.

cannot compute difference between binary files

Deleted _build/test/lib/antigone/ebin/Elixir.Router.beam.

cannot compute difference between binary files

Deleted _build/test/lib/antigone/ebin/antigone.app.

1
2
3
4
5
6
7
{application,antigone,
             [{registered,[]},
              {description,"antigone"},
              {applications,[kernel,stdlib,elixir,logger,cowboy,plug]},
              {mod,{'Elixir.Antigone',[]}},
              {vsn,"0.0.1"},
              {modules,['Elixir.Antigone','Elixir.Router']}]}.
<
<
<
<
<
<
<














Deleted _build/test/lib/cowboy/ebin/cowboy.app.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{application,cowboy,
             [{description,"Small, fast, modular HTTP server."},
              {vsn,"1.0.4"},
              {id,"git"},
              {modules,[cowboy,cowboy_app,cowboy_bstr,cowboy_clock,
                        cowboy_handler,cowboy_http,cowboy_http_handler,
                        cowboy_loop_handler,cowboy_middleware,cowboy_protocol,
                        cowboy_req,cowboy_rest,cowboy_router,cowboy_spdy,
                        cowboy_static,cowboy_sub_protocol,cowboy_sup,
                        cowboy_websocket,cowboy_websocket_handler]},
              {registered,[cowboy_clock,cowboy_sup]},
              {applications,[kernel,stdlib,ranch,cowlib,crypto]},
              {mod,{cowboy_app,[]}},
              {env,[]}]}.
<
<
<
<
<
<
<
<
<
<
<
<
<
<




























Deleted _build/test/lib/cowboy/ebin/cowboy.beam.

cannot compute difference between binary files

Deleted _build/test/lib/cowboy/ebin/cowboy_app.beam.

cannot compute difference between binary files

Deleted _build/test/lib/cowboy/ebin/cowboy_bstr.beam.

cannot compute difference between binary files

Deleted _build/test/lib/cowboy/ebin/cowboy_clock.beam.

cannot compute difference between binary files

Deleted _build/test/lib/cowboy/ebin/cowboy_handler.beam.

cannot compute difference between binary files

Deleted _build/test/lib/cowboy/ebin/cowboy_http.beam.

cannot compute difference between binary files

Deleted _build/test/lib/cowboy/ebin/cowboy_http_handler.beam.

cannot compute difference between binary files

Deleted _build/test/lib/cowboy/ebin/cowboy_loop_handler.beam.

cannot compute difference between binary files

Deleted _build/test/lib/cowboy/ebin/cowboy_middleware.beam.

cannot compute difference between binary files

Deleted _build/test/lib/cowboy/ebin/cowboy_protocol.beam.

cannot compute difference between binary files

Deleted _build/test/lib/cowboy/ebin/cowboy_req.beam.

cannot compute difference between binary files

Deleted _build/test/lib/cowboy/ebin/cowboy_rest.beam.

cannot compute difference between binary files

Deleted _build/test/lib/cowboy/ebin/cowboy_router.beam.

cannot compute difference between binary files

Deleted _build/test/lib/cowboy/ebin/cowboy_spdy.beam.

cannot compute difference between binary files

Deleted _build/test/lib/cowboy/ebin/cowboy_static.beam.

cannot compute difference between binary files

Deleted _build/test/lib/cowboy/ebin/cowboy_sub_protocol.beam.

cannot compute difference between binary files

Deleted _build/test/lib/cowboy/ebin/cowboy_sup.beam.

cannot compute difference between binary files

Deleted _build/test/lib/cowboy/ebin/cowboy_websocket.beam.

cannot compute difference between binary files

Deleted _build/test/lib/cowboy/ebin/cowboy_websocket_handler.beam.

cannot compute difference between binary files

Deleted _build/test/lib/cowlib/ebin/cow_cookie.beam.

cannot compute difference between binary files

Deleted _build/test/lib/cowlib/ebin/cow_date.beam.

cannot compute difference between binary files

Deleted _build/test/lib/cowlib/ebin/cow_http.beam.

cannot compute difference between binary files

Deleted _build/test/lib/cowlib/ebin/cow_http_hd.beam.

cannot compute difference between binary files

Deleted _build/test/lib/cowlib/ebin/cow_http_te.beam.

cannot compute difference between binary files

Deleted _build/test/lib/cowlib/ebin/cow_mimetypes.beam.

cannot compute difference between binary files

Deleted _build/test/lib/cowlib/ebin/cow_multipart.beam.

cannot compute difference between binary files

Deleted _build/test/lib/cowlib/ebin/cow_qs.beam.

cannot compute difference between binary files

Deleted _build/test/lib/cowlib/ebin/cow_spdy.beam.

cannot compute difference between binary files

Deleted _build/test/lib/cowlib/ebin/cowlib.app.

1
2
3
4
5
6
7
8
{application,cowlib,
             [{description,"Support library for manipulating Web protocols."},
              {vsn,"1.0.2"},
              {id,"git"},
              {modules,[cow_cookie,cow_date,cow_http,cow_http_hd,cow_http_te,
                        cow_mimetypes,cow_multipart,cow_qs,cow_spdy]},
              {registered,[]},
              {applications,[kernel,stdlib,crypto]}]}.
<
<
<
<
<
<
<
<
















Deleted _build/test/lib/cowlib/include/cow_inline.hrl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
%% Copyright (c) 2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-ifndef(COW_INLINE_HRL).
-define(COW_INLINE_HRL, 1).

%% INLINE_LOWERCASE(Function, Rest, Acc, ...)
%%
%% To be included at the end of a case block.
%% Defined for up to 10 extra arguments.

-define(INLINE_LOWERCASE(Function, Rest, Acc),
	$A -> Function(Rest, << Acc/binary, $a >>);
	$B -> Function(Rest, << Acc/binary, $b >>);
	$C -> Function(Rest, << Acc/binary, $c >>);
	$D -> Function(Rest, << Acc/binary, $d >>);
	$E -> Function(Rest, << Acc/binary, $e >>);
	$F -> Function(Rest, << Acc/binary, $f >>);
	$G -> Function(Rest, << Acc/binary, $g >>);
	$H -> Function(Rest, << Acc/binary, $h >>);
	$I -> Function(Rest, << Acc/binary, $i >>);
	$J -> Function(Rest, << Acc/binary, $j >>);
	$K -> Function(Rest, << Acc/binary, $k >>);
	$L -> Function(Rest, << Acc/binary, $l >>);
	$M -> Function(Rest, << Acc/binary, $m >>);
	$N -> Function(Rest, << Acc/binary, $n >>);
	$O -> Function(Rest, << Acc/binary, $o >>);
	$P -> Function(Rest, << Acc/binary, $p >>);
	$Q -> Function(Rest, << Acc/binary, $q >>);
	$R -> Function(Rest, << Acc/binary, $r >>);
	$S -> Function(Rest, << Acc/binary, $s >>);
	$T -> Function(Rest, << Acc/binary, $t >>);
	$U -> Function(Rest, << Acc/binary, $u >>);
	$V -> Function(Rest, << Acc/binary, $v >>);
	$W -> Function(Rest, << Acc/binary, $w >>);
	$X -> Function(Rest, << Acc/binary, $x >>);
	$Y -> Function(Rest, << Acc/binary, $y >>);
	$Z -> Function(Rest, << Acc/binary, $z >>);
	C -> Function(Rest, << Acc/binary, C >>)
).

-define(INLINE_LOWERCASE(Function, Rest, A0, Acc),
	$A -> Function(Rest, A0, << Acc/binary, $a >>);
	$B -> Function(Rest, A0, << Acc/binary, $b >>);
	$C -> Function(Rest, A0, << Acc/binary, $c >>);
	$D -> Function(Rest, A0, << Acc/binary, $d >>);
	$E -> Function(Rest, A0, << Acc/binary, $e >>);
	$F -> Function(Rest, A0, << Acc/binary, $f >>);
	$G -> Function(Rest, A0, << Acc/binary, $g >>);
	$H -> Function(Rest, A0, << Acc/binary, $h >>);
	$I -> Function(Rest, A0, << Acc/binary, $i >>);
	$J -> Function(Rest, A0, << Acc/binary, $j >>);
	$K -> Function(Rest, A0, << Acc/binary, $k >>);
	$L -> Function(Rest, A0, << Acc/binary, $l >>);
	$M -> Function(Rest, A0, << Acc/binary, $m >>);
	$N -> Function(Rest, A0, << Acc/binary, $n >>);
	$O -> Function(Rest, A0, << Acc/binary, $o >>);
	$P -> Function(Rest, A0, << Acc/binary, $p >>);
	$Q -> Function(Rest, A0, << Acc/binary, $q >>);
	$R -> Function(Rest, A0, << Acc/binary, $r >>);
	$S -> Function(Rest, A0, << Acc/binary, $s >>);
	$T -> Function(Rest, A0, << Acc/binary, $t >>);
	$U -> Function(Rest, A0, << Acc/binary, $u >>);
	$V -> Function(Rest, A0, << Acc/binary, $v >>);
	$W -> Function(Rest, A0, << Acc/binary, $w >>);
	$X -> Function(Rest, A0, << Acc/binary, $x >>);
	$Y -> Function(Rest, A0, << Acc/binary, $y >>);
	$Z -> Function(Rest, A0, << Acc/binary, $z >>);
	C -> Function(Rest, A0, << Acc/binary, C >>)
).

-define(INLINE_LOWERCASE(Function, Rest, A0, A1, Acc),
	$A -> Function(Rest, A0, A1, << Acc/binary, $a >>);
	$B -> Function(Rest, A0, A1, << Acc/binary, $b >>);
	$C -> Function(Rest, A0, A1, << Acc/binary, $c >>);
	$D -> Function(Rest, A0, A1, << Acc/binary, $d >>);
	$E -> Function(Rest, A0, A1, << Acc/binary, $e >>);
	$F -> Function(Rest, A0, A1, << Acc/binary, $f >>);
	$G -> Function(Rest, A0, A1, << Acc/binary, $g >>);
	$H -> Function(Rest, A0, A1, << Acc/binary, $h >>);
	$I -> Function(Rest, A0, A1, << Acc/binary, $i >>);
	$J -> Function(Rest, A0, A1, << Acc/binary, $j >>);
	$K -> Function(Rest, A0, A1, << Acc/binary, $k >>);
	$L -> Function(Rest, A0, A1, << Acc/binary, $l >>);
	$M -> Function(Rest, A0, A1, << Acc/binary, $m >>);
	$N -> Function(Rest, A0, A1, << Acc/binary, $n >>);
	$O -> Function(Rest, A0, A1, << Acc/binary, $o >>);
	$P -> Function(Rest, A0, A1, << Acc/binary, $p >>);
	$Q -> Function(Rest, A0, A1, << Acc/binary, $q >>);
	$R -> Function(Rest, A0, A1, << Acc/binary, $r >>);
	$S -> Function(Rest, A0, A1, << Acc/binary, $s >>);
	$T -> Function(Rest, A0, A1, << Acc/binary, $t >>);
	$U -> Function(Rest, A0, A1, << Acc/binary, $u >>);
	$V -> Function(Rest, A0, A1, << Acc/binary, $v >>);
	$W -> Function(Rest, A0, A1, << Acc/binary, $w >>);
	$X -> Function(Rest, A0, A1, << Acc/binary, $x >>);
	$Y -> Function(Rest, A0, A1, << Acc/binary, $y >>);
	$Z -> Function(Rest, A0, A1, << Acc/binary, $z >>);
	C -> Function(Rest, A0, A1, << Acc/binary, C >>)
).

-define(INLINE_LOWERCASE(Function, Rest, A0, A1, A2, Acc),
	$A -> Function(Rest, A0, A1, A2, << Acc/binary, $a >>);
	$B -> Function(Rest, A0, A1, A2, << Acc/binary, $b >>);
	$C -> Function(Rest, A0, A1, A2, << Acc/binary, $c >>);
	$D -> Function(Rest, A0, A1, A2, << Acc/binary, $d >>);
	$E -> Function(Rest, A0, A1, A2, << Acc/binary, $e >>);
	$F -> Function(Rest, A0, A1, A2, << Acc/binary, $f >>);
	$G -> Function(Rest, A0, A1, A2, << Acc/binary, $g >>);
	$H -> Function(Rest, A0, A1, A2, << Acc/binary, $h >>);
	$I -> Function(Rest, A0, A1, A2, << Acc/binary, $i >>);
	$J -> Function(Rest, A0, A1, A2, << Acc/binary, $j >>);
	$K -> Function(Rest, A0, A1, A2, << Acc/binary, $k >>);
	$L -> Function(Rest, A0, A1, A2, << Acc/binary, $l >>);
	$M -> Function(Rest, A0, A1, A2, << Acc/binary, $m >>);
	$N -> Function(Rest, A0, A1, A2, << Acc/binary, $n >>);
	$O -> Function(Rest, A0, A1, A2, << Acc/binary, $o >>);
	$P -> Function(Rest, A0, A1, A2, << Acc/binary, $p >>);
	$Q -> Function(Rest, A0, A1, A2, << Acc/binary, $q >>);
	$R -> Function(Rest, A0, A1, A2, << Acc/binary, $r >>);
	$S -> Function(Rest, A0, A1, A2, << Acc/binary, $s >>);
	$T -> Function(Rest, A0, A1, A2, << Acc/binary, $t >>);
	$U -> Function(Rest, A0, A1, A2, << Acc/binary, $u >>);
	$V -> Function(Rest, A0, A1, A2, << Acc/binary, $v >>);
	$W -> Function(Rest, A0, A1, A2, << Acc/binary, $w >>);
	$X -> Function(Rest, A0, A1, A2, << Acc/binary, $x >>);
	$Y -> Function(Rest, A0, A1, A2, << Acc/binary, $y >>);
	$Z -> Function(Rest, A0, A1, A2, << Acc/binary, $z >>);
	C -> Function(Rest, A0, A1, A2, << Acc/binary, C >>)
).

-define(INLINE_LOWERCASE(Function, Rest, A0, A1, A2, A3, Acc),
	$A -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $a >>);
	$B -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $b >>);
	$C -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $c >>);
	$D -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $d >>);
	$E -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $e >>);
	$F -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $f >>);
	$G -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $g >>);
	$H -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $h >>);
	$I -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $i >>);
	$J -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $j >>);
	$K -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $k >>);
	$L -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $l >>);
	$M -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $m >>);
	$N -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $n >>);
	$O -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $o >>);
	$P -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $p >>);
	$Q -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $q >>);
	$R -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $r >>);
	$S -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $s >>);
	$T -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $t >>);
	$U -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $u >>);
	$V -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $v >>);
	$W -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $w >>);
	$X -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $x >>);
	$Y -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $y >>);
	$Z -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $z >>);
	C -> Function(Rest, A0, A1, A2, A3, << Acc/binary, C >>)
).

-define(INLINE_LOWERCASE(Function, Rest, A0, A1, A2, A3, A4, Acc),
	$A -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $a >>);
	$B -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $b >>);
	$C -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $c >>);
	$D -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $d >>);
	$E -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $e >>);
	$F -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $f >>);
	$G -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $g >>);
	$H -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $h >>);
	$I -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $i >>);
	$J -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $j >>);
	$K -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $k >>);
	$L -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $l >>);
	$M -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $m >>);
	$N -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $n >>);
	$O -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $o >>);
	$P -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $p >>);
	$Q -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $q >>);
	$R -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $r >>);
	$S -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $s >>);
	$T -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $t >>);
	$U -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $u >>);
	$V -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $v >>);
	$W -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $w >>);
	$X -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $x >>);
	$Y -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $y >>);
	$Z -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $z >>);
	C -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, C >>)
).

-define(INLINE_LOWERCASE(Function, Rest, A0, A1, A2, A3, A4, A5, Acc),
	$A -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $a >>);
	$B -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $b >>);
	$C -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $c >>);
	$D -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $d >>);
	$E -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $e >>);
	$F -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $f >>);
	$G -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $g >>);
	$H -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $h >>);
	$I -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $i >>);
	$J -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $j >>);
	$K -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $k >>);
	$L -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $l >>);
	$M -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $m >>);
	$N -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $n >>);
	$O -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $o >>);
	$P -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $p >>);
	$Q -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $q >>);
	$R -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $r >>);
	$S -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $s >>);
	$T -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $t >>);
	$U -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $u >>);
	$V -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $v >>);
	$W -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $w >>);
	$X -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $x >>);
	$Y -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $y >>);
	$Z -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $z >>);
	C -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, C >>)
).

-define(INLINE_LOWERCASE(Function, Rest, A0, A1, A2, A3, A4, A5, A6, Acc),
	$A -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $a >>);
	$B -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $b >>);
	$C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $c >>);
	$D -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $d >>);
	$E -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $e >>);
	$F -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $f >>);
	$G -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $g >>);
	$H -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $h >>);
	$I -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $i >>);
	$J -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $j >>);
	$K -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $k >>);
	$L -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $l >>);
	$M -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $m >>);
	$N -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $n >>);
	$O -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $o >>);
	$P -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $p >>);
	$Q -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $q >>);
	$R -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $r >>);
	$S -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $s >>);
	$T -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $t >>);
	$U -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $u >>);
	$V -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $v >>);
	$W -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $w >>);
	$X -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $x >>);
	$Y -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $y >>);
	$Z -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $z >>);
	C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, C >>)
).

-define(INLINE_LOWERCASE(Function, Rest, A0, A1, A2, A3, A4, A5, A6, A7, Acc),
	$A -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $a >>);
	$B -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $b >>);
	$C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $c >>);
	$D -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $d >>);
	$E -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $e >>);
	$F -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $f >>);
	$G -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $g >>);
	$H -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $h >>);
	$I -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $i >>);
	$J -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $j >>);
	$K -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $k >>);
	$L -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $l >>);
	$M -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $m >>);
	$N -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $n >>);
	$O -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $o >>);
	$P -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $p >>);
	$Q -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $q >>);
	$R -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $r >>);
	$S -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $s >>);
	$T -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $t >>);
	$U -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $u >>);
	$V -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $v >>);
	$W -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $w >>);
	$X -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $x >>);
	$Y -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $y >>);
	$Z -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $z >>);
	C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, C >>)
).

-define(INLINE_LOWERCASE(Function, Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, Acc),
	$A -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $a >>);
	$B -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $b >>);
	$C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $c >>);
	$D -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $d >>);
	$E -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $e >>);
	$F -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $f >>);
	$G -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $g >>);
	$H -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $h >>);
	$I -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $i >>);
	$J -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $j >>);
	$K -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $k >>);
	$L -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $l >>);
	$M -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $m >>);
	$N -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $n >>);
	$O -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $o >>);
	$P -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $p >>);
	$Q -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $q >>);
	$R -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $r >>);
	$S -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $s >>);
	$T -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $t >>);
	$U -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $u >>);
	$V -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $v >>);
	$W -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $w >>);
	$X -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $x >>);
	$Y -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $y >>);
	$Z -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $z >>);
	C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, C >>)
).

-define(INLINE_LOWERCASE(Function, Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, Acc),
	$A -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $a >>);
	$B -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $b >>);
	$C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $c >>);
	$D -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $d >>);
	$E -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $e >>);
	$F -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $f >>);
	$G -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $g >>);
	$H -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $h >>);
	$I -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $i >>);
	$J -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $j >>);
	$K -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $k >>);
	$L -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $l >>);
	$M -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $m >>);
	$N -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $n >>);
	$O -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $o >>);
	$P -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $p >>);
	$Q -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $q >>);
	$R -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $r >>);
	$S -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $s >>);
	$T -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $t >>);
	$U -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $u >>);
	$V -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $v >>);
	$W -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $w >>);
	$X -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $x >>);
	$Y -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $y >>);
	$Z -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $z >>);
	C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, C >>)
).

%% INLINE_LOWERCASE_BC(Bin)
%%
%% Lowercase the entire binary string in a binary comprehension.

-define(INLINE_LOWERCASE_BC(Bin),
	<< << case C of
		$A -> $a;
		$B -> $b;
		$C -> $c;
		$D -> $d;
		$E -> $e;
		$F -> $f;
		$G -> $g;
		$H -> $h;
		$I -> $i;
		$J -> $j;
		$K -> $k;
		$L -> $l;
		$M -> $m;
		$N -> $n;
		$O -> $o;
		$P -> $p;
		$Q -> $q;
		$R -> $r;
		$S -> $s;
		$T -> $t;
		$U -> $u;
		$V -> $v;
		$W -> $w;
		$X -> $x;
		$Y -> $y;
		$Z -> $z;
		C -> C
	end >> || << C >> <= Bin >>).

-endif.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<








































































































































































































































































































































































































































































































































































































































































































































































































Deleted _build/test/lib/plug/ebin/Elixir.Collectable.Plug.Conn.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Inspect.Plug.Conn.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Adapters.Cowboy.Conn.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Adapters.Cowboy.Handler.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Adapters.Cowboy.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Adapters.Test.Conn.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Adapters.Translator.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Builder.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.CSRFProtection.InvalidCSRFTokenError.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.CSRFProtection.InvalidCrossOriginRequestError.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.CSRFProtection.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Conn.Adapter.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Conn.AlreadySentError.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Conn.Cookies.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Conn.InvalidHeaderError.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Conn.NotSentError.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Conn.Query.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Conn.Status.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Conn.Unfetched.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Conn.Utils.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Conn.WrapperError.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Conn.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Crypto.KeyGenerator.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Crypto.MessageEncryptor.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Crypto.MessageVerifier.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Crypto.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Debugger.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.ErrorHandler.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Exception.Any.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Exception.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.HTML.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Head.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Logger.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.MIME.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.MethodOverride.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Parsers.BadEncodingError.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Parsers.JSON.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Parsers.MULTIPART.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Parsers.ParseError.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Parsers.RequestTooLargeError.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Parsers.URLENCODED.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Parsers.UnsupportedMediaTypeError.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Parsers.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.RequestId.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Router.InvalidSpecError.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Router.Utils.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Router.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.SSL.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Session.COOKIE.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Session.ETS.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Session.Store.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Session.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Static.InvalidPathError.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Static.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Supervisor.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Test.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.Upload.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/Elixir.Plug.beam.

cannot compute difference between binary files

Deleted _build/test/lib/plug/ebin/plug.app.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
{application,plug,
             [{registered,[]},
              {description,"A specification and conveniences for composable modules between web applications"},
              {applications,[kernel,stdlib,elixir,crypto,logger]},
              {mod,{'Elixir.Plug',[]}},
              {vsn,"1.0.3"},
              {modules,['Elixir.Collectable.Plug.Conn',
                        'Elixir.Inspect.Plug.Conn','Elixir.Plug',
                        'Elixir.Plug.Adapters.Cowboy',
                        'Elixir.Plug.Adapters.Cowboy.Conn',
                        'Elixir.Plug.Adapters.Cowboy.Handler',
                        'Elixir.Plug.Adapters.Test.Conn',
                        'Elixir.Plug.Adapters.Translator',
                        'Elixir.Plug.Builder','Elixir.Plug.CSRFProtection',
                        'Elixir.Plug.CSRFProtection.InvalidCSRFTokenError',
                        'Elixir.Plug.CSRFProtection.InvalidCrossOriginRequestError',
                        'Elixir.Plug.Conn','Elixir.Plug.Conn.Adapter',
                        'Elixir.Plug.Conn.AlreadySentError',
                        'Elixir.Plug.Conn.Cookies',
                        'Elixir.Plug.Conn.InvalidHeaderError',
                        'Elixir.Plug.Conn.NotSentError',
                        'Elixir.Plug.Conn.Query','Elixir.Plug.Conn.Status',
                        'Elixir.Plug.Conn.Unfetched','Elixir.Plug.Conn.Utils',
                        'Elixir.Plug.Conn.WrapperError','Elixir.Plug.Crypto',
                        'Elixir.Plug.Crypto.KeyGenerator',
                        'Elixir.Plug.Crypto.MessageEncryptor',
                        'Elixir.Plug.Crypto.MessageVerifier',
                        'Elixir.Plug.Debugger','Elixir.Plug.ErrorHandler',
                        'Elixir.Plug.Exception','Elixir.Plug.Exception.Any',
                        'Elixir.Plug.HTML','Elixir.Plug.Head',
                        'Elixir.Plug.Logger','Elixir.Plug.MIME',
                        'Elixir.Plug.MethodOverride','Elixir.Plug.Parsers',
                        'Elixir.Plug.Parsers.BadEncodingError',
                        'Elixir.Plug.Parsers.JSON',
                        'Elixir.Plug.Parsers.MULTIPART',
                        'Elixir.Plug.Parsers.ParseError',
                        'Elixir.Plug.Parsers.RequestTooLargeError',
                        'Elixir.Plug.Parsers.URLENCODED',
                        'Elixir.Plug.Parsers.UnsupportedMediaTypeError',
                        'Elixir.Plug.RequestId','Elixir.Plug.Router',
                        'Elixir.Plug.Router.InvalidSpecError',
                        'Elixir.Plug.Router.Utils','Elixir.Plug.SSL',
                        'Elixir.Plug.Session','Elixir.Plug.Session.COOKIE',
                        'Elixir.Plug.Session.ETS','Elixir.Plug.Session.Store',
                        'Elixir.Plug.Static',
                        'Elixir.Plug.Static.InvalidPathError',
                        'Elixir.Plug.Supervisor','Elixir.Plug.Test',
                        'Elixir.Plug.Upload']}]}.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
































































































Deleted _build/test/lib/ranch/ebin/ranch.app.

1
2
3
4
5
6
7
8
9
10
11
12
{application,ranch,
             [{description,"Socket acceptor pool for TCP protocols."},
              {vsn,"1.2.0"},
              {id,"git"},
              {modules,[ranch,ranch_acceptor,ranch_acceptors_sup,ranch_app,
                        ranch_conns_sup,ranch_listener_sup,ranch_protocol,
                        ranch_server,ranch_ssl,ranch_sup,ranch_tcp,
                        ranch_transport]},
              {registered,[ranch_sup,ranch_server]},
              {applications,[kernel,stdlib]},
              {mod,{ranch_app,[]}},
              {env,[]}]}.
<
<
<
<
<
<
<
<
<
<
<
<
























Deleted _build/test/lib/ranch/ebin/ranch.beam.

cannot compute difference between binary files

Deleted _build/test/lib/ranch/ebin/ranch_acceptor.beam.

cannot compute difference between binary files

Deleted _build/test/lib/ranch/ebin/ranch_acceptors_sup.beam.

cannot compute difference between binary files

Deleted _build/test/lib/ranch/ebin/ranch_app.beam.

cannot compute difference between binary files

Deleted _build/test/lib/ranch/ebin/ranch_conns_sup.beam.

cannot compute difference between binary files

Deleted _build/test/lib/ranch/ebin/ranch_listener_sup.beam.

cannot compute difference between binary files

Deleted _build/test/lib/ranch/ebin/ranch_protocol.beam.

cannot compute difference between binary files

Deleted _build/test/lib/ranch/ebin/ranch_server.beam.

cannot compute difference between binary files

Deleted _build/test/lib/ranch/ebin/ranch_ssl.beam.

cannot compute difference between binary files

Deleted _build/test/lib/ranch/ebin/ranch_sup.beam.

cannot compute difference between binary files

Deleted _build/test/lib/ranch/ebin/ranch_tcp.beam.

cannot compute difference between binary files

Deleted _build/test/lib/ranch/ebin/ranch_transport.beam.

cannot compute difference between binary files

Deleted config/config.exs.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
use Mix.Config

# This configuration is loaded before any dependency and is restricted
# to this project. If another project depends on this project, this
# file won't be loaded nor affect the parent project. For this reason,
# if you want to provide default values for your application for
# 3rd-party users, it should be done in your "mix.exs" file.

# You can configure for your application as:
#
#     config :antigone, key: :value
#
# And access this configuration in your application as:
#
#     Application.get_env(:antigone, :key)
#
# Or configure a 3rd-party app:
#
#     config :logger, level: :info
#

# It is also possible to import configuration files, relative to this
# directory. For example, you can emulate configuration per environment
# by uncommenting the line below and defining dev.exs, test.exs and such.
# Configuration from the imported file will override the ones defined
# here (which is why it is important to import them last).
#
#     import_config "#{Mix.env}.exs"
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




























































Deleted deps/cowboy/AUTHORS.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
Cowboy is available thanks to the work of:

Loïc Hoguin
Magnus Klaar
Ali Sabil
Anthony Ramine
Adam Cammack
Tom Burdick
James Fish
Paul Oliver
Slava Yurin
Vladimir Dronnikov
YAMAMOTO Takashi
Yurii Rashkovskii
Andrew Majorov
Eduardo Gurgel
Egobrain
Josh Toft
Steven Gravell
Alex Kovalev
Andrew Thompson
Hunter Morris
Ivan Lisenkov
Martin Rehfeld
Matthias Endler
Seletskiy Stanislav
Sina Samavati
Tristan Sloughter
0x00F6
0xAX
Adam Cammmack
Adrian Roe
Alexander Færøy
Alexei Sholik
Alexey Lebedeff
Andre Graf
Andrzej Sliwa
Blake Gentry
Bob Ippolito
Boris Faure
Cameron Bytheway
Cristian Hancila
Daniel White
Danielle Sucher
Dave Peticolas
David Kelly
David N. Welton
DeadZen
Dmitry Groshev
Drew
Drew Varner
Eiichi Tsukata
Fred Hebert
Hans Ulrich Niedermann
Ivan Blinkov
Jeremy Ong
Jesper Louis Andersen
Josh Allmann
Josh Marchán
José Valim
Julian Squires
Klaus Trainer
Kuk-Hyun Lee
Mathieu Lecarme
Max Lapshin
Michael Truog
Michiel Hakvoort
Nakai Ryosuke
Ori Bar
Pablo Vieytes
Paulo Oliveira
Peter Ericson
RJ
Radosław Szymczyszyn
Richard Ramsden
Roberto Ostinelli
Sergey Prokhorov
Sergey Rublev
Sergey Urbanovich
Seven Du
Thomas Nordström
Tim Dysinger
Tomas Morstein
Unix1
alisdair sullivan
dbmercer
derdesign
mocchira
pmyarchon
rambocoder
serge
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






















































































































































































Deleted deps/cowboy/LICENSE.

1
2
3
4
5
6
7
8
9
10
11
12
13
Copyright (c) 2011-2014, Loïc Hoguin <essen@ninenines.eu>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
<
<
<
<
<
<
<
<
<
<
<
<
<


























Deleted deps/cowboy/Makefile.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# See LICENSE for licensing information.

PROJECT = cowboy

# Options.

ERLC_OPTS ?= -Werror +debug_info +warn_export_all +warn_export_vars \
	+warn_shadow_vars +warn_obsolete_guard +warn_missing_spec
COMPILE_FIRST = cowboy_middleware cowboy_sub_protocol
CT_OPTS += -pa test -ct_hooks cowboy_ct_hook []
PLT_APPS = crypto public_key ssl
CI_OTP = OTP_R16B01 OTP_R16B02 OTP_R16B03-1 OTP-17.0.2 OTP-17.1.2 OTP-17.2.2 OTP-17.3.4 OTP-17.4.1 OTP-17.5.6.2 OTP-18.0.2

# Dependencies.

DEPS = cowlib ranch
TEST_DEPS = ct_helper gun
dep_ct_helper = git https://github.com/extend/ct_helper.git master
dep_gun = git https://github.com/ninenines/gun b85c1f726ca49ac0e3abdcf717317cb95b06207d

# Standard targets.

include erlang.mk

# Documentation.

dep_ezdoc = git https://github.com/ninenines/ezdoc master
$(eval $(call dep_target,ezdoc))

build-doc-deps: $(DEPS_DIR)/ezdoc
	$(MAKE) -C $(DEPS_DIR)/ezdoc

define ezdoc_script
io:format("Building manual~n"),
[begin
	AST = ezdoc:parse_file(F),
	BF = filename:rootname(filename:basename(F)),
	io:format("  ~s~n", [BF]),
	file:write_file("doc/markdown/manual/" ++ BF ++ ".md", ezdoc_markdown:export(AST)),
	case BF of
		"cowboy" ++ _ when BF =/= "cowboy_app" ->
			file:write_file("doc/man3/" ++ BF ++ ".3", ezdoc_man:export(3, AST));
		_ when BF =/= "index" ->
			file:write_file("doc/man7/" ++ BF ++ ".7", ezdoc_man:export(7, AST));
		_ ->
			ok
	end
end || F <- filelib:wildcard("doc/src/manual/*.ezdoc")],
io:format("Building guide~n"),
[begin
	AST = ezdoc:parse_file(F),
	BF = filename:rootname(filename:basename(F)),
	io:format("  ~s~n", [BF]),
	file:write_file("doc/markdown/guide/" ++ BF ++ ".md", ezdoc_markdown:export(AST))
end || F <- filelib:wildcard("doc/src/guide/*.ezdoc")],
io:format("Done.~n"),
init:stop().
endef
export ezdoc_script

docs:: clean-docs build-doc-deps
	@mkdir -p doc/man3 doc/man7 doc/markdown/guide doc/markdown/manual
	$(gen_verbose) erl -noinput -pa ebin deps/ezdoc/ebin -eval "$$ezdoc_script"
	@gzip doc/man3/*.3 doc/man7/*.7
	@cp doc/src/guide/*.png doc/markdown/guide

clean-docs:
	$(gen_verbose) rm -rf doc/man3 doc/man7 doc/markdown

MAN_INSTALL_PATH ?= /usr/local/share/man

install-docs:
	mkdir -p $(MAN_INSTALL_PATH)/man3/ $(MAN_INSTALL_PATH)/man7/
	install -g 0 -o 0 -m 0644 doc/man3/*.gz $(MAN_INSTALL_PATH)/man3/
	install -g 0 -o 0 -m 0644 doc/man7/*.gz $(MAN_INSTALL_PATH)/man7/
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






















































































































































Deleted deps/cowboy/README.md.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
Cowboy
======

Cowboy is a small, fast and modular HTTP server written in Erlang.

Goals
-----

Cowboy aims to provide a **complete** HTTP stack in a **small** code base.
It is optimized for **low latency** and **low memory usage**, in part
because it uses **binary strings**.

Cowboy provides **routing** capabilities, selectively dispatching requests
to handlers written in Erlang.

Because it uses Ranch for managing connections, Cowboy can easily be
**embedded** in any other application.

No parameterized module. No process dictionary. **Clean** Erlang code.

Sponsors
--------

The SPDY implementation was sponsored by
[LeoFS Cloud Storage](http://www.leofs.org).

The project is currently sponsored by
[Kato.im](https://kato.im).

Online documentation
--------------------

 *  [User guide](http://ninenines.eu/docs/en/cowboy/HEAD/guide)
 *  [Function reference](http://ninenines.eu/docs/en/cowboy/HEAD/manual)

Offline documentation
---------------------

 *  While still online, run `make docs`
 *  Function reference man pages available in `doc/man3/` and `doc/man7/`
 *  Run `make install-docs` to install man pages on your system
 *  Full documentation in Markdown available in `doc/markdown/`
 *  Examples available in `examples/`

Getting help
------------

 *  Official IRC Channel: #ninenines on irc.freenode.net
 *  [Mailing Lists](http://lists.ninenines.eu)
 *  [Commercial Support](http://ninenines.eu/support)
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




































































































Deleted deps/cowboy/ebin/cowboy.app.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{application,cowboy,
             [{description,"Small, fast, modular HTTP server."},
              {vsn,"1.0.4"},
              {id,"git"},
              {modules,[cowboy,cowboy_app,cowboy_bstr,cowboy_clock,
                        cowboy_handler,cowboy_http,cowboy_http_handler,
                        cowboy_loop_handler,cowboy_middleware,cowboy_protocol,
                        cowboy_req,cowboy_rest,cowboy_router,cowboy_spdy,
                        cowboy_static,cowboy_sub_protocol,cowboy_sup,
                        cowboy_websocket,cowboy_websocket_handler]},
              {registered,[cowboy_clock,cowboy_sup]},
              {applications,[kernel,stdlib,ranch,cowlib,crypto]},
              {mod,{cowboy_app,[]}},
              {env,[]}]}.
<
<
<
<
<
<
<
<
<
<
<
<
<
<




























Deleted deps/cowboy/ebin/cowboy.beam.

cannot compute difference between binary files

Deleted deps/cowboy/ebin/cowboy_app.beam.

cannot compute difference between binary files

Deleted deps/cowboy/ebin/cowboy_bstr.beam.

cannot compute difference between binary files

Deleted deps/cowboy/ebin/cowboy_clock.beam.

cannot compute difference between binary files

Deleted deps/cowboy/ebin/cowboy_handler.beam.

cannot compute difference between binary files

Deleted deps/cowboy/ebin/cowboy_http.beam.

cannot compute difference between binary files

Deleted deps/cowboy/ebin/cowboy_http_handler.beam.

cannot compute difference between binary files

Deleted deps/cowboy/ebin/cowboy_loop_handler.beam.

cannot compute difference between binary files

Deleted deps/cowboy/ebin/cowboy_middleware.beam.

cannot compute difference between binary files

Deleted deps/cowboy/ebin/cowboy_protocol.beam.

cannot compute difference between binary files

Deleted deps/cowboy/ebin/cowboy_req.beam.

cannot compute difference between binary files

Deleted deps/cowboy/ebin/cowboy_rest.beam.

cannot compute difference between binary files

Deleted deps/cowboy/ebin/cowboy_router.beam.

cannot compute difference between binary files

Deleted deps/cowboy/ebin/cowboy_spdy.beam.

cannot compute difference between binary files

Deleted deps/cowboy/ebin/cowboy_static.beam.

cannot compute difference between binary files

Deleted deps/cowboy/ebin/cowboy_sub_protocol.beam.

cannot compute difference between binary files

Deleted deps/cowboy/ebin/cowboy_sup.beam.

cannot compute difference between binary files

Deleted deps/cowboy/ebin/cowboy_websocket.beam.

cannot compute difference between binary files

Deleted deps/cowboy/ebin/cowboy_websocket_handler.beam.

cannot compute difference between binary files

Deleted deps/cowboy/erlang.mk.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
4526
4527
4528
4529
4530
4531
4532
4533
4534
4535
4536
4537
4538
4539
4540
4541
4542
4543
4544
4545
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
4560
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
4576
4577
4578
4579
4580
4581
4582
4583
4584
4585
4586
4587
4588
4589
4590
4591
4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606
4607
4608
4609
4610
4611
4612
4613
4614
4615
4616
4617
4618
4619
4620
4621
4622
4623
4624
4625
4626
4627
4628
4629
4630
4631
4632
4633
4634
4635
4636
4637
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651
4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
4669
4670
4671
4672
4673
4674
4675
4676
4677
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
4693
4694
4695
4696
4697
4698
4699
4700
4701
4702
4703
4704
4705
4706
4707
4708
4709
4710
4711
4712
4713
4714
4715
4716
4717
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
4732
4733
4734
4735
4736
4737
4738
4739
4740
4741
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771
4772
4773
4774
4775
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
4794
4795
4796
4797
4798
4799
4800
4801
4802
4803
4804
4805
4806
4807
4808
4809
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819
4820
4821
4822
4823
4824
4825
4826
4827
4828
4829
4830
4831
4832
4833
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
4847
4848
4849
4850
4851
4852
4853
4854
4855
4856
4857
4858
4859
4860
4861
4862
4863
4864
4865
4866
4867
4868
4869
4870
4871
4872
4873
4874
4875
4876
4877
4878
4879
4880
4881
4882
4883
4884
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
4915
4916
4917
4918
4919
4920
4921
4922
4923
4924
4925
4926
4927
4928
4929
4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960
4961
4962
4963
4964
4965
4966
4967
4968
4969
4970
4971
4972
4973
4974
4975
4976
4977
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987
4988
4989
4990
4991
4992
4993
4994
4995
4996
4997
4998
4999
5000
5001
5002
5003
5004
5005
5006
5007
5008
5009
5010
5011
5012
5013
5014
5015
5016
5017
5018
5019
5020
5021
5022
5023
5024
5025
5026
5027
5028
5029
5030
5031
5032
5033
5034
5035
5036
5037
5038
5039
5040
5041
5042
5043
5044
5045
5046
5047
5048
5049
5050
5051
5052
5053
5054
5055
5056
5057
5058
5059
5060
5061
5062
5063
5064
5065
5066
5067
5068
5069
5070
5071
5072
5073
5074
5075
5076
5077
5078
5079
5080
5081
5082
5083
5084
5085
5086
5087
5088
5089
5090
5091
5092
5093
5094
5095
5096
5097
5098
5099
5100
5101
5102
5103
5104
5105
5106
5107
5108
5109
5110
5111
5112
5113
5114
5115
5116
5117
5118
5119
5120
5121
5122
5123
5124
5125
5126
5127
5128
5129
5130
5131
5132
5133
5134
5135
5136
5137
5138
5139
5140
5141
5142
5143
5144
5145
5146
5147
5148
5149
5150
5151
5152
5153
5154
5155
5156
5157
5158
5159
5160
5161
5162
5163
5164
5165
5166
5167
5168
5169
5170
5171
5172
5173
5174
5175
5176
5177
5178
5179
5180
5181
5182
5183
5184
5185
5186
5187
5188
5189
5190
5191
5192
5193
5194
5195
5196
5197
5198
5199
5200
5201
5202
5203
5204
5205
5206
5207
5208
5209
5210
5211
5212
5213
5214
5215
5216
5217
5218
5219
5220
5221
5222
5223
5224
5225
5226
5227
5228
5229
5230
5231
5232
5233
5234
5235
5236
5237
5238
5239
5240
5241
5242
5243
5244
5245
5246
5247
5248
5249
5250
5251
5252
5253
5254
5255
5256
5257
5258
5259
5260
5261
5262
5263
5264
5265
5266
5267
5268
5269
5270
5271
5272
5273
5274
5275
5276
5277
5278
5279
5280
5281
5282
5283
5284
5285
5286
5287
5288
5289
5290
5291
5292
5293
5294
5295
5296
5297
5298
5299
5300
5301
5302
5303
5304
5305
5306
5307
5308
5309
5310
5311
5312
5313
5314
5315
5316
5317
5318
5319
5320
5321
5322
5323
5324
5325
5326
5327
5328
5329
5330
5331
5332
5333
5334
5335
5336
5337
5338
5339
5340
5341
5342
5343
5344
5345
5346
5347
5348
5349
5350
5351
5352
5353
5354
5355
5356
5357
5358
5359
5360
5361
5362
5363
5364
5365
5366
5367
5368
5369
5370
5371
5372
5373
5374
5375
5376
5377
5378
5379
5380
5381
5382
5383
5384
5385
5386
5387
5388
5389
5390
5391
5392
5393
5394
5395
5396
5397
5398
5399
5400
5401
5402
5403
5404
5405
5406
5407
5408
5409
5410
5411
5412
5413
5414
5415
5416
5417
5418
5419
5420
5421
5422
5423
5424
5425
5426
5427
5428
5429
5430
5431
5432
5433
5434
5435
5436
5437
5438
5439
5440
5441
5442
5443
5444
5445
5446
5447
5448
5449
5450
5451
5452
5453
5454
5455
5456
5457
5458
5459
5460
5461
5462
5463
5464
5465
5466
5467
5468
5469
5470
5471
5472
5473
5474
5475
5476
5477
5478
5479
5480
5481
5482
5483
5484
5485
5486
5487
5488
5489
5490
5491
5492
5493
5494
5495
5496
5497
5498
5499
5500
5501
5502
5503
5504
5505
5506
5507
5508
5509
5510
5511
5512
5513
5514
5515
5516
5517
5518
5519
5520
5521
5522
5523
5524
5525
5526
5527
5528
5529
5530
5531
5532
5533
5534
5535
5536
5537
5538
5539
5540
5541
5542
5543
5544
5545
5546
5547
5548
5549
5550
5551
5552
5553
5554
5555
5556
5557
5558
5559
5560
5561
5562
5563
5564
5565
5566
5567
5568
5569
5570
5571
5572
5573
5574
5575
5576
5577
5578
5579
5580
5581
5582
5583
5584
5585
5586
5587
5588
5589
5590
5591
5592
5593
5594
5595
5596
5597
5598
5599
5600
5601
5602
5603
5604
5605
5606
5607
5608
5609
5610
5611
5612
5613
5614
5615
5616
5617
5618
5619
5620
5621
5622
5623
5624
5625
5626
5627
5628
5629
5630
5631
5632
5633
5634
5635
5636
5637
5638
5639
5640
5641
5642
5643
5644
5645
5646
5647
5648
5649
5650
5651
5652
5653
5654
5655
5656
5657
5658
5659
5660
5661
5662
5663
5664
5665
5666
5667
5668
5669
5670
5671
5672
5673
5674
5675
5676
5677
5678
5679
5680
5681
5682
5683
5684
5685
5686
5687
5688
5689
5690
5691
5692
5693
5694
5695
5696
5697
5698
5699
5700
5701
5702
5703
5704
5705
5706
5707
5708
5709
5710
5711
5712
5713
5714
5715
5716
5717
5718
5719
5720
5721
5722
5723
5724
5725
5726
5727
5728
5729
5730
5731
5732
5733
5734
5735
5736
5737
5738
5739
5740
5741
5742
5743
5744
5745
5746
5747
5748
5749
5750
5751
5752
5753
5754
5755
5756
5757
5758
5759
5760
5761
5762
5763
5764
5765
5766
5767
5768
5769
5770
5771
5772
5773
5774
5775
5776
5777
5778
5779
5780
5781
5782
5783
5784
5785
5786
5787
5788
5789
5790
5791
5792
5793
5794
5795
5796
5797
5798
5799
5800
5801
5802
5803
5804
5805
5806
5807
5808
5809
5810
5811
5812
5813
5814
5815
5816
5817
5818
5819
5820
5821
5822
5823
5824
5825
5826
5827
5828
5829
5830
5831
5832
5833
5834
5835
5836
5837
5838
5839
5840
5841
5842
5843
5844
5845
5846
5847
5848
5849
5850
5851
5852
5853
5854
5855
5856
5857
5858
5859
5860
5861
5862
5863
5864
5865
5866
5867
5868
5869
5870
5871
5872
5873
5874
5875
5876
5877
5878
5879
5880
5881
5882
5883
5884
5885
5886
5887
5888
5889
5890
5891
5892
5893
5894
5895
5896
5897
5898
5899
5900
5901
5902
5903
5904
5905
5906
5907
5908
5909
5910
5911
5912
5913
5914
5915
5916
5917
5918
5919
5920
5921
5922
5923
5924
5925
5926
5927
5928
5929
5930
5931
5932
5933
5934
5935
5936
5937
5938
5939
5940
5941
5942
5943
5944
5945
5946
5947
5948
5949
5950
5951
5952
5953
5954
5955
5956
5957
5958
5959
5960
5961
5962
5963
5964
5965
5966
5967
5968
5969
5970
5971
5972
5973
5974
5975
5976
5977
5978
5979
5980
5981
5982
5983
5984
5985
5986
5987
5988
5989
5990
5991
5992
5993
5994
5995
5996
5997
5998
5999
6000
6001
6002
6003
6004
6005
6006
6007
6008
6009
6010
6011
6012
6013
6014
6015
6016
6017
6018
6019
6020
6021
6022
6023
6024
6025
6026
6027
6028
6029
# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

.PHONY: all app deps search rel docs install-docs check tests clean distclean help erlang-mk

ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))

ERLANG_MK_VERSION = 1.2.0-632-g26ea355-dirty

# Core configuration.

PROJECT ?= $(notdir $(CURDIR))
PROJECT := $(strip $(PROJECT))

PROJECT_VERSION ?= rolling

# Verbosity.

V ?= 0

verbose_0 = @
verbose = $(verbose_$(V))

gen_verbose_0 = @echo " GEN   " $@;
gen_verbose = $(gen_verbose_$(V))

# Temporary files directory.

ERLANG_MK_TMP ?= $(CURDIR)/.erlang.mk
export ERLANG_MK_TMP

# "erl" command.

ERL = erl +A0 -noinput -boot start_clean

# Platform detection.
# @todo Add Windows/Cygwin detection eventually.

ifeq ($(PLATFORM),)
UNAME_S := $(shell uname -s)

ifeq ($(UNAME_S),Linux)
PLATFORM = linux
else ifeq ($(UNAME_S),Darwin)
PLATFORM = darwin
else ifeq ($(UNAME_S),SunOS)
PLATFORM = solaris
else ifeq ($(UNAME_S),GNU)
PLATFORM = gnu
else ifeq ($(UNAME_S),FreeBSD)
PLATFORM = freebsd
else ifeq ($(UNAME_S),NetBSD)
PLATFORM = netbsd
else ifeq ($(UNAME_S),OpenBSD)
PLATFORM = openbsd
else
$(error Unable to detect platform. Please open a ticket with the output of uname -a.)
endif

export PLATFORM
endif

# Core targets.

ifneq ($(words $(MAKECMDGOALS)),1)
.NOTPARALLEL:
endif

all:: deps
	$(verbose) $(MAKE) --no-print-directory app
	$(verbose) $(MAKE) --no-print-directory rel

# Noop to avoid a Make warning when there's nothing to do.
rel::
	$(verbose) echo -n

check:: clean app tests

clean:: clean-crashdump

clean-crashdump:
ifneq ($(wildcard erl_crash.dump),)
	$(gen_verbose) rm -f erl_crash.dump
endif

distclean:: clean

help::
	$(verbose) printf "%s\n" \
		"erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
		"Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \
		"" \
		"Usage: [V=1] $(MAKE) [-jNUM] [target]..." \
		"" \
		"Core targets:" \
		"  all           Run deps, app and rel targets in that order" \
		"  app           Compile the project" \
		"  deps          Fetch dependencies (if needed) and compile them" \
		"  search q=...  Search for a package in the built-in index" \
		"  rel           Build a release for this project, if applicable" \
		"  docs          Build the documentation for this project" \
		"  install-docs  Install the man pages for this project" \
		"  check         Compile and run all tests and analysis for this project" \
		"  tests         Run the tests for this project" \
		"  clean         Delete temporary and output files from most targets" \
		"  distclean     Delete all temporary and output files" \
		"  help          Display this help and exit" \
		"  erlang-mk     Update erlang.mk to the latest version"

# Core functions.

empty :=
space := $(empty) $(empty)
tab := $(empty)	$(empty)
comma := ,

define newline


endef

define comma_list
$(subst $(space),$(comma),$(strip $(1)))
endef

# Adding erlang.mk to make Erlang scripts who call init:get_plain_arguments() happy.
define erlang
$(ERL) -pz $(ERLANG_MK_TMP)/rebar/ebin -eval "$(subst $(newline),,$(subst ",\",$(1)))" -- erlang.mk
endef

ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
define core_http_get
	wget --no-check-certificate -O $(1) $(2)|| rm $(1)
endef
else
define core_http_get.erl
	ssl:start(),
	inets:start(),
	case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
		{ok, {{_, 200, _}, _, Body}} ->
			case file:write_file("$(1)", Body) of
				ok -> ok;
				{error, R1} -> halt(R1)
			end;
		{error, R2} ->
			halt(R2)
	end,
	halt(0).
endef

define core_http_get
	$(call erlang,$(call core_http_get.erl,$(1),$(2)))
endef
endif

core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))

core_find = $(foreach d,$(call core_ls,$1*),$(call core_find,$d/,$2) $(filter $(subst *,%,$2),$d))

core_lc = $(subst A,a,$(subst B,b,$(subst C,c,$(subst D,d,$(subst E,e,$(subst F,f,$(subst G,g,$(subst H,h,$(subst I,i,$(subst J,j,$(subst K,k,$(subst L,l,$(subst M,m,$(subst N,n,$(subst O,o,$(subst P,p,$(subst Q,q,$(subst R,r,$(subst S,s,$(subst T,t,$(subst U,u,$(subst V,v,$(subst W,w,$(subst X,x,$(subst Y,y,$(subst Z,z,$(1)))))))))))))))))))))))))))

# @todo On Windows: $(shell dir /B $(1)); make sure to handle when no file exists.
core_ls = $(filter-out $(1),$(shell echo $(1)))

# Automated update.

ERLANG_MK_BUILD_CONFIG ?= build.config
ERLANG_MK_BUILD_DIR ?= .erlang.mk.build

erlang-mk:
	git clone https://github.com/ninenines/erlang.mk $(ERLANG_MK_BUILD_DIR)
	if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR); fi
	cd $(ERLANG_MK_BUILD_DIR) && $(MAKE)
	cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
	rm -rf $(ERLANG_MK_BUILD_DIR)

# The erlang.mk package index is bundled in the default erlang.mk build.
# Search for the string "copyright" to skip to the rest of the code.

PACKAGES += aberth
pkg_aberth_name = aberth
pkg_aberth_description = Generic BERT-RPC server in Erlang
pkg_aberth_homepage = https://github.com/a13x/aberth
pkg_aberth_fetch = git
pkg_aberth_repo = https://github.com/a13x/aberth
pkg_aberth_commit = master

PACKAGES += active
pkg_active_name = active
pkg_active_description = Active development for Erlang: rebuild and reload source/binary files while the VM is running
pkg_active_homepage = https://github.com/proger/active
pkg_active_fetch = git
pkg_active_repo = https://github.com/proger/active
pkg_active_commit = master

PACKAGES += actordb_core
pkg_actordb_core_name = actordb_core
pkg_actordb_core_description = ActorDB main source
pkg_actordb_core_homepage = http://www.actordb.com/
pkg_actordb_core_fetch = git
pkg_actordb_core_repo = https://github.com/biokoda/actordb_core
pkg_actordb_core_commit = master

PACKAGES += actordb_thrift
pkg_actordb_thrift_name = actordb_thrift
pkg_actordb_thrift_description = Thrift API for ActorDB
pkg_actordb_thrift_homepage = http://www.actordb.com/
pkg_actordb_thrift_fetch = git
pkg_actordb_thrift_repo = https://github.com/biokoda/actordb_thrift
pkg_actordb_thrift_commit = master

PACKAGES += aleppo
pkg_aleppo_name = aleppo
pkg_aleppo_description = Alternative Erlang Pre-Processor
pkg_aleppo_homepage = https://github.com/ErlyORM/aleppo
pkg_aleppo_fetch = git
pkg_aleppo_repo = https://github.com/ErlyORM/aleppo
pkg_aleppo_commit = master

PACKAGES += alog
pkg_alog_name = alog
pkg_alog_description = Simply the best logging framework for Erlang
pkg_alog_homepage = https://github.com/siberian-fast-food/alogger
pkg_alog_fetch = git
pkg_alog_repo = https://github.com/siberian-fast-food/alogger
pkg_alog_commit = master

PACKAGES += amqp_client
pkg_amqp_client_name = amqp_client
pkg_amqp_client_description = RabbitMQ Erlang AMQP client
pkg_amqp_client_homepage = https://www.rabbitmq.com/erlang-client-user-guide.html
pkg_amqp_client_fetch = git
pkg_amqp_client_repo = https://github.com/rabbitmq/rabbitmq-erlang-client.git
pkg_amqp_client_commit = master

PACKAGES += annotations
pkg_annotations_name = annotations
pkg_annotations_description = Simple code instrumentation utilities
pkg_annotations_homepage = https://github.com/hyperthunk/annotations
pkg_annotations_fetch = git
pkg_annotations_repo = https://github.com/hyperthunk/annotations
pkg_annotations_commit = master

PACKAGES += antidote
pkg_antidote_name = antidote
pkg_antidote_description = Large-scale computation without synchronisation
pkg_antidote_homepage = https://syncfree.lip6.fr/
pkg_antidote_fetch = git
pkg_antidote_repo = https://github.com/SyncFree/antidote
pkg_antidote_commit = master

PACKAGES += apns
pkg_apns_name = apns
pkg_apns_description = Apple Push Notification Server for Erlang
pkg_apns_homepage = http://inaka.github.com/apns4erl
pkg_apns_fetch = git
pkg_apns_repo = https://github.com/inaka/apns4erl
pkg_apns_commit = 1.0.4

PACKAGES += azdht
pkg_azdht_name = azdht
pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
pkg_azdht_homepage = https://github.com/arcusfelis/azdht
pkg_azdht_fetch = git
pkg_azdht_repo = https://github.com/arcusfelis/azdht
pkg_azdht_commit = master

PACKAGES += backoff
pkg_backoff_name = backoff
pkg_backoff_description = Simple exponential backoffs in Erlang
pkg_backoff_homepage = https://github.com/ferd/backoff
pkg_backoff_fetch = git
pkg_backoff_repo = https://github.com/ferd/backoff
pkg_backoff_commit = master

PACKAGES += barrel_tcp
pkg_barrel_tcp_name = barrel_tcp
pkg_barrel_tcp_description = barrel is a generic TCP acceptor pool with low latency in Erlang.
pkg_barrel_tcp_homepage = https://github.com/benoitc-attic/barrel_tcp
pkg_barrel_tcp_fetch = git
pkg_barrel_tcp_repo = https://github.com/benoitc-attic/barrel_tcp
pkg_barrel_tcp_commit = master

PACKAGES += basho_bench
pkg_basho_bench_name = basho_bench
pkg_basho_bench_description = A load-generation and testing tool for basically whatever you can write a returning Erlang function for.
pkg_basho_bench_homepage = https://github.com/basho/basho_bench
pkg_basho_bench_fetch = git
pkg_basho_bench_repo = https://github.com/basho/basho_bench
pkg_basho_bench_commit = master

PACKAGES += bcrypt
pkg_bcrypt_name = bcrypt
pkg_bcrypt_description = Bcrypt Erlang / C library
pkg_bcrypt_homepage = https://github.com/riverrun/branglecrypt
pkg_bcrypt_fetch = git
pkg_bcrypt_repo = https://github.com/riverrun/branglecrypt
pkg_bcrypt_commit = master

PACKAGES += beam
pkg_beam_name = beam
pkg_beam_description = BEAM emulator written in Erlang
pkg_beam_homepage = https://github.com/tonyrog/beam
pkg_beam_fetch = git
pkg_beam_repo = https://github.com/tonyrog/beam
pkg_beam_commit = master

PACKAGES += beanstalk
pkg_beanstalk_name = beanstalk
pkg_beanstalk_description = An Erlang client for beanstalkd
pkg_beanstalk_homepage = https://github.com/tim/erlang-beanstalk
pkg_beanstalk_fetch = git
pkg_beanstalk_repo = https://github.com/tim/erlang-beanstalk
pkg_beanstalk_commit = master

PACKAGES += bear
pkg_bear_name = bear
pkg_bear_description = a set of statistics functions for erlang
pkg_bear_homepage = https://github.com/boundary/bear
pkg_bear_fetch = git
pkg_bear_repo = https://github.com/boundary/bear
pkg_bear_commit = master

PACKAGES += bertconf
pkg_bertconf_name = bertconf
pkg_bertconf_description = Make ETS tables out of statc BERT files that are auto-reloaded
pkg_bertconf_homepage = https://github.com/ferd/bertconf
pkg_bertconf_fetch = git
pkg_bertconf_repo = https://github.com/ferd/bertconf
pkg_bertconf_commit = master

PACKAGES += bifrost
pkg_bifrost_name = bifrost
pkg_bifrost_description = Erlang FTP Server Framework
pkg_bifrost_homepage = https://github.com/thorstadt/bifrost
pkg_bifrost_fetch = git
pkg_bifrost_repo = https://github.com/thorstadt/bifrost
pkg_bifrost_commit = master

PACKAGES += binpp
pkg_binpp_name = binpp
pkg_binpp_description = Erlang Binary Pretty Printer
pkg_binpp_homepage = https://github.com/jtendo/binpp
pkg_binpp_fetch = git
pkg_binpp_repo = https://github.com/jtendo/binpp
pkg_binpp_commit = master

PACKAGES += bisect
pkg_bisect_name = bisect
pkg_bisect_description = Ordered fixed-size binary dictionary in Erlang
pkg_bisect_homepage = https://github.com/knutin/bisect
pkg_bisect_fetch = git
pkg_bisect_repo = https://github.com/knutin/bisect
pkg_bisect_commit = master

PACKAGES += bitcask
pkg_bitcask_name = bitcask
pkg_bitcask_description = because you need another a key/value storage engine
pkg_bitcask_homepage = https://github.com/basho/bitcask
pkg_bitcask_fetch = git
pkg_bitcask_repo = https://github.com/basho/bitcask
pkg_bitcask_commit = master

PACKAGES += bitstore
pkg_bitstore_name = bitstore
pkg_bitstore_description = A document based ontology development environment
pkg_bitstore_homepage = https://github.com/bdionne/bitstore
pkg_bitstore_fetch = git
pkg_bitstore_repo = https://github.com/bdionne/bitstore
pkg_bitstore_commit = master

PACKAGES += bootstrap
pkg_bootstrap_name = bootstrap
pkg_bootstrap_description = A simple, yet powerful Erlang cluster bootstrapping application.
pkg_bootstrap_homepage = https://github.com/schlagert/bootstrap
pkg_bootstrap_fetch = git
pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
pkg_bootstrap_commit = master

PACKAGES += boss_db
pkg_boss_db_name = boss_db
pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
pkg_boss_db_homepage = https://github.com/ErlyORM/boss_db
pkg_boss_db_fetch = git
pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
pkg_boss_db_commit = master

PACKAGES += boss
pkg_boss_name = boss
pkg_boss_description = Erlang web MVC, now featuring Comet
pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
pkg_boss_fetch = git
pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
pkg_boss_commit = master

PACKAGES += bson
pkg_bson_name = bson
pkg_bson_description = BSON documents in Erlang, see bsonspec.org
pkg_bson_homepage = https://github.com/comtihon/bson-erlang
pkg_bson_fetch = git
pkg_bson_repo = https://github.com/comtihon/bson-erlang
pkg_bson_commit = master

PACKAGES += bullet
pkg_bullet_name = bullet
pkg_bullet_description = Simple, reliable, efficient streaming for Cowboy.
pkg_bullet_homepage = http://ninenines.eu
pkg_bullet_fetch = git
pkg_bullet_repo = https://github.com/extend/bullet
pkg_bullet_commit = master

PACKAGES += cache
pkg_cache_name = cache
pkg_cache_description = Erlang in-memory cache
pkg_cache_homepage = https://github.com/fogfish/cache
pkg_cache_fetch = git
pkg_cache_repo = https://github.com/fogfish/cache
pkg_cache_commit = master

PACKAGES += cake
pkg_cake_name = cake
pkg_cake_description = Really simple terminal colorization
pkg_cake_homepage = https://github.com/darach/cake-erl
pkg_cake_fetch = git
pkg_cake_repo = https://github.com/darach/cake-erl
pkg_cake_commit = v0.1.2

PACKAGES += carotene
pkg_carotene_name = carotene
pkg_carotene_description = Real-time server
pkg_carotene_homepage = https://github.com/carotene/carotene
pkg_carotene_fetch = git
pkg_carotene_repo = https://github.com/carotene/carotene
pkg_carotene_commit = master

PACKAGES += cberl
pkg_cberl_name = cberl
pkg_cberl_description = NIF based Erlang bindings for Couchbase
pkg_cberl_homepage = https://github.com/chitika/cberl
pkg_cberl_fetch = git
pkg_cberl_repo = https://github.com/chitika/cberl
pkg_cberl_commit = master

PACKAGES += cecho
pkg_cecho_name = cecho
pkg_cecho_description = An ncurses library for Erlang
pkg_cecho_homepage = https://github.com/mazenharake/cecho
pkg_cecho_fetch = git
pkg_cecho_repo = https://github.com/mazenharake/cecho
pkg_cecho_commit = master

PACKAGES += cferl
pkg_cferl_name = cferl
pkg_cferl_description = Rackspace / Open Stack Cloud Files Erlang Client
pkg_cferl_homepage = https://github.com/ddossot/cferl
pkg_cferl_fetch = git
pkg_cferl_repo = https://github.com/ddossot/cferl
pkg_cferl_commit = master

PACKAGES += chaos_monkey
pkg_chaos_monkey_name = chaos_monkey
pkg_chaos_monkey_description = This is The CHAOS MONKEY.  It will kill your processes.
pkg_chaos_monkey_homepage = https://github.com/dLuna/chaos_monkey
pkg_chaos_monkey_fetch = git
pkg_chaos_monkey_repo = https://github.com/dLuna/chaos_monkey
pkg_chaos_monkey_commit = master

PACKAGES += check_node
pkg_check_node_name = check_node
pkg_check_node_description = Nagios Scripts for monitoring Riak
pkg_check_node_homepage = https://github.com/basho-labs/riak_nagios
pkg_check_node_fetch = git
pkg_check_node_repo = https://github.com/basho-labs/riak_nagios
pkg_check_node_commit = master

PACKAGES += chronos
pkg_chronos_name = chronos
pkg_chronos_description = Timer module for Erlang that makes it easy to abstact time out of the tests.
pkg_chronos_homepage = https://github.com/lehoff/chronos
pkg_chronos_fetch = git
pkg_chronos_repo = https://github.com/lehoff/chronos
pkg_chronos_commit = master

PACKAGES += classifier
pkg_classifier_name = classifier
pkg_classifier_description = An Erlang Bayesian Filter and Text Classifier
pkg_classifier_homepage = https://github.com/inaka/classifier
pkg_classifier_fetch = git
pkg_classifier_repo = https://github.com/inaka/classifier
pkg_classifier_commit = master

PACKAGES += clique
pkg_clique_name = clique
pkg_clique_description = CLI Framework for Erlang
pkg_clique_homepage = https://github.com/basho/clique
pkg_clique_fetch = git
pkg_clique_repo = https://github.com/basho/clique
pkg_clique_commit = develop

PACKAGES += cl
pkg_cl_name = cl
pkg_cl_description = OpenCL binding for Erlang
pkg_cl_homepage = https://github.com/tonyrog/cl
pkg_cl_fetch = git
pkg_cl_repo = https://github.com/tonyrog/cl
pkg_cl_commit = master

PACKAGES += cloudi_core
pkg_cloudi_core_name = cloudi_core
pkg_cloudi_core_description = CloudI internal service runtime
pkg_cloudi_core_homepage = http://cloudi.org/
pkg_cloudi_core_fetch = git
pkg_cloudi_core_repo = https://github.com/CloudI/cloudi_core
pkg_cloudi_core_commit = master

PACKAGES += cloudi_service_api_requests
pkg_cloudi_service_api_requests_name = cloudi_service_api_requests
pkg_cloudi_service_api_requests_description = CloudI Service API requests (JSON-RPC/Erlang-term support)
pkg_cloudi_service_api_requests_homepage = http://cloudi.org/
pkg_cloudi_service_api_requests_fetch = git
pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
pkg_cloudi_service_api_requests_commit = master

PACKAGES += cloudi_service_db_cassandra_cql
pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
pkg_cloudi_service_db_cassandra_cql_homepage = http://cloudi.org/
pkg_cloudi_service_db_cassandra_cql_fetch = git
pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
pkg_cloudi_service_db_cassandra_cql_commit = master

PACKAGES += cloudi_service_db_cassandra
pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
pkg_cloudi_service_db_cassandra_fetch = git
pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
pkg_cloudi_service_db_cassandra_commit = master

PACKAGES += cloudi_service_db_couchdb
pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
pkg_cloudi_service_db_couchdb_homepage = http://cloudi.org/
pkg_cloudi_service_db_couchdb_fetch = git
pkg_cloudi_service_db_couchdb_repo = https://github.com/CloudI/cloudi_service_db_couchdb
pkg_cloudi_service_db_couchdb_commit = master

PACKAGES += cloudi_service_db_elasticsearch
pkg_cloudi_service_db_elasticsearch_name = cloudi_service_db_elasticsearch
pkg_cloudi_service_db_elasticsearch_description = elasticsearch CloudI Service
pkg_cloudi_service_db_elasticsearch_homepage = http://cloudi.org/
pkg_cloudi_service_db_elasticsearch_fetch = git
pkg_cloudi_service_db_elasticsearch_repo = https://github.com/CloudI/cloudi_service_db_elasticsearch
pkg_cloudi_service_db_elasticsearch_commit = master

PACKAGES += cloudi_service_db_memcached
pkg_cloudi_service_db_memcached_name = cloudi_service_db_memcached
pkg_cloudi_service_db_memcached_description = memcached CloudI Service
pkg_cloudi_service_db_memcached_homepage = http://cloudi.org/
pkg_cloudi_service_db_memcached_fetch = git
pkg_cloudi_service_db_memcached_repo = https://github.com/CloudI/cloudi_service_db_memcached
pkg_cloudi_service_db_memcached_commit = master

PACKAGES += cloudi_service_db
pkg_cloudi_service_db_name = cloudi_service_db
pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
pkg_cloudi_service_db_homepage = http://cloudi.org/
pkg_cloudi_service_db_fetch = git
pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
pkg_cloudi_service_db_commit = master

PACKAGES += cloudi_service_db_mysql
pkg_cloudi_service_db_mysql_name = cloudi_service_db_mysql
pkg_cloudi_service_db_mysql_description = MySQL CloudI Service
pkg_cloudi_service_db_mysql_homepage = http://cloudi.org/
pkg_cloudi_service_db_mysql_fetch = git
pkg_cloudi_service_db_mysql_repo = https://github.com/CloudI/cloudi_service_db_mysql
pkg_cloudi_service_db_mysql_commit = master

PACKAGES += cloudi_service_db_pgsql
pkg_cloudi_service_db_pgsql_name = cloudi_service_db_pgsql
pkg_cloudi_service_db_pgsql_description = PostgreSQL CloudI Service
pkg_cloudi_service_db_pgsql_homepage = http://cloudi.org/
pkg_cloudi_service_db_pgsql_fetch = git
pkg_cloudi_service_db_pgsql_repo = https://github.com/CloudI/cloudi_service_db_pgsql
pkg_cloudi_service_db_pgsql_commit = master

PACKAGES += cloudi_service_db_riak
pkg_cloudi_service_db_riak_name = cloudi_service_db_riak
pkg_cloudi_service_db_riak_description = Riak CloudI Service
pkg_cloudi_service_db_riak_homepage = http://cloudi.org/
pkg_cloudi_service_db_riak_fetch = git
pkg_cloudi_service_db_riak_repo = https://github.com/CloudI/cloudi_service_db_riak
pkg_cloudi_service_db_riak_commit = master

PACKAGES += cloudi_service_db_tokyotyrant
pkg_cloudi_service_db_tokyotyrant_name = cloudi_service_db_tokyotyrant
pkg_cloudi_service_db_tokyotyrant_description = Tokyo Tyrant CloudI Service
pkg_cloudi_service_db_tokyotyrant_homepage = http://cloudi.org/
pkg_cloudi_service_db_tokyotyrant_fetch = git
pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
pkg_cloudi_service_db_tokyotyrant_commit = master

PACKAGES += cloudi_service_filesystem
pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
pkg_cloudi_service_filesystem_homepage = http://cloudi.org/
pkg_cloudi_service_filesystem_fetch = git
pkg_cloudi_service_filesystem_repo = https://github.com/CloudI/cloudi_service_filesystem
pkg_cloudi_service_filesystem_commit = master

PACKAGES += cloudi_service_http_client
pkg_cloudi_service_http_client_name = cloudi_service_http_client
pkg_cloudi_service_http_client_description = HTTP client CloudI Service
pkg_cloudi_service_http_client_homepage = http://cloudi.org/
pkg_cloudi_service_http_client_fetch = git
pkg_cloudi_service_http_client_repo = https://github.com/CloudI/cloudi_service_http_client
pkg_cloudi_service_http_client_commit = master

PACKAGES += cloudi_service_http_cowboy
pkg_cloudi_service_http_cowboy_name = cloudi_service_http_cowboy
pkg_cloudi_service_http_cowboy_description = cowboy HTTP/HTTPS CloudI Service
pkg_cloudi_service_http_cowboy_homepage = http://cloudi.org/
pkg_cloudi_service_http_cowboy_fetch = git
pkg_cloudi_service_http_cowboy_repo = https://github.com/CloudI/cloudi_service_http_cowboy
pkg_cloudi_service_http_cowboy_commit = master

PACKAGES += cloudi_service_http_elli
pkg_cloudi_service_http_elli_name = cloudi_service_http_elli
pkg_cloudi_service_http_elli_description = elli HTTP CloudI Service
pkg_cloudi_service_http_elli_homepage = http://cloudi.org/
pkg_cloudi_service_http_elli_fetch = git
pkg_cloudi_service_http_elli_repo = https://github.com/CloudI/cloudi_service_http_elli
pkg_cloudi_service_http_elli_commit = master

PACKAGES += cloudi_service_map_reduce
pkg_cloudi_service_map_reduce_name = cloudi_service_map_reduce
pkg_cloudi_service_map_reduce_description = Map/Reduce CloudI Service
pkg_cloudi_service_map_reduce_homepage = http://cloudi.org/
pkg_cloudi_service_map_reduce_fetch = git
pkg_cloudi_service_map_reduce_repo = https://github.com/CloudI/cloudi_service_map_reduce
pkg_cloudi_service_map_reduce_commit = master

PACKAGES += cloudi_service_oauth1
pkg_cloudi_service_oauth1_name = cloudi_service_oauth1
pkg_cloudi_service_oauth1_description = OAuth v1.0 CloudI Service
pkg_cloudi_service_oauth1_homepage = http://cloudi.org/
pkg_cloudi_service_oauth1_fetch = git
pkg_cloudi_service_oauth1_repo = https://github.com/CloudI/cloudi_service_oauth1
pkg_cloudi_service_oauth1_commit = master

PACKAGES += cloudi_service_queue
pkg_cloudi_service_queue_name = cloudi_service_queue
pkg_cloudi_service_queue_description = Persistent Queue Service
pkg_cloudi_service_queue_homepage = http://cloudi.org/
pkg_cloudi_service_queue_fetch = git
pkg_cloudi_service_queue_repo = https://github.com/CloudI/cloudi_service_queue
pkg_cloudi_service_queue_commit = master

PACKAGES += cloudi_service_quorum
pkg_cloudi_service_quorum_name = cloudi_service_quorum
pkg_cloudi_service_quorum_description = CloudI Quorum Service
pkg_cloudi_service_quorum_homepage = http://cloudi.org/
pkg_cloudi_service_quorum_fetch = git
pkg_cloudi_service_quorum_repo = https://github.com/CloudI/cloudi_service_quorum
pkg_cloudi_service_quorum_commit = master

PACKAGES += cloudi_service_router
pkg_cloudi_service_router_name = cloudi_service_router
pkg_cloudi_service_router_description = CloudI Router Service
pkg_cloudi_service_router_homepage = http://cloudi.org/
pkg_cloudi_service_router_fetch = git
pkg_cloudi_service_router_repo = https://github.com/CloudI/cloudi_service_router
pkg_cloudi_service_router_commit = master

PACKAGES += cloudi_service_tcp
pkg_cloudi_service_tcp_name = cloudi_service_tcp
pkg_cloudi_service_tcp_description = TCP CloudI Service
pkg_cloudi_service_tcp_homepage = http://cloudi.org/
pkg_cloudi_service_tcp_fetch = git
pkg_cloudi_service_tcp_repo = https://github.com/CloudI/cloudi_service_tcp
pkg_cloudi_service_tcp_commit = master

PACKAGES += cloudi_service_timers
pkg_cloudi_service_timers_name = cloudi_service_timers
pkg_cloudi_service_timers_description = Timers CloudI Service
pkg_cloudi_service_timers_homepage = http://cloudi.org/
pkg_cloudi_service_timers_fetch = git
pkg_cloudi_service_timers_repo = https://github.com/CloudI/cloudi_service_timers
pkg_cloudi_service_timers_commit = master

PACKAGES += cloudi_service_udp
pkg_cloudi_service_udp_name = cloudi_service_udp
pkg_cloudi_service_udp_description = UDP CloudI Service
pkg_cloudi_service_udp_homepage = http://cloudi.org/
pkg_cloudi_service_udp_fetch = git
pkg_cloudi_service_udp_repo = https://github.com/CloudI/cloudi_service_udp
pkg_cloudi_service_udp_commit = master

PACKAGES += cloudi_service_validate
pkg_cloudi_service_validate_name = cloudi_service_validate
pkg_cloudi_service_validate_description = CloudI Validate Service
pkg_cloudi_service_validate_homepage = http://cloudi.org/
pkg_cloudi_service_validate_fetch = git
pkg_cloudi_service_validate_repo = https://github.com/CloudI/cloudi_service_validate
pkg_cloudi_service_validate_commit = master

PACKAGES += cloudi_service_zeromq
pkg_cloudi_service_zeromq_name = cloudi_service_zeromq
pkg_cloudi_service_zeromq_description = ZeroMQ CloudI Service
pkg_cloudi_service_zeromq_homepage = http://cloudi.org/
pkg_cloudi_service_zeromq_fetch = git
pkg_cloudi_service_zeromq_repo = https://github.com/CloudI/cloudi_service_zeromq
pkg_cloudi_service_zeromq_commit = master

PACKAGES += cluster_info
pkg_cluster_info_name = cluster_info
pkg_cluster_info_description = Fork of Hibari's nifty cluster_info OTP app
pkg_cluster_info_homepage = https://github.com/basho/cluster_info
pkg_cluster_info_fetch = git
pkg_cluster_info_repo = https://github.com/basho/cluster_info
pkg_cluster_info_commit = master

PACKAGES += color
pkg_color_name = color
pkg_color_description = ANSI colors for your Erlang
pkg_color_homepage = https://github.com/julianduque/erlang-color
pkg_color_fetch = git
pkg_color_repo = https://github.com/julianduque/erlang-color
pkg_color_commit = master

PACKAGES += confetti
pkg_confetti_name = confetti
pkg_confetti_description = Erlang configuration provider / application:get_env/2 on steroids
pkg_confetti_homepage = https://github.com/jtendo/confetti
pkg_confetti_fetch = git
pkg_confetti_repo = https://github.com/jtendo/confetti
pkg_confetti_commit = master

PACKAGES += couchbeam
pkg_couchbeam_name = couchbeam
pkg_couchbeam_description = Apache CouchDB client in Erlang
pkg_couchbeam_homepage = https://github.com/benoitc/couchbeam
pkg_couchbeam_fetch = git
pkg_couchbeam_repo = https://github.com/benoitc/couchbeam
pkg_couchbeam_commit = master

PACKAGES += couch
pkg_couch_name = couch
pkg_couch_description = A embeddable document oriented database compatible with Apache CouchDB
pkg_couch_homepage = https://github.com/benoitc/opencouch
pkg_couch_fetch = git
pkg_couch_repo = https://github.com/benoitc/opencouch
pkg_couch_commit = master

PACKAGES += covertool
pkg_covertool_name = covertool
pkg_covertool_description = Tool to convert Erlang cover data files into Cobertura XML reports
pkg_covertool_homepage = https://github.com/idubrov/covertool
pkg_covertool_fetch = git
pkg_covertool_repo = https://github.com/idubrov/covertool
pkg_covertool_commit = master

PACKAGES += cowboy
pkg_cowboy_name = cowboy
pkg_cowboy_description = Small, fast and modular HTTP server.
pkg_cowboy_homepage = http://ninenines.eu
pkg_cowboy_fetch = git
pkg_cowboy_repo = https://github.com/ninenines/cowboy
pkg_cowboy_commit = 1.0.1

PACKAGES += cowdb
pkg_cowdb_name = cowdb
pkg_cowdb_description = Pure Key/Value database library for Erlang Applications
pkg_cowdb_homepage = https://github.com/refuge/cowdb
pkg_cowdb_fetch = git
pkg_cowdb_repo = https://github.com/refuge/cowdb
pkg_cowdb_commit = master

PACKAGES += cowlib
pkg_cowlib_name = cowlib
pkg_cowlib_description = Support library for manipulating Web protocols.
pkg_cowlib_homepage = http://ninenines.eu
pkg_cowlib_fetch = git
pkg_cowlib_repo = https://github.com/ninenines/cowlib
pkg_cowlib_commit = 1.0.1

PACKAGES += cpg
pkg_cpg_name = cpg
pkg_cpg_description = CloudI Process Groups
pkg_cpg_homepage = https://github.com/okeuday/cpg
pkg_cpg_fetch = git
pkg_cpg_repo = https://github.com/okeuday/cpg
pkg_cpg_commit = master

PACKAGES += cqerl
pkg_cqerl_name = cqerl
pkg_cqerl_description = Native Erlang CQL client for Cassandra
pkg_cqerl_homepage = https://matehat.github.io/cqerl/
pkg_cqerl_fetch = git
pkg_cqerl_repo = https://github.com/matehat/cqerl
pkg_cqerl_commit = master

PACKAGES += cr
pkg_cr_name = cr
pkg_cr_description = Chain Replication
pkg_cr_homepage = https://synrc.com/apps/cr/doc/cr.htm
pkg_cr_fetch = git
pkg_cr_repo = https://github.com/spawnproc/cr
pkg_cr_commit = master

PACKAGES += cuttlefish
pkg_cuttlefish_name = cuttlefish
pkg_cuttlefish_description = never lose your childlike sense of wonder baby cuttlefish, promise me?
pkg_cuttlefish_homepage = https://github.com/basho/cuttlefish
pkg_cuttlefish_fetch = git
pkg_cuttlefish_repo = https://github.com/basho/cuttlefish
pkg_cuttlefish_commit = master

PACKAGES += damocles
pkg_damocles_name = damocles
pkg_damocles_description = Erlang library for generating adversarial network conditions for QAing distributed applications/systems on a single Linux box.
pkg_damocles_homepage = https://github.com/lostcolony/damocles
pkg_damocles_fetch = git
pkg_damocles_repo = https://github.com/lostcolony/damocles
pkg_damocles_commit = master

PACKAGES += debbie
pkg_debbie_name = debbie
pkg_debbie_description = .DEB Built In Erlang
pkg_debbie_homepage = https://github.com/crownedgrouse/debbie
pkg_debbie_fetch = git
pkg_debbie_repo = https://github.com/crownedgrouse/debbie
pkg_debbie_commit = master

PACKAGES += decimal
pkg_decimal_name = decimal
pkg_decimal_description = An Erlang decimal arithmetic library
pkg_decimal_homepage = https://github.com/tim/erlang-decimal
pkg_decimal_fetch = git
pkg_decimal_repo = https://github.com/tim/erlang-decimal
pkg_decimal_commit = master

PACKAGES += detergent
pkg_detergent_name = detergent
pkg_detergent_description = An emulsifying Erlang SOAP library
pkg_detergent_homepage = https://github.com/devinus/detergent
pkg_detergent_fetch = git
pkg_detergent_repo = https://github.com/devinus/detergent
pkg_detergent_commit = master

PACKAGES += detest
pkg_detest_name = detest
pkg_detest_description = Tool for running tests on a cluster of erlang nodes
pkg_detest_homepage = https://github.com/biokoda/detest
pkg_detest_fetch = git
pkg_detest_repo = https://github.com/biokoda/detest
pkg_detest_commit = master

PACKAGES += dh_date
pkg_dh_date_name = dh_date
pkg_dh_date_description = Date formatting / parsing library for erlang
pkg_dh_date_homepage = https://github.com/daleharvey/dh_date
pkg_dh_date_fetch = git
pkg_dh_date_repo = https://github.com/daleharvey/dh_date
pkg_dh_date_commit = master

PACKAGES += dhtcrawler
pkg_dhtcrawler_name = dhtcrawler
pkg_dhtcrawler_description = dhtcrawler is a DHT crawler written in erlang. It can join a DHT network and crawl many P2P torrents.
pkg_dhtcrawler_homepage = https://github.com/kevinlynx/dhtcrawler
pkg_dhtcrawler_fetch = git
pkg_dhtcrawler_repo = https://github.com/kevinlynx/dhtcrawler
pkg_dhtcrawler_commit = master

PACKAGES += dirbusterl
pkg_dirbusterl_name = dirbusterl
pkg_dirbusterl_description = DirBuster successor in Erlang
pkg_dirbusterl_homepage = https://github.com/silentsignal/DirBustErl
pkg_dirbusterl_fetch = git
pkg_dirbusterl_repo = https://github.com/silentsignal/DirBustErl
pkg_dirbusterl_commit = master

PACKAGES += dispcount
pkg_dispcount_name = dispcount
pkg_dispcount_description = Erlang task dispatcher based on ETS counters.
pkg_dispcount_homepage = https://github.com/ferd/dispcount
pkg_dispcount_fetch = git
pkg_dispcount_repo = https://github.com/ferd/dispcount
pkg_dispcount_commit = master

PACKAGES += dlhttpc
pkg_dlhttpc_name = dlhttpc
pkg_dlhttpc_description = dispcount-based lhttpc fork for massive amounts of requests to limited endpoints
pkg_dlhttpc_homepage = https://github.com/ferd/dlhttpc
pkg_dlhttpc_fetch = git
pkg_dlhttpc_repo = https://github.com/ferd/dlhttpc
pkg_dlhttpc_commit = master

PACKAGES += dns
pkg_dns_name = dns
pkg_dns_description = Erlang DNS library
pkg_dns_homepage = https://github.com/aetrion/dns_erlang
pkg_dns_fetch = git
pkg_dns_repo = https://github.com/aetrion/dns_erlang
pkg_dns_commit = master

PACKAGES += dnssd
pkg_dnssd_name = dnssd
pkg_dnssd_description = Erlang interface to Apple's Bonjour D    NS Service Discovery implementation
pkg_dnssd_homepage = https://github.com/benoitc/dnssd_erlang
pkg_dnssd_fetch = git
pkg_dnssd_repo = https://github.com/benoitc/dnssd_erlang
pkg_dnssd_commit = master

PACKAGES += dtl
pkg_dtl_name = dtl
pkg_dtl_description = Django Template Language: A full-featured port of the Django template engine to Erlang.
pkg_dtl_homepage = https://github.com/oinksoft/dtl
pkg_dtl_fetch = git
pkg_dtl_repo = https://github.com/oinksoft/dtl
pkg_dtl_commit = master

PACKAGES += dynamic_compile
pkg_dynamic_compile_name = dynamic_compile
pkg_dynamic_compile_description = compile and load erlang modules from string input
pkg_dynamic_compile_homepage = https://github.com/jkvor/dynamic_compile
pkg_dynamic_compile_fetch = git
pkg_dynamic_compile_repo = https://github.com/jkvor/dynamic_compile
pkg_dynamic_compile_commit = master

PACKAGES += e2
pkg_e2_name = e2
pkg_e2_description = Library to simply writing correct OTP applications.
pkg_e2_homepage = http://e2project.org
pkg_e2_fetch = git
pkg_e2_repo = https://github.com/gar1t/e2
pkg_e2_commit = master

PACKAGES += eamf
pkg_eamf_name = eamf
pkg_eamf_description = eAMF provides Action Message Format (AMF) support for Erlang
pkg_eamf_homepage = https://github.com/mrinalwadhwa/eamf
pkg_eamf_fetch = git
pkg_eamf_repo = https://github.com/mrinalwadhwa/eamf
pkg_eamf_commit = master

PACKAGES += eavro
pkg_eavro_name = eavro
pkg_eavro_description = Apache Avro encoder/decoder
pkg_eavro_homepage = https://github.com/SIfoxDevTeam/eavro
pkg_eavro_fetch = git
pkg_eavro_repo = https://github.com/SIfoxDevTeam/eavro
pkg_eavro_commit = master

PACKAGES += ecapnp
pkg_ecapnp_name = ecapnp
pkg_ecapnp_description = Cap'n Proto library for Erlang
pkg_ecapnp_homepage = https://github.com/kaos/ecapnp
pkg_ecapnp_fetch = git
pkg_ecapnp_repo = https://github.com/kaos/ecapnp
pkg_ecapnp_commit = master

PACKAGES += econfig
pkg_econfig_name = econfig
pkg_econfig_description = simple Erlang config handler using INI files
pkg_econfig_homepage = https://github.com/benoitc/econfig
pkg_econfig_fetch = git
pkg_econfig_repo = https://github.com/benoitc/econfig
pkg_econfig_commit = master

PACKAGES += edate
pkg_edate_name = edate
pkg_edate_description = date manipulation library for erlang
pkg_edate_homepage = https://github.com/dweldon/edate
pkg_edate_fetch = git
pkg_edate_repo = https://github.com/dweldon/edate
pkg_edate_commit = master

PACKAGES += edgar
pkg_edgar_name = edgar
pkg_edgar_description = Erlang Does GNU AR
pkg_edgar_homepage = https://github.com/crownedgrouse/edgar
pkg_edgar_fetch = git
pkg_edgar_repo = https://github.com/crownedgrouse/edgar
pkg_edgar_commit = master

PACKAGES += edis
pkg_edis_name = edis
pkg_edis_description = An Erlang implementation of Redis KV Store
pkg_edis_homepage = http://inaka.github.com/edis/
pkg_edis_fetch = git
pkg_edis_repo = https://github.com/inaka/edis
pkg_edis_commit = master

PACKAGES += edns
pkg_edns_name = edns
pkg_edns_description = Erlang/OTP DNS server
pkg_edns_homepage = https://github.com/hcvst/erlang-dns
pkg_edns_fetch = git
pkg_edns_repo = https://github.com/hcvst/erlang-dns
pkg_edns_commit = master

PACKAGES += edown
pkg_edown_name = edown
pkg_edown_description = EDoc extension for generating Github-flavored Markdown
pkg_edown_homepage = https://github.com/uwiger/edown
pkg_edown_fetch = git
pkg_edown_repo = https://github.com/uwiger/edown
pkg_edown_commit = master

PACKAGES += eep_app
pkg_eep_app_name = eep_app
pkg_eep_app_description = Embedded Event Processing
pkg_eep_app_homepage = https://github.com/darach/eep-erl
pkg_eep_app_fetch = git
pkg_eep_app_repo = https://github.com/darach/eep-erl
pkg_eep_app_commit = master

PACKAGES += eep
pkg_eep_name = eep
pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
pkg_eep_homepage = https://github.com/virtan/eep
pkg_eep_fetch = git
pkg_eep_repo = https://github.com/virtan/eep
pkg_eep_commit = master

PACKAGES += efene
pkg_efene_name = efene
pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
pkg_efene_homepage = https://github.com/efene/efene
pkg_efene_fetch = git
pkg_efene_repo = https://github.com/efene/efene
pkg_efene_commit = master

PACKAGES += eganglia
pkg_eganglia_name = eganglia
pkg_eganglia_description = Erlang library to interact with Ganglia
pkg_eganglia_homepage = https://github.com/inaka/eganglia
pkg_eganglia_fetch = git
pkg_eganglia_repo = https://github.com/inaka/eganglia
pkg_eganglia_commit = v0.9.1

PACKAGES += egeoip
pkg_egeoip_name = egeoip
pkg_egeoip_description = Erlang IP Geolocation module, currently supporting the MaxMind GeoLite City Database.
pkg_egeoip_homepage = https://github.com/mochi/egeoip
pkg_egeoip_fetch = git
pkg_egeoip_repo = https://github.com/mochi/egeoip
pkg_egeoip_commit = master

PACKAGES += ehsa
pkg_ehsa_name = ehsa
pkg_ehsa_description = Erlang HTTP server basic and digest authentication modules
pkg_ehsa_homepage = https://bitbucket.org/a12n/ehsa
pkg_ehsa_fetch = hg
pkg_ehsa_repo = https://bitbucket.org/a12n/ehsa
pkg_ehsa_commit = 2.0.4

PACKAGES += ejabberd
pkg_ejabberd_name = ejabberd
pkg_ejabberd_description = Robust, ubiquitous and massively scalable Jabber / XMPP Instant Messaging platform
pkg_ejabberd_homepage = https://github.com/processone/ejabberd
pkg_ejabberd_fetch = git
pkg_ejabberd_repo = https://github.com/processone/ejabberd
pkg_ejabberd_commit = master

PACKAGES += ej
pkg_ej_name = ej
pkg_ej_description = Helper module for working with Erlang terms representing JSON
pkg_ej_homepage = https://github.com/seth/ej
pkg_ej_fetch = git
pkg_ej_repo = https://github.com/seth/ej
pkg_ej_commit = master

PACKAGES += ejwt
pkg_ejwt_name = ejwt
pkg_ejwt_description = erlang library for JSON Web Token
pkg_ejwt_homepage = https://github.com/artefactop/ejwt
pkg_ejwt_fetch = git
pkg_ejwt_repo = https://github.com/artefactop/ejwt
pkg_ejwt_commit = master

PACKAGES += ekaf
pkg_ekaf_name = ekaf
pkg_ekaf_description = A minimal, high-performance Kafka client in Erlang.
pkg_ekaf_homepage = https://github.com/helpshift/ekaf
pkg_ekaf_fetch = git
pkg_ekaf_repo = https://github.com/helpshift/ekaf
pkg_ekaf_commit = master

PACKAGES += elarm
pkg_elarm_name = elarm
pkg_elarm_description = Alarm Manager for Erlang.
pkg_elarm_homepage = https://github.com/esl/elarm
pkg_elarm_fetch = git
pkg_elarm_repo = https://github.com/esl/elarm
pkg_elarm_commit = master

PACKAGES += eleveldb
pkg_eleveldb_name = eleveldb
pkg_eleveldb_description = Erlang LevelDB API
pkg_eleveldb_homepage = https://github.com/basho/eleveldb
pkg_eleveldb_fetch = git
pkg_eleveldb_repo = https://github.com/basho/eleveldb
pkg_eleveldb_commit = master

PACKAGES += elli
pkg_elli_name = elli
pkg_elli_description = Simple, robust and performant Erlang web server
pkg_elli_homepage = https://github.com/knutin/elli
pkg_elli_fetch = git
pkg_elli_repo = https://github.com/knutin/elli
pkg_elli_commit = master

PACKAGES += elvis
pkg_elvis_name = elvis
pkg_elvis_description = Erlang Style Reviewer
pkg_elvis_homepage = https://github.com/inaka/elvis
pkg_elvis_fetch = git
pkg_elvis_repo = https://github.com/inaka/elvis
pkg_elvis_commit = 0.2.4

PACKAGES += emagick
pkg_emagick_name = emagick
pkg_emagick_description = Wrapper for Graphics/ImageMagick command line tool.
pkg_emagick_homepage = https://github.com/kivra/emagick
pkg_emagick_fetch = git
pkg_emagick_repo = https://github.com/kivra/emagick
pkg_emagick_commit = master

PACKAGES += emysql
pkg_emysql_name = emysql
pkg_emysql_description = Stable, pure Erlang MySQL driver.
pkg_emysql_homepage = https://github.com/Eonblast/Emysql
pkg_emysql_fetch = git
pkg_emysql_repo = https://github.com/Eonblast/Emysql
pkg_emysql_commit = master

PACKAGES += enm
pkg_enm_name = enm
pkg_enm_description = Erlang driver for nanomsg
pkg_enm_homepage = https://github.com/basho/enm
pkg_enm_fetch = git
pkg_enm_repo = https://github.com/basho/enm
pkg_enm_commit = master

PACKAGES += entop
pkg_entop_name = entop
pkg_entop_description = A top-like tool for monitoring an Erlang node
pkg_entop_homepage = https://github.com/mazenharake/entop
pkg_entop_fetch = git
pkg_entop_repo = https://github.com/mazenharake/entop
pkg_entop_commit = master

PACKAGES += epcap
pkg_epcap_name = epcap
pkg_epcap_description = Erlang packet capture interface using pcap
pkg_epcap_homepage = https://github.com/msantos/epcap
pkg_epcap_fetch = git
pkg_epcap_repo = https://github.com/msantos/epcap
pkg_epcap_commit = master

PACKAGES += eper
pkg_eper_name = eper
pkg_eper_description = Erlang performance and debugging tools.
pkg_eper_homepage = https://github.com/massemanet/eper
pkg_eper_fetch = git
pkg_eper_repo = https://github.com/massemanet/eper
pkg_eper_commit = master

PACKAGES += epgsql
pkg_epgsql_name = epgsql
pkg_epgsql_description = Erlang PostgreSQL client library.
pkg_epgsql_homepage = https://github.com/epgsql/epgsql
pkg_epgsql_fetch = git
pkg_epgsql_repo = https://github.com/epgsql/epgsql
pkg_epgsql_commit = master

PACKAGES += episcina
pkg_episcina_name = episcina
pkg_episcina_description = A simple non intrusive resource pool for connections
pkg_episcina_homepage = https://github.com/erlware/episcina
pkg_episcina_fetch = git
pkg_episcina_repo = https://github.com/erlware/episcina
pkg_episcina_commit = master

PACKAGES += eplot
pkg_eplot_name = eplot
pkg_eplot_description = A plot engine written in erlang.
pkg_eplot_homepage = https://github.com/psyeugenic/eplot
pkg_eplot_fetch = git
pkg_eplot_repo = https://github.com/psyeugenic/eplot
pkg_eplot_commit = master

PACKAGES += epocxy
pkg_epocxy_name = epocxy
pkg_epocxy_description = Erlang Patterns of Concurrency
pkg_epocxy_homepage = https://github.com/duomark/epocxy
pkg_epocxy_fetch = git
pkg_epocxy_repo = https://github.com/duomark/epocxy
pkg_epocxy_commit = master

PACKAGES += epubnub
pkg_epubnub_name = epubnub
pkg_epubnub_description = Erlang PubNub API
pkg_epubnub_homepage = https://github.com/tsloughter/epubnub
pkg_epubnub_fetch = git
pkg_epubnub_repo = https://github.com/tsloughter/epubnub
pkg_epubnub_commit = master

PACKAGES += eqm
pkg_eqm_name = eqm
pkg_eqm_description = Erlang pub sub with supply-demand channels
pkg_eqm_homepage = https://github.com/loucash/eqm
pkg_eqm_fetch = git
pkg_eqm_repo = https://github.com/loucash/eqm
pkg_eqm_commit = master

PACKAGES += eredis
pkg_eredis_name = eredis
pkg_eredis_description = Erlang Redis client
pkg_eredis_homepage = https://github.com/wooga/eredis
pkg_eredis_fetch = git
pkg_eredis_repo = https://github.com/wooga/eredis
pkg_eredis_commit = master

PACKAGES += eredis_pool
pkg_eredis_pool_name = eredis_pool
pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
pkg_eredis_pool_homepage = https://github.com/hiroeorz/eredis_pool
pkg_eredis_pool_fetch = git
pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
pkg_eredis_pool_commit = master

PACKAGES += erlang_cep
pkg_erlang_cep_name = erlang_cep
pkg_erlang_cep_description = A basic CEP package written in erlang
pkg_erlang_cep_homepage = https://github.com/danmacklin/erlang_cep
pkg_erlang_cep_fetch = git
pkg_erlang_cep_repo = https://github.com/danmacklin/erlang_cep
pkg_erlang_cep_commit = master

PACKAGES += erlang_js
pkg_erlang_js_name = erlang_js
pkg_erlang_js_description = A linked-in driver for Erlang to Mozilla's Spidermonkey Javascript runtime.
pkg_erlang_js_homepage = https://github.com/basho/erlang_js
pkg_erlang_js_fetch = git
pkg_erlang_js_repo = https://github.com/basho/erlang_js
pkg_erlang_js_commit = master

PACKAGES += erlang_localtime
pkg_erlang_localtime_name = erlang_localtime
pkg_erlang_localtime_description = Erlang library for conversion from one local time to another
pkg_erlang_localtime_homepage = https://github.com/dmitryme/erlang_localtime
pkg_erlang_localtime_fetch = git
pkg_erlang_localtime_repo = https://github.com/dmitryme/erlang_localtime
pkg_erlang_localtime_commit = master

PACKAGES += erlang_smtp
pkg_erlang_smtp_name = erlang_smtp
pkg_erlang_smtp_description = Erlang SMTP and POP3 server code.
pkg_erlang_smtp_homepage = https://github.com/tonyg/erlang-smtp
pkg_erlang_smtp_fetch = git
pkg_erlang_smtp_repo = https://github.com/tonyg/erlang-smtp
pkg_erlang_smtp_commit = master

PACKAGES += erlang_term
pkg_erlang_term_name = erlang_term
pkg_erlang_term_description = Erlang Term Info
pkg_erlang_term_homepage = https://github.com/okeuday/erlang_term
pkg_erlang_term_fetch = git
pkg_erlang_term_repo = https://github.com/okeuday/erlang_term
pkg_erlang_term_commit = master

PACKAGES += erlastic_search
pkg_erlastic_search_name = erlastic_search
pkg_erlastic_search_description = An Erlang app for communicating with Elastic Search's rest interface.
pkg_erlastic_search_homepage = https://github.com/tsloughter/erlastic_search
pkg_erlastic_search_fetch = git
pkg_erlastic_search_repo = https://github.com/tsloughter/erlastic_search
pkg_erlastic_search_commit = master

PACKAGES += erlasticsearch
pkg_erlasticsearch_name = erlasticsearch
pkg_erlasticsearch_description = Erlang thrift interface to elastic_search
pkg_erlasticsearch_homepage = https://github.com/dieswaytoofast/erlasticsearch
pkg_erlasticsearch_fetch = git
pkg_erlasticsearch_repo = https://github.com/dieswaytoofast/erlasticsearch
pkg_erlasticsearch_commit = master

PACKAGES += erlbrake
pkg_erlbrake_name = erlbrake
pkg_erlbrake_description = Erlang Airbrake notification client
pkg_erlbrake_homepage = https://github.com/kenpratt/erlbrake
pkg_erlbrake_fetch = git
pkg_erlbrake_repo = https://github.com/kenpratt/erlbrake
pkg_erlbrake_commit = master

PACKAGES += erlcloud
pkg_erlcloud_name = erlcloud
pkg_erlcloud_description = Cloud Computing library for erlang (Amazon EC2, S3, SQS, SimpleDB, Mechanical Turk, ELB)
pkg_erlcloud_homepage = https://github.com/gleber/erlcloud
pkg_erlcloud_fetch = git
pkg_erlcloud_repo = https://github.com/gleber/erlcloud
pkg_erlcloud_commit = master

PACKAGES += erlcron
pkg_erlcron_name = erlcron
pkg_erlcron_description = Erlang cronish system
pkg_erlcron_homepage = https://github.com/erlware/erlcron
pkg_erlcron_fetch = git
pkg_erlcron_repo = https://github.com/erlware/erlcron
pkg_erlcron_commit = master

PACKAGES += erldb
pkg_erldb_name = erldb
pkg_erldb_description = ORM (Object-relational mapping) application implemented in Erlang
pkg_erldb_homepage = http://erldb.org
pkg_erldb_fetch = git
pkg_erldb_repo = https://github.com/erldb/erldb
pkg_erldb_commit = master

PACKAGES += erldis
pkg_erldis_name = erldis
pkg_erldis_description = redis erlang client library
pkg_erldis_homepage = https://github.com/cstar/erldis
pkg_erldis_fetch = git
pkg_erldis_repo = https://github.com/cstar/erldis
pkg_erldis_commit = master

PACKAGES += erldns
pkg_erldns_name = erldns
pkg_erldns_description = DNS server, in erlang.
pkg_erldns_homepage = https://github.com/aetrion/erl-dns
pkg_erldns_fetch = git
pkg_erldns_repo = https://github.com/aetrion/erl-dns
pkg_erldns_commit = master

PACKAGES += erldocker
pkg_erldocker_name = erldocker
pkg_erldocker_description = Docker Remote API client for Erlang
pkg_erldocker_homepage = https://github.com/proger/erldocker
pkg_erldocker_fetch = git
pkg_erldocker_repo = https://github.com/proger/erldocker
pkg_erldocker_commit = master

PACKAGES += erlfsmon
pkg_erlfsmon_name = erlfsmon
pkg_erlfsmon_description = Erlang filesystem event watcher for Linux and OSX
pkg_erlfsmon_homepage = https://github.com/proger/erlfsmon
pkg_erlfsmon_fetch = git
pkg_erlfsmon_repo = https://github.com/proger/erlfsmon
pkg_erlfsmon_commit = master

PACKAGES += erlgit
pkg_erlgit_name = erlgit
pkg_erlgit_description = Erlang convenience wrapper around git executable
pkg_erlgit_homepage = https://github.com/gleber/erlgit
pkg_erlgit_fetch = git
pkg_erlgit_repo = https://github.com/gleber/erlgit
pkg_erlgit_commit = master

PACKAGES += erlguten
pkg_erlguten_name = erlguten
pkg_erlguten_description = ErlGuten is a system for high-quality typesetting, written purely in Erlang.
pkg_erlguten_homepage = https://github.com/richcarl/erlguten
pkg_erlguten_fetch = git
pkg_erlguten_repo = https://github.com/richcarl/erlguten
pkg_erlguten_commit = master

PACKAGES += erlmc
pkg_erlmc_name = erlmc
pkg_erlmc_description = Erlang memcached binary protocol client
pkg_erlmc_homepage = https://github.com/jkvor/erlmc
pkg_erlmc_fetch = git
pkg_erlmc_repo = https://github.com/jkvor/erlmc
pkg_erlmc_commit = master

PACKAGES += erlmongo
pkg_erlmongo_name = erlmongo
pkg_erlmongo_description = Record based Erlang driver for MongoDB with gridfs support
pkg_erlmongo_homepage = https://github.com/SergejJurecko/erlmongo
pkg_erlmongo_fetch = git
pkg_erlmongo_repo = https://github.com/SergejJurecko/erlmongo
pkg_erlmongo_commit = master

PACKAGES += erlog
pkg_erlog_name = erlog
pkg_erlog_description = Prolog interpreter in and for Erlang
pkg_erlog_homepage = https://github.com/rvirding/erlog
pkg_erlog_fetch = git
pkg_erlog_repo = https://github.com/rvirding/erlog
pkg_erlog_commit = master

PACKAGES += erlpass
pkg_erlpass_name = erlpass
pkg_erlpass_description = A library to handle password hashing and changing in a safe manner, independent from any kind of storage whatsoever.
pkg_erlpass_homepage = https://github.com/ferd/erlpass
pkg_erlpass_fetch = git
pkg_erlpass_repo = https://github.com/ferd/erlpass
pkg_erlpass_commit = master

PACKAGES += erlport
pkg_erlport_name = erlport
pkg_erlport_description = ErlPort - connect Erlang to other languages
pkg_erlport_homepage = https://github.com/hdima/erlport
pkg_erlport_fetch = git
pkg_erlport_repo = https://github.com/hdima/erlport
pkg_erlport_commit = master

PACKAGES += erlsha2
pkg_erlsha2_name = erlsha2
pkg_erlsha2_description = SHA-224, SHA-256, SHA-384, SHA-512 implemented in Erlang NIFs.
pkg_erlsha2_homepage = https://github.com/vinoski/erlsha2
pkg_erlsha2_fetch = git
pkg_erlsha2_repo = https://github.com/vinoski/erlsha2
pkg_erlsha2_commit = master

PACKAGES += erlsh
pkg_erlsh_name = erlsh
pkg_erlsh_description = Erlang shell tools
pkg_erlsh_homepage = https://github.com/proger/erlsh
pkg_erlsh_fetch = git
pkg_erlsh_repo = https://github.com/proger/erlsh
pkg_erlsh_commit = master

PACKAGES += erlsom
pkg_erlsom_name = erlsom
pkg_erlsom_description = XML parser for Erlang
pkg_erlsom_homepage = https://github.com/willemdj/erlsom
pkg_erlsom_fetch = git
pkg_erlsom_repo = https://github.com/willemdj/erlsom
pkg_erlsom_commit = master

PACKAGES += erl_streams
pkg_erl_streams_name = erl_streams
pkg_erl_streams_description = Streams in Erlang
pkg_erl_streams_homepage = https://github.com/epappas/erl_streams
pkg_erl_streams_fetch = git
pkg_erl_streams_repo = https://github.com/epappas/erl_streams
pkg_erl_streams_commit = master

PACKAGES += erlubi
pkg_erlubi_name = erlubi
pkg_erlubi_description = Ubigraph Erlang Client (and Process Visualizer)
pkg_erlubi_homepage = https://github.com/krestenkrab/erlubi
pkg_erlubi_fetch = git
pkg_erlubi_repo = https://github.com/krestenkrab/erlubi
pkg_erlubi_commit = master

PACKAGES += erlvolt
pkg_erlvolt_name = erlvolt
pkg_erlvolt_description = VoltDB Erlang Client Driver
pkg_erlvolt_homepage = https://github.com/VoltDB/voltdb-client-erlang
pkg_erlvolt_fetch = git
pkg_erlvolt_repo = https://github.com/VoltDB/voltdb-client-erlang
pkg_erlvolt_commit = master

PACKAGES += erlware_commons
pkg_erlware_commons_name = erlware_commons
pkg_erlware_commons_description = Erlware Commons is an Erlware project focused on all aspects of reusable Erlang components.
pkg_erlware_commons_homepage = https://github.com/erlware/erlware_commons
pkg_erlware_commons_fetch = git
pkg_erlware_commons_repo = https://github.com/erlware/erlware_commons
pkg_erlware_commons_commit = master

PACKAGES += erlydtl
pkg_erlydtl_name = erlydtl
pkg_erlydtl_description = Django Template Language for Erlang.
pkg_erlydtl_homepage = https://github.com/erlydtl/erlydtl
pkg_erlydtl_fetch = git
pkg_erlydtl_repo = https://github.com/erlydtl/erlydtl
pkg_erlydtl_commit = master

PACKAGES += errd
pkg_errd_name = errd
pkg_errd_description = Erlang RRDTool library
pkg_errd_homepage = https://github.com/archaelus/errd
pkg_errd_fetch = git
pkg_errd_repo = https://github.com/archaelus/errd
pkg_errd_commit = master

PACKAGES += erserve
pkg_erserve_name = erserve
pkg_erserve_description = Erlang/Rserve communication interface
pkg_erserve_homepage = https://github.com/del/erserve
pkg_erserve_fetch = git
pkg_erserve_repo = https://github.com/del/erserve
pkg_erserve_commit = master

PACKAGES += erwa
pkg_erwa_name = erwa
pkg_erwa_description = A WAMP router and client written in Erlang.
pkg_erwa_homepage = https://github.com/bwegh/erwa
pkg_erwa_fetch = git
pkg_erwa_repo = https://github.com/bwegh/erwa
pkg_erwa_commit = 0.1.1

PACKAGES += espec
pkg_espec_name = espec
pkg_espec_description = ESpec: Behaviour driven development framework for Erlang
pkg_espec_homepage = https://github.com/lucaspiller/espec
pkg_espec_fetch = git
pkg_espec_repo = https://github.com/lucaspiller/espec
pkg_espec_commit = master

PACKAGES += estatsd
pkg_estatsd_name = estatsd
pkg_estatsd_description = Erlang stats aggregation app that periodically flushes data to graphite
pkg_estatsd_homepage = https://github.com/RJ/estatsd
pkg_estatsd_fetch = git
pkg_estatsd_repo = https://github.com/RJ/estatsd
pkg_estatsd_commit = master

PACKAGES += etap
pkg_etap_name = etap
pkg_etap_description = etap is a simple erlang testing library that provides TAP compliant output.
pkg_etap_homepage = https://github.com/ngerakines/etap
pkg_etap_fetch = git
pkg_etap_repo = https://github.com/ngerakines/etap
pkg_etap_commit = master

PACKAGES += etest_http
pkg_etest_http_name = etest_http
pkg_etest_http_description = etest Assertions around HTTP (client-side)
pkg_etest_http_homepage = https://github.com/wooga/etest_http
pkg_etest_http_fetch = git
pkg_etest_http_repo = https://github.com/wooga/etest_http
pkg_etest_http_commit = master

PACKAGES += etest
pkg_etest_name = etest
pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
pkg_etest_homepage = https://github.com/wooga/etest
pkg_etest_fetch = git
pkg_etest_repo = https://github.com/wooga/etest
pkg_etest_commit = master

PACKAGES += etoml
pkg_etoml_name = etoml
pkg_etoml_description = TOML language erlang parser
pkg_etoml_homepage = https://github.com/kalta/etoml
pkg_etoml_fetch = git
pkg_etoml_repo = https://github.com/kalta/etoml
pkg_etoml_commit = master

PACKAGES += eunit_formatters
pkg_eunit_formatters_name = eunit_formatters
pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
pkg_eunit_formatters_homepage = https://github.com/seancribbs/eunit_formatters
pkg_eunit_formatters_fetch = git
pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
pkg_eunit_formatters_commit = master

PACKAGES += eunit
pkg_eunit_name = eunit
pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
pkg_eunit_homepage = https://github.com/richcarl/eunit
pkg_eunit_fetch = git
pkg_eunit_repo = https://github.com/richcarl/eunit
pkg_eunit_commit = master

PACKAGES += euthanasia
pkg_euthanasia_name = euthanasia
pkg_euthanasia_description = Merciful killer for your Erlang processes
pkg_euthanasia_homepage = https://github.com/doubleyou/euthanasia
pkg_euthanasia_fetch = git
pkg_euthanasia_repo = https://github.com/doubleyou/euthanasia
pkg_euthanasia_commit = master

PACKAGES += evum
pkg_evum_name = evum
pkg_evum_description = Spawn Linux VMs as Erlang processes in the Erlang VM
pkg_evum_homepage = https://github.com/msantos/evum
pkg_evum_fetch = git
pkg_evum_repo = https://github.com/msantos/evum
pkg_evum_commit = master

PACKAGES += exec
pkg_exec_name = exec
pkg_exec_description = Execute and control OS processes from Erlang/OTP.
pkg_exec_homepage = http://saleyn.github.com/erlexec
pkg_exec_fetch = git
pkg_exec_repo = https://github.com/saleyn/erlexec
pkg_exec_commit = master

PACKAGES += exml
pkg_exml_name = exml
pkg_exml_description = XML parsing library in Erlang
pkg_exml_homepage = https://github.com/paulgray/exml
pkg_exml_fetch = git
pkg_exml_repo = https://github.com/paulgray/exml
pkg_exml_commit = master

PACKAGES += exometer
pkg_exometer_name = exometer
pkg_exometer_description = Basic measurement objects and probe behavior
pkg_exometer_homepage = https://github.com/Feuerlabs/exometer
pkg_exometer_fetch = git
pkg_exometer_repo = https://github.com/Feuerlabs/exometer
pkg_exometer_commit = 1.2

PACKAGES += exs1024
pkg_exs1024_name = exs1024
pkg_exs1024_description = Xorshift1024star pseudo random number generator for Erlang.
pkg_exs1024_homepage = https://github.com/jj1bdx/exs1024
pkg_exs1024_fetch = git
pkg_exs1024_repo = https://github.com/jj1bdx/exs1024
pkg_exs1024_commit = master

PACKAGES += exs64
pkg_exs64_name = exs64
pkg_exs64_description = Xorshift64star pseudo random number generator for Erlang.
pkg_exs64_homepage = https://github.com/jj1bdx/exs64
pkg_exs64_fetch = git
pkg_exs64_repo = https://github.com/jj1bdx/exs64
pkg_exs64_commit = master

PACKAGES += exsplus116
pkg_exsplus116_name = exsplus116
pkg_exsplus116_description = Xorshift116plus for Erlang
pkg_exsplus116_homepage = https://github.com/jj1bdx/exsplus116
pkg_exsplus116_fetch = git
pkg_exsplus116_repo = https://github.com/jj1bdx/exsplus116
pkg_exsplus116_commit = master

PACKAGES += exsplus128
pkg_exsplus128_name = exsplus128
pkg_exsplus128_description = Xorshift128plus pseudo random number generator for Erlang.
pkg_exsplus128_homepage = https://github.com/jj1bdx/exsplus128
pkg_exsplus128_fetch = git
pkg_exsplus128_repo = https://github.com/jj1bdx/exsplus128
pkg_exsplus128_commit = master

PACKAGES += ezmq
pkg_ezmq_name = ezmq
pkg_ezmq_description = zMQ implemented in Erlang
pkg_ezmq_homepage = https://github.com/RoadRunnr/ezmq
pkg_ezmq_fetch = git
pkg_ezmq_repo = https://github.com/RoadRunnr/ezmq
pkg_ezmq_commit = master

PACKAGES += ezmtp
pkg_ezmtp_name = ezmtp
pkg_ezmtp_description = ZMTP protocol in pure Erlang.
pkg_ezmtp_homepage = https://github.com/a13x/ezmtp
pkg_ezmtp_fetch = git
pkg_ezmtp_repo = https://github.com/a13x/ezmtp
pkg_ezmtp_commit = master

PACKAGES += fast_disk_log
pkg_fast_disk_log_name = fast_disk_log
pkg_fast_disk_log_description = Pool-based asynchronous Erlang disk logger
pkg_fast_disk_log_homepage = https://github.com/lpgauth/fast_disk_log
pkg_fast_disk_log_fetch = git
pkg_fast_disk_log_repo = https://github.com/lpgauth/fast_disk_log
pkg_fast_disk_log_commit = master

PACKAGES += feeder
pkg_feeder_name = feeder
pkg_feeder_description = Stream parse RSS and Atom formatted XML feeds.
pkg_feeder_homepage = https://github.com/michaelnisi/feeder
pkg_feeder_fetch = git
pkg_feeder_repo = https://github.com/michaelnisi/feeder
pkg_feeder_commit = v1.4.6

PACKAGES += fix
pkg_fix_name = fix
pkg_fix_description = http://fixprotocol.org/ implementation.
pkg_fix_homepage = https://github.com/maxlapshin/fix
pkg_fix_fetch = git
pkg_fix_repo = https://github.com/maxlapshin/fix
pkg_fix_commit = master

PACKAGES += flower
pkg_flower_name = flower
pkg_flower_description = FlowER - a Erlang OpenFlow development platform
pkg_flower_homepage = https://github.com/travelping/flower
pkg_flower_fetch = git
pkg_flower_repo = https://github.com/travelping/flower
pkg_flower_commit = master

PACKAGES += fn
pkg_fn_name = fn
pkg_fn_description = Function utilities for Erlang
pkg_fn_homepage = https://github.com/reiddraper/fn
pkg_fn_fetch = git
pkg_fn_repo = https://github.com/reiddraper/fn
pkg_fn_commit = master

PACKAGES += folsom_cowboy
pkg_folsom_cowboy_name = folsom_cowboy
pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
pkg_folsom_cowboy_homepage = https://github.com/boundary/folsom_cowboy
pkg_folsom_cowboy_fetch = git
pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
pkg_folsom_cowboy_commit = master

PACKAGES += folsomite
pkg_folsomite_name = folsomite
pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
pkg_folsomite_homepage = https://github.com/campanja/folsomite
pkg_folsomite_fetch = git
pkg_folsomite_repo = https://github.com/campanja/folsomite
pkg_folsomite_commit = master

PACKAGES += folsom
pkg_folsom_name = folsom
pkg_folsom_description = Expose Erlang Events and Metrics
pkg_folsom_homepage = https://github.com/boundary/folsom
pkg_folsom_fetch = git
pkg_folsom_repo = https://github.com/boundary/folsom
pkg_folsom_commit = master

PACKAGES += fs
pkg_fs_name = fs
pkg_fs_description = Erlang FileSystem Listener
pkg_fs_homepage = https://github.com/synrc/fs
pkg_fs_fetch = git
pkg_fs_repo = https://github.com/synrc/fs
pkg_fs_commit = master

PACKAGES += fuse
pkg_fuse_name = fuse
pkg_fuse_description = A Circuit Breaker for Erlang
pkg_fuse_homepage = https://github.com/jlouis/fuse
pkg_fuse_fetch = git
pkg_fuse_repo = https://github.com/jlouis/fuse
pkg_fuse_commit = master

PACKAGES += gcm
pkg_gcm_name = gcm
pkg_gcm_description = An Erlang application for Google Cloud Messaging
pkg_gcm_homepage = https://github.com/pdincau/gcm-erlang
pkg_gcm_fetch = git
pkg_gcm_repo = https://github.com/pdincau/gcm-erlang
pkg_gcm_commit = master

PACKAGES += gcprof
pkg_gcprof_name = gcprof
pkg_gcprof_description = Garbage Collection profiler for Erlang
pkg_gcprof_homepage = https://github.com/knutin/gcprof
pkg_gcprof_fetch = git
pkg_gcprof_repo = https://github.com/knutin/gcprof
pkg_gcprof_commit = master

PACKAGES += geas
pkg_geas_name = geas
pkg_geas_description = Guess Erlang Application Scattering
pkg_geas_homepage = https://github.com/crownedgrouse/geas
pkg_geas_fetch = git
pkg_geas_repo = https://github.com/crownedgrouse/geas
pkg_geas_commit = master

PACKAGES += geef
pkg_geef_name = geef
pkg_geef_description = Git NEEEEF (Erlang NIF)
pkg_geef_homepage = https://github.com/carlosmn/geef
pkg_geef_fetch = git
pkg_geef_repo = https://github.com/carlosmn/geef
pkg_geef_commit = master

PACKAGES += gen_cycle
pkg_gen_cycle_name = gen_cycle
pkg_gen_cycle_description = Simple, generic OTP behaviour for recurring tasks
pkg_gen_cycle_homepage = https://github.com/aerosol/gen_cycle
pkg_gen_cycle_fetch = git
pkg_gen_cycle_repo = https://github.com/aerosol/gen_cycle
pkg_gen_cycle_commit = develop

PACKAGES += gen_icmp
pkg_gen_icmp_name = gen_icmp
pkg_gen_icmp_description = Erlang interface to ICMP sockets
pkg_gen_icmp_homepage = https://github.com/msantos/gen_icmp
pkg_gen_icmp_fetch = git
pkg_gen_icmp_repo = https://github.com/msantos/gen_icmp
pkg_gen_icmp_commit = master

PACKAGES += gen_nb_server
pkg_gen_nb_server_name = gen_nb_server
pkg_gen_nb_server_description = OTP behavior for writing non-blocking servers
pkg_gen_nb_server_homepage = https://github.com/kevsmith/gen_nb_server
pkg_gen_nb_server_fetch = git
pkg_gen_nb_server_repo = https://github.com/kevsmith/gen_nb_server
pkg_gen_nb_server_commit = master

PACKAGES += gen_paxos
pkg_gen_paxos_name = gen_paxos
pkg_gen_paxos_description = An Erlang/OTP-style implementation of the PAXOS distributed consensus protocol
pkg_gen_paxos_homepage = https://github.com/gburd/gen_paxos
pkg_gen_paxos_fetch = git
pkg_gen_paxos_repo = https://github.com/gburd/gen_paxos
pkg_gen_paxos_commit = master

PACKAGES += gen_smtp
pkg_gen_smtp_name = gen_smtp
pkg_gen_smtp_description = A generic Erlang SMTP server and client that can be extended via callback modules
pkg_gen_smtp_homepage = https://github.com/Vagabond/gen_smtp
pkg_gen_smtp_fetch = git
pkg_gen_smtp_repo = https://github.com/Vagabond/gen_smtp
pkg_gen_smtp_commit = master

PACKAGES += gen_tracker
pkg_gen_tracker_name = gen_tracker
pkg_gen_tracker_description = supervisor with ets handling of children and their metadata
pkg_gen_tracker_homepage = https://github.com/erlyvideo/gen_tracker
pkg_gen_tracker_fetch = git
pkg_gen_tracker_repo = https://github.com/erlyvideo/gen_tracker
pkg_gen_tracker_commit = master

PACKAGES += gen_unix
pkg_gen_unix_name = gen_unix
pkg_gen_unix_description = Erlang Unix socket interface
pkg_gen_unix_homepage = https://github.com/msantos/gen_unix
pkg_gen_unix_fetch = git
pkg_gen_unix_repo = https://github.com/msantos/gen_unix
pkg_gen_unix_commit = master

PACKAGES += getopt
pkg_getopt_name = getopt
pkg_getopt_description = Module to parse command line arguments using the GNU getopt syntax
pkg_getopt_homepage = https://github.com/jcomellas/getopt
pkg_getopt_fetch = git
pkg_getopt_repo = https://github.com/jcomellas/getopt
pkg_getopt_commit = master

PACKAGES += gettext
pkg_gettext_name = gettext
pkg_gettext_description = Erlang internationalization library.
pkg_gettext_homepage = https://github.com/etnt/gettext
pkg_gettext_fetch = git
pkg_gettext_repo = https://github.com/etnt/gettext
pkg_gettext_commit = master

PACKAGES += giallo
pkg_giallo_name = giallo
pkg_giallo_description = Small and flexible web framework on top of Cowboy
pkg_giallo_homepage = https://github.com/kivra/giallo
pkg_giallo_fetch = git
pkg_giallo_repo = https://github.com/kivra/giallo
pkg_giallo_commit = master

PACKAGES += gin
pkg_gin_name = gin
pkg_gin_description = The guards  and  for Erlang parse_transform
pkg_gin_homepage = https://github.com/mad-cocktail/gin
pkg_gin_fetch = git
pkg_gin_repo = https://github.com/mad-cocktail/gin
pkg_gin_commit = master

PACKAGES += gitty
pkg_gitty_name = gitty
pkg_gitty_description = Git access in erlang
pkg_gitty_homepage = https://github.com/maxlapshin/gitty
pkg_gitty_fetch = git
pkg_gitty_repo = https://github.com/maxlapshin/gitty
pkg_gitty_commit = master

PACKAGES += gold_fever
pkg_gold_fever_name = gold_fever
pkg_gold_fever_description = A Treasure Hunt for Erlangers
pkg_gold_fever_homepage = https://github.com/inaka/gold_fever
pkg_gold_fever_fetch = git
pkg_gold_fever_repo = https://github.com/inaka/gold_fever
pkg_gold_fever_commit = master

PACKAGES += gossiperl
pkg_gossiperl_name = gossiperl
pkg_gossiperl_description = Gossip middleware in Erlang
pkg_gossiperl_homepage = http://gossiperl.com/
pkg_gossiperl_fetch = git
pkg_gossiperl_repo = https://github.com/gossiperl/gossiperl
pkg_gossiperl_commit = master

PACKAGES += gpb
pkg_gpb_name = gpb
pkg_gpb_description = A Google Protobuf implementation for Erlang
pkg_gpb_homepage = https://github.com/tomas-abrahamsson/gpb
pkg_gpb_fetch = git
pkg_gpb_repo = https://github.com/tomas-abrahamsson/gpb
pkg_gpb_commit = master

PACKAGES += gproc
pkg_gproc_name = gproc
pkg_gproc_description = Extended process registry for Erlang
pkg_gproc_homepage = https://github.com/uwiger/gproc
pkg_gproc_fetch = git
pkg_gproc_repo = https://github.com/uwiger/gproc
pkg_gproc_commit = master

PACKAGES += grapherl
pkg_grapherl_name = grapherl
pkg_grapherl_description = Create graphs of Erlang systems and programs
pkg_grapherl_homepage = https://github.com/eproxus/grapherl
pkg_grapherl_fetch = git
pkg_grapherl_repo = https://github.com/eproxus/grapherl
pkg_grapherl_commit = master

PACKAGES += gun
pkg_gun_name = gun
pkg_gun_description = Asynchronous SPDY, HTTP and Websocket client written in Erlang.
pkg_gun_homepage = http//ninenines.eu
pkg_gun_fetch = git
pkg_gun_repo = https://github.com/ninenines/gun
pkg_gun_commit = master

PACKAGES += gut
pkg_gut_name = gut
pkg_gut_description = gut is a template printing, aka scaffolding, tool for Erlang. Like rails generate or yeoman
pkg_gut_homepage = https://github.com/unbalancedparentheses/gut
pkg_gut_fetch = git
pkg_gut_repo = https://github.com/unbalancedparentheses/gut
pkg_gut_commit = master

PACKAGES += hackney
pkg_hackney_name = hackney
pkg_hackney_description = simple HTTP client in Erlang
pkg_hackney_homepage = https://github.com/benoitc/hackney
pkg_hackney_fetch = git
pkg_hackney_repo = https://github.com/benoitc/hackney
pkg_hackney_commit = master

PACKAGES += hamcrest
pkg_hamcrest_name = hamcrest
pkg_hamcrest_description = Erlang port of Hamcrest
pkg_hamcrest_homepage = https://github.com/hyperthunk/hamcrest-erlang
pkg_hamcrest_fetch = git
pkg_hamcrest_repo = https://github.com/hyperthunk/hamcrest-erlang
pkg_hamcrest_commit = master

PACKAGES += hanoidb
pkg_hanoidb_name = hanoidb
pkg_hanoidb_description = Erlang LSM BTree Storage
pkg_hanoidb_homepage = https://github.com/krestenkrab/hanoidb
pkg_hanoidb_fetch = git
pkg_hanoidb_repo = https://github.com/krestenkrab/hanoidb
pkg_hanoidb_commit = master

PACKAGES += hottub
pkg_hottub_name = hottub
pkg_hottub_description = Permanent Erlang Worker Pool
pkg_hottub_homepage = https://github.com/bfrog/hottub
pkg_hottub_fetch = git
pkg_hottub_repo = https://github.com/bfrog/hottub
pkg_hottub_commit = master

PACKAGES += hpack
pkg_hpack_name = hpack
pkg_hpack_description = HPACK Implementation for Erlang
pkg_hpack_homepage = https://github.com/joedevivo/hpack
pkg_hpack_fetch = git
pkg_hpack_repo = https://github.com/joedevivo/hpack
pkg_hpack_commit = master

PACKAGES += hyper
pkg_hyper_name = hyper
pkg_hyper_description = Erlang implementation of HyperLogLog
pkg_hyper_homepage = https://github.com/GameAnalytics/hyper
pkg_hyper_fetch = git
pkg_hyper_repo = https://github.com/GameAnalytics/hyper
pkg_hyper_commit = master

PACKAGES += ibrowse
pkg_ibrowse_name = ibrowse
pkg_ibrowse_description = Erlang HTTP client
pkg_ibrowse_homepage = https://github.com/cmullaparthi/ibrowse
pkg_ibrowse_fetch = git
pkg_ibrowse_repo = https://github.com/cmullaparthi/ibrowse
pkg_ibrowse_commit = v4.1.1

PACKAGES += ierlang
pkg_ierlang_name = ierlang
pkg_ierlang_description = An Erlang language kernel for IPython.
pkg_ierlang_homepage = https://github.com/robbielynch/ierlang
pkg_ierlang_fetch = git
pkg_ierlang_repo = https://github.com/robbielynch/ierlang
pkg_ierlang_commit = master

PACKAGES += iota
pkg_iota_name = iota
pkg_iota_description = iota (Inter-dependency Objective Testing Apparatus) - a tool to enforce clean separation of responsibilities in Erlang code
pkg_iota_homepage = https://github.com/jpgneves/iota
pkg_iota_fetch = git
pkg_iota_repo = https://github.com/jpgneves/iota
pkg_iota_commit = master

PACKAGES += ircd
pkg_ircd_name = ircd
pkg_ircd_description = A pluggable IRC daemon application/library for Erlang.
pkg_ircd_homepage = https://github.com/tonyg/erlang-ircd
pkg_ircd_fetch = git
pkg_ircd_repo = https://github.com/tonyg/erlang-ircd
pkg_ircd_commit = master

PACKAGES += irc_lib
pkg_irc_lib_name = irc_lib
pkg_irc_lib_description = Erlang irc client library
pkg_irc_lib_homepage = https://github.com/OtpChatBot/irc_lib
pkg_irc_lib_fetch = git
pkg_irc_lib_repo = https://github.com/OtpChatBot/irc_lib
pkg_irc_lib_commit = master

PACKAGES += iris
pkg_iris_name = iris
pkg_iris_description = Iris Erlang binding
pkg_iris_homepage = https://github.com/project-iris/iris-erl
pkg_iris_fetch = git
pkg_iris_repo = https://github.com/project-iris/iris-erl
pkg_iris_commit = master

PACKAGES += iso8601
pkg_iso8601_name = iso8601
pkg_iso8601_description = Erlang ISO 8601 date formatter/parser
pkg_iso8601_homepage = https://github.com/seansawyer/erlang_iso8601
pkg_iso8601_fetch = git
pkg_iso8601_repo = https://github.com/seansawyer/erlang_iso8601
pkg_iso8601_commit = master

PACKAGES += itweet
pkg_itweet_name = itweet
pkg_itweet_description = Twitter Stream API on ibrowse
pkg_itweet_homepage = http://inaka.github.com/itweet/
pkg_itweet_fetch = git
pkg_itweet_repo = https://github.com/inaka/itweet
pkg_itweet_commit = v2.0

PACKAGES += jerg
pkg_jerg_name = jerg
pkg_jerg_description = JSON Schema to Erlang Records Generator
pkg_jerg_homepage = https://github.com/ddossot/jerg
pkg_jerg_fetch = git
pkg_jerg_repo = https://github.com/ddossot/jerg
pkg_jerg_commit = master

PACKAGES += jesse
pkg_jesse_name = jesse
pkg_jesse_description = jesse (JSon Schema Erlang) is an implementation of a json schema validator for Erlang.
pkg_jesse_homepage = https://github.com/klarna/jesse
pkg_jesse_fetch = git
pkg_jesse_repo = https://github.com/klarna/jesse
pkg_jesse_commit = master

PACKAGES += jiffy
pkg_jiffy_name = jiffy
pkg_jiffy_description = JSON NIFs for Erlang.
pkg_jiffy_homepage = https://github.com/davisp/jiffy
pkg_jiffy_fetch = git
pkg_jiffy_repo = https://github.com/davisp/jiffy
pkg_jiffy_commit = master

PACKAGES += jiffy_v
pkg_jiffy_v_name = jiffy_v
pkg_jiffy_v_description = JSON validation utility
pkg_jiffy_v_homepage = https://github.com/shizzard/jiffy-v
pkg_jiffy_v_fetch = git
pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
pkg_jiffy_v_commit = 0.3.3

PACKAGES += jobs
pkg_jobs_name = jobs
pkg_jobs_description = a Job scheduler for load regulation
pkg_jobs_homepage = https://github.com/esl/jobs
pkg_jobs_fetch = git
pkg_jobs_repo = https://github.com/esl/jobs
pkg_jobs_commit = 0.3

PACKAGES += joxa
pkg_joxa_name = joxa
pkg_joxa_description = A Modern Lisp for the Erlang VM
pkg_joxa_homepage = https://github.com/joxa/joxa
pkg_joxa_fetch = git
pkg_joxa_repo = https://github.com/joxa/joxa
pkg_joxa_commit = master

PACKAGES += jsonerl
pkg_jsonerl_name = jsonerl
pkg_jsonerl_description = yet another but slightly different erlang <-> json encoder/decoder
pkg_jsonerl_homepage = https://github.com/lambder/jsonerl
pkg_jsonerl_fetch = git
pkg_jsonerl_repo = https://github.com/lambder/jsonerl
pkg_jsonerl_commit = master

PACKAGES += json
pkg_json_name = json
pkg_json_description = a high level json library for erlang (17.0+)
pkg_json_homepage = https://github.com/talentdeficit/json
pkg_json_fetch = git
pkg_json_repo = https://github.com/talentdeficit/json
pkg_json_commit = master

PACKAGES += jsonpath
pkg_jsonpath_name = jsonpath
pkg_jsonpath_description = Fast Erlang JSON data retrieval and updates via javascript-like notation
pkg_jsonpath_homepage = https://github.com/GeneStevens/jsonpath
pkg_jsonpath_fetch = git
pkg_jsonpath_repo = https://github.com/GeneStevens/jsonpath
pkg_jsonpath_commit = master

PACKAGES += json_rec
pkg_json_rec_name = json_rec
pkg_json_rec_description = JSON to erlang record
pkg_json_rec_homepage = https://github.com/justinkirby/json_rec
pkg_json_rec_fetch = git
pkg_json_rec_repo = https://github.com/justinkirby/json_rec
pkg_json_rec_commit = master

PACKAGES += jsonx
pkg_jsonx_name = jsonx
pkg_jsonx_description = JSONX is an Erlang library for efficient decode and encode JSON, written in C.
pkg_jsonx_homepage = https://github.com/iskra/jsonx
pkg_jsonx_fetch = git
pkg_jsonx_repo = https://github.com/iskra/jsonx
pkg_jsonx_commit = master

PACKAGES += jsx
pkg_jsx_name = jsx
pkg_jsx_description = An Erlang application for consuming, producing and manipulating JSON.
pkg_jsx_homepage = https://github.com/talentdeficit/jsx
pkg_jsx_fetch = git
pkg_jsx_repo = https://github.com/talentdeficit/jsx
pkg_jsx_commit = master

PACKAGES += kafka
pkg_kafka_name = kafka
pkg_kafka_description = Kafka consumer and producer in Erlang
pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
pkg_kafka_fetch = git
pkg_kafka_repo = https://github.com/wooga/kafka-erlang
pkg_kafka_commit = master

PACKAGES += kai
pkg_kai_name = kai
pkg_kai_description = DHT storage by Takeshi Inoue
pkg_kai_homepage = https://github.com/synrc/kai
pkg_kai_fetch = git
pkg_kai_repo = https://github.com/synrc/kai
pkg_kai_commit = master

PACKAGES += katja
pkg_katja_name = katja
pkg_katja_description = A simple Riemann client written in Erlang.
pkg_katja_homepage = https://github.com/nifoc/katja
pkg_katja_fetch = git
pkg_katja_repo = https://github.com/nifoc/katja
pkg_katja_commit = master

PACKAGES += kdht
pkg_kdht_name = kdht
pkg_kdht_description = kdht is an erlang DHT implementation
pkg_kdht_homepage = https://github.com/kevinlynx/kdht
pkg_kdht_fetch = git
pkg_kdht_repo = https://github.com/kevinlynx/kdht
pkg_kdht_commit = master

PACKAGES += key2value
pkg_key2value_name = key2value
pkg_key2value_description = Erlang 2-way map
pkg_key2value_homepage = https://github.com/okeuday/key2value
pkg_key2value_fetch = git
pkg_key2value_repo = https://github.com/okeuday/key2value
pkg_key2value_commit = master

PACKAGES += keys1value
pkg_keys1value_name = keys1value
pkg_keys1value_description = Erlang set associative map for key lists
pkg_keys1value_homepage = https://github.com/okeuday/keys1value
pkg_keys1value_fetch = git
pkg_keys1value_repo = https://github.com/okeuday/keys1value
pkg_keys1value_commit = master

PACKAGES += kinetic
pkg_kinetic_name = kinetic
pkg_kinetic_description = Erlang Kinesis Client
pkg_kinetic_homepage = https://github.com/AdRoll/kinetic
pkg_kinetic_fetch = git
pkg_kinetic_repo = https://github.com/AdRoll/kinetic
pkg_kinetic_commit = master

PACKAGES += kjell
pkg_kjell_name = kjell
pkg_kjell_description = Erlang Shell
pkg_kjell_homepage = https://github.com/karlll/kjell
pkg_kjell_fetch = git
pkg_kjell_repo = https://github.com/karlll/kjell
pkg_kjell_commit = master

PACKAGES += kraken
pkg_kraken_name = kraken
pkg_kraken_description = Distributed Pubsub Server for Realtime Apps
pkg_kraken_homepage = https://github.com/Asana/kraken
pkg_kraken_fetch = git
pkg_kraken_repo = https://github.com/Asana/kraken
pkg_kraken_commit = master

PACKAGES += kucumberl
pkg_kucumberl_name = kucumberl
pkg_kucumberl_description = A pure-erlang, open-source, implementation of Cucumber
pkg_kucumberl_homepage = https://github.com/openshine/kucumberl
pkg_kucumberl_fetch = git
pkg_kucumberl_repo = https://github.com/openshine/kucumberl
pkg_kucumberl_commit = master

PACKAGES += kvc
pkg_kvc_name = kvc
pkg_kvc_description = KVC - Key Value Coding for Erlang data structures
pkg_kvc_homepage = https://github.com/etrepum/kvc
pkg_kvc_fetch = git
pkg_kvc_repo = https://github.com/etrepum/kvc
pkg_kvc_commit = master

PACKAGES += kvlists
pkg_kvlists_name = kvlists
pkg_kvlists_description = Lists of key-value pairs (decoded JSON) in Erlang
pkg_kvlists_homepage = https://github.com/jcomellas/kvlists
pkg_kvlists_fetch = git
pkg_kvlists_repo = https://github.com/jcomellas/kvlists
pkg_kvlists_commit = master

PACKAGES += kvs
pkg_kvs_name = kvs
pkg_kvs_description = Container and Iterator
pkg_kvs_homepage = https://github.com/synrc/kvs
pkg_kvs_fetch = git
pkg_kvs_repo = https://github.com/synrc/kvs
pkg_kvs_commit = master

PACKAGES += lager_amqp_backend
pkg_lager_amqp_backend_name = lager_amqp_backend
pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
pkg_lager_amqp_backend_homepage = https://github.com/jbrisbin/lager_amqp_backend
pkg_lager_amqp_backend_fetch = git
pkg_lager_amqp_backend_repo = https://github.com/jbrisbin/lager_amqp_backend
pkg_lager_amqp_backend_commit = master

PACKAGES += lager
pkg_lager_name = lager
pkg_lager_description = A logging framework for Erlang/OTP.
pkg_lager_homepage = https://github.com/basho/lager
pkg_lager_fetch = git
pkg_lager_repo = https://github.com/basho/lager
pkg_lager_commit = master

PACKAGES += lager_syslog
pkg_lager_syslog_name = lager_syslog
pkg_lager_syslog_description = Syslog backend for lager
pkg_lager_syslog_homepage = https://github.com/basho/lager_syslog
pkg_lager_syslog_fetch = git
pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
pkg_lager_syslog_commit = master

PACKAGES += lambdapad
pkg_lambdapad_name = lambdapad
pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
pkg_lambdapad_homepage = https://github.com/gar1t/lambdapad
pkg_lambdapad_fetch = git
pkg_lambdapad_repo = https://github.com/gar1t/lambdapad
pkg_lambdapad_commit = master

PACKAGES += lasp
pkg_lasp_name = lasp
pkg_lasp_description = A Language for Distributed, Eventually Consistent Computations
pkg_lasp_homepage = http://lasp-lang.org/
pkg_lasp_fetch = git
pkg_lasp_repo = https://github.com/lasp-lang/lasp
pkg_lasp_commit = master

PACKAGES += lasse
pkg_lasse_name = lasse
pkg_lasse_description = SSE handler for Cowboy
pkg_lasse_homepage = https://github.com/inaka/lasse
pkg_lasse_fetch = git
pkg_lasse_repo = https://github.com/inaka/lasse
pkg_lasse_commit = 0.1.0

PACKAGES += ldap
pkg_ldap_name = ldap
pkg_ldap_description = LDAP server written in Erlang
pkg_ldap_homepage = https://github.com/spawnproc/ldap
pkg_ldap_fetch = git
pkg_ldap_repo = https://github.com/spawnproc/ldap
pkg_ldap_commit = master

PACKAGES += lethink
pkg_lethink_name = lethink
pkg_lethink_description = erlang driver for rethinkdb
pkg_lethink_homepage = https://github.com/taybin/lethink
pkg_lethink_fetch = git
pkg_lethink_repo = https://github.com/taybin/lethink
pkg_lethink_commit = master

PACKAGES += lfe
pkg_lfe_name = lfe
pkg_lfe_description = Lisp Flavoured Erlang (LFE)
pkg_lfe_homepage = https://github.com/rvirding/lfe
pkg_lfe_fetch = git
pkg_lfe_repo = https://github.com/rvirding/lfe
pkg_lfe_commit = master

PACKAGES += ling
pkg_ling_name = ling
pkg_ling_description = Erlang on Xen
pkg_ling_homepage = https://github.com/cloudozer/ling
pkg_ling_fetch = git
pkg_ling_repo = https://github.com/cloudozer/ling
pkg_ling_commit = master

PACKAGES += live
pkg_live_name = live
pkg_live_description = Automated module and configuration reloader.
pkg_live_homepage = http://ninenines.eu
pkg_live_fetch = git
pkg_live_repo = https://github.com/ninenines/live
pkg_live_commit = master

PACKAGES += lmq
pkg_lmq_name = lmq
pkg_lmq_description = Lightweight Message Queue
pkg_lmq_homepage = https://github.com/iij/lmq
pkg_lmq_fetch = git
pkg_lmq_repo = https://github.com/iij/lmq
pkg_lmq_commit = master

PACKAGES += locker
pkg_locker_name = locker
pkg_locker_description = Atomic distributed 'check and set' for short-lived keys
pkg_locker_homepage = https://github.com/wooga/locker
pkg_locker_fetch = git
pkg_locker_repo = https://github.com/wooga/locker
pkg_locker_commit = master

PACKAGES += locks
pkg_locks_name = locks
pkg_locks_description = A scalable, deadlock-resolving resource locker
pkg_locks_homepage = https://github.com/uwiger/locks
pkg_locks_fetch = git
pkg_locks_repo = https://github.com/uwiger/locks
pkg_locks_commit = master

PACKAGES += log4erl
pkg_log4erl_name = log4erl
pkg_log4erl_description = A logger for erlang in the spirit of Log4J.
pkg_log4erl_homepage = https://github.com/ahmednawras/log4erl
pkg_log4erl_fetch = git
pkg_log4erl_repo = https://github.com/ahmednawras/log4erl
pkg_log4erl_commit = master

PACKAGES += lol
pkg_lol_name = lol
pkg_lol_description = Lisp on erLang, and programming is fun again
pkg_lol_homepage = https://github.com/b0oh/lol
pkg_lol_fetch = git
pkg_lol_repo = https://github.com/b0oh/lol
pkg_lol_commit = master

PACKAGES += lucid
pkg_lucid_name = lucid
pkg_lucid_description = HTTP/2 server written in Erlang
pkg_lucid_homepage = https://github.com/tatsuhiro-t/lucid
pkg_lucid_fetch = git
pkg_lucid_repo = https://github.com/tatsuhiro-t/lucid
pkg_lucid_commit = master

PACKAGES += luerl
pkg_luerl_name = luerl
pkg_luerl_description = Lua in Erlang
pkg_luerl_homepage = https://github.com/rvirding/luerl
pkg_luerl_fetch = git
pkg_luerl_repo = https://github.com/rvirding/luerl
pkg_luerl_commit = develop

PACKAGES += luwak
pkg_luwak_name = luwak
pkg_luwak_description = Large-object storage interface for Riak
pkg_luwak_homepage = https://github.com/basho/luwak
pkg_luwak_fetch = git
pkg_luwak_repo = https://github.com/basho/luwak
pkg_luwak_commit = master

PACKAGES += lux
pkg_lux_name = lux
pkg_lux_description = Lux (LUcid eXpect scripting) simplifies test automation and provides an Expect-style execution of commands
pkg_lux_homepage = https://github.com/hawk/lux
pkg_lux_fetch = git
pkg_lux_repo = https://github.com/hawk/lux
pkg_lux_commit = master

PACKAGES += machi
pkg_machi_name = machi
pkg_machi_description = Machi file store
pkg_machi_homepage = https://github.com/basho/machi
pkg_machi_fetch = git
pkg_machi_repo = https://github.com/basho/machi
pkg_machi_commit = master

PACKAGES += mad
pkg_mad_name = mad
pkg_mad_description = Small and Fast Rebar Replacement
pkg_mad_homepage = https://github.com/synrc/mad
pkg_mad_fetch = git
pkg_mad_repo = https://github.com/synrc/mad
pkg_mad_commit = master

PACKAGES += marina
pkg_marina_name = marina
pkg_marina_description = Non-blocking Erlang Cassandra CQL3 client
pkg_marina_homepage = https://github.com/lpgauth/marina
pkg_marina_fetch = git
pkg_marina_repo = https://github.com/lpgauth/marina
pkg_marina_commit = master

PACKAGES += mavg
pkg_mavg_name = mavg
pkg_mavg_description = Erlang :: Exponential moving average library
pkg_mavg_homepage = https://github.com/EchoTeam/mavg
pkg_mavg_fetch = git
pkg_mavg_repo = https://github.com/EchoTeam/mavg
pkg_mavg_commit = master

PACKAGES += mcd
pkg_mcd_name = mcd
pkg_mcd_description = Fast memcached protocol client in pure Erlang
pkg_mcd_homepage = https://github.com/EchoTeam/mcd
pkg_mcd_fetch = git
pkg_mcd_repo = https://github.com/EchoTeam/mcd
pkg_mcd_commit = master

PACKAGES += mcerlang
pkg_mcerlang_name = mcerlang
pkg_mcerlang_description = The McErlang model checker for Erlang
pkg_mcerlang_homepage = https://github.com/fredlund/McErlang
pkg_mcerlang_fetch = git
pkg_mcerlang_repo = https://github.com/fredlund/McErlang
pkg_mcerlang_commit = master

PACKAGES += mc_erl
pkg_mc_erl_name = mc_erl
pkg_mc_erl_description = mc-erl is a server for Minecraft 1.4.7 written in Erlang.
pkg_mc_erl_homepage = https://github.com/clonejo/mc-erl
pkg_mc_erl_fetch = git
pkg_mc_erl_repo = https://github.com/clonejo/mc-erl
pkg_mc_erl_commit = master

PACKAGES += meck
pkg_meck_name = meck
pkg_meck_description = A mocking library for Erlang
pkg_meck_homepage = https://github.com/eproxus/meck
pkg_meck_fetch = git
pkg_meck_repo = https://github.com/eproxus/meck
pkg_meck_commit = master

PACKAGES += mekao
pkg_mekao_name = mekao
pkg_mekao_description = SQL constructor
pkg_mekao_homepage = https://github.com/ddosia/mekao
pkg_mekao_fetch = git
pkg_mekao_repo = https://github.com/ddosia/mekao
pkg_mekao_commit = master

PACKAGES += memo
pkg_memo_name = memo
pkg_memo_description = Erlang memoization server
pkg_memo_homepage = https://github.com/tuncer/memo
pkg_memo_fetch = git
pkg_memo_repo = https://github.com/tuncer/memo
pkg_memo_commit = master

PACKAGES += merge_index
pkg_merge_index_name = merge_index
pkg_merge_index_description = MergeIndex is an Erlang library for storing ordered sets on disk. It is very similar to an SSTable (in Google's Bigtable) or an HFile (in Hadoop).
pkg_merge_index_homepage = https://github.com/basho/merge_index
pkg_merge_index_fetch = git
pkg_merge_index_repo = https://github.com/basho/merge_index
pkg_merge_index_commit = master

PACKAGES += merl
pkg_merl_name = merl
pkg_merl_description = Metaprogramming in Erlang
pkg_merl_homepage = https://github.com/richcarl/merl
pkg_merl_fetch = git
pkg_merl_repo = https://github.com/richcarl/merl
pkg_merl_commit = master

PACKAGES += mimetypes
pkg_mimetypes_name = mimetypes
pkg_mimetypes_description = Erlang MIME types library
pkg_mimetypes_homepage = https://github.com/spawngrid/mimetypes
pkg_mimetypes_fetch = git
pkg_mimetypes_repo = https://github.com/spawngrid/mimetypes
pkg_mimetypes_commit = master

PACKAGES += mixer
pkg_mixer_name = mixer
pkg_mixer_description = Mix in functions from other modules
pkg_mixer_homepage = https://github.com/chef/mixer
pkg_mixer_fetch = git
pkg_mixer_repo = https://github.com/chef/mixer
pkg_mixer_commit = master

PACKAGES += mochiweb
pkg_mochiweb_name = mochiweb
pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
pkg_mochiweb_fetch = git
pkg_mochiweb_repo = https://github.com/mochi/mochiweb
pkg_mochiweb_commit = master

PACKAGES += mochiweb_xpath
pkg_mochiweb_xpath_name = mochiweb_xpath
pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
pkg_mochiweb_xpath_homepage = https://github.com/retnuh/mochiweb_xpath
pkg_mochiweb_xpath_fetch = git
pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
pkg_mochiweb_xpath_commit = master

PACKAGES += mockgyver
pkg_mockgyver_name = mockgyver
pkg_mockgyver_description = A mocking library for Erlang
pkg_mockgyver_homepage = https://github.com/klajo/mockgyver
pkg_mockgyver_fetch = git
pkg_mockgyver_repo = https://github.com/klajo/mockgyver
pkg_mockgyver_commit = master

PACKAGES += modlib
pkg_modlib_name = modlib
pkg_modlib_description = Web framework based on Erlang's inets httpd
pkg_modlib_homepage = https://github.com/gar1t/modlib
pkg_modlib_fetch = git
pkg_modlib_repo = https://github.com/gar1t/modlib
pkg_modlib_commit = master

PACKAGES += mongodb
pkg_mongodb_name = mongodb
pkg_mongodb_description = MongoDB driver for Erlang
pkg_mongodb_homepage = https://github.com/comtihon/mongodb-erlang
pkg_mongodb_fetch = git
pkg_mongodb_repo = https://github.com/comtihon/mongodb-erlang
pkg_mongodb_commit = master

PACKAGES += mongooseim
pkg_mongooseim_name = mongooseim
pkg_mongooseim_description = Jabber / XMPP server with focus on performance and scalability, by Erlang Solutions
pkg_mongooseim_homepage = https://www.erlang-solutions.com/products/mongooseim-massively-scalable-ejabberd-platform
pkg_mongooseim_fetch = git
pkg_mongooseim_repo = https://github.com/esl/MongooseIM
pkg_mongooseim_commit = master

PACKAGES += moyo
pkg_moyo_name = moyo
pkg_moyo_description = Erlang utility functions library
pkg_moyo_homepage = https://github.com/dwango/moyo
pkg_moyo_fetch = git
pkg_moyo_repo = https://github.com/dwango/moyo
pkg_moyo_commit = master

PACKAGES += msgpack
pkg_msgpack_name = msgpack
pkg_msgpack_description = MessagePack (de)serializer implementation for Erlang
pkg_msgpack_homepage = https://github.com/msgpack/msgpack-erlang
pkg_msgpack_fetch = git
pkg_msgpack_repo = https://github.com/msgpack/msgpack-erlang
pkg_msgpack_commit = master

PACKAGES += mu2
pkg_mu2_name = mu2
pkg_mu2_description = Erlang mutation testing tool
pkg_mu2_homepage = https://github.com/ramsay-t/mu2
pkg_mu2_fetch = git
pkg_mu2_repo = https://github.com/ramsay-t/mu2
pkg_mu2_commit = master

PACKAGES += mustache
pkg_mustache_name = mustache
pkg_mustache_description = Mustache template engine for Erlang.
pkg_mustache_homepage = https://github.com/mojombo/mustache.erl
pkg_mustache_fetch = git
pkg_mustache_repo = https://github.com/mojombo/mustache.erl
pkg_mustache_commit = master

PACKAGES += myproto
pkg_myproto_name = myproto
pkg_myproto_description = MySQL Server Protocol in Erlang
pkg_myproto_homepage = https://github.com/altenwald/myproto
pkg_myproto_fetch = git
pkg_myproto_repo = https://github.com/altenwald/myproto
pkg_myproto_commit = master

PACKAGES += mysql
pkg_mysql_name = mysql
pkg_mysql_description = Erlang MySQL Driver (from code.google.com)
pkg_mysql_homepage = https://github.com/dizzyd/erlang-mysql-driver
pkg_mysql_fetch = git
pkg_mysql_repo = https://github.com/dizzyd/erlang-mysql-driver
pkg_mysql_commit = master

PACKAGES += n2o
pkg_n2o_name = n2o
pkg_n2o_description = WebSocket Application Server
pkg_n2o_homepage = https://github.com/5HT/n2o
pkg_n2o_fetch = git
pkg_n2o_repo = https://github.com/5HT/n2o
pkg_n2o_commit = master

PACKAGES += nat_upnp
pkg_nat_upnp_name = nat_upnp
pkg_nat_upnp_description = Erlang library to map your internal port to an external using UNP IGD
pkg_nat_upnp_homepage = https://github.com/benoitc/nat_upnp
pkg_nat_upnp_fetch = git
pkg_nat_upnp_repo = https://github.com/benoitc/nat_upnp
pkg_nat_upnp_commit = master

PACKAGES += neo4j
pkg_neo4j_name = neo4j
pkg_neo4j_description = Erlang client library for Neo4J.
pkg_neo4j_homepage = https://github.com/dmitriid/neo4j-erlang
pkg_neo4j_fetch = git
pkg_neo4j_repo = https://github.com/dmitriid/neo4j-erlang
pkg_neo4j_commit = master

PACKAGES += neotoma
pkg_neotoma_name = neotoma
pkg_neotoma_description = Erlang library and packrat parser-generator for parsing expression grammars.
pkg_neotoma_homepage = https://github.com/seancribbs/neotoma
pkg_neotoma_fetch = git
pkg_neotoma_repo = https://github.com/seancribbs/neotoma
pkg_neotoma_commit = master

PACKAGES += newrelic
pkg_newrelic_name = newrelic
pkg_newrelic_description = Erlang library for sending metrics to New Relic
pkg_newrelic_homepage = https://github.com/wooga/newrelic-erlang
pkg_newrelic_fetch = git
pkg_newrelic_repo = https://github.com/wooga/newrelic-erlang
pkg_newrelic_commit = master

PACKAGES += nifty
pkg_nifty_name = nifty
pkg_nifty_description = Erlang NIF wrapper generator
pkg_nifty_homepage = https://github.com/parapluu/nifty
pkg_nifty_fetch = git
pkg_nifty_repo = https://github.com/parapluu/nifty
pkg_nifty_commit = master

PACKAGES += nitrogen_core
pkg_nitrogen_core_name = nitrogen_core
pkg_nitrogen_core_description = The core Nitrogen library.
pkg_nitrogen_core_homepage = http://nitrogenproject.com/
pkg_nitrogen_core_fetch = git
pkg_nitrogen_core_repo = https://github.com/nitrogen/nitrogen_core
pkg_nitrogen_core_commit = master

PACKAGES += nkbase
pkg_nkbase_name = nkbase
pkg_nkbase_description = NkBASE distributed database
pkg_nkbase_homepage = https://github.com/Nekso/nkbase
pkg_nkbase_fetch = git
pkg_nkbase_repo = https://github.com/Nekso/nkbase
pkg_nkbase_commit = develop

PACKAGES += nkdocker
pkg_nkdocker_name = nkdocker
pkg_nkdocker_description = Erlang Docker client
pkg_nkdocker_homepage = https://github.com/Nekso/nkdocker
pkg_nkdocker_fetch = git
pkg_nkdocker_repo = https://github.com/Nekso/nkdocker
pkg_nkdocker_commit = master

PACKAGES += nkpacket
pkg_nkpacket_name = nkpacket
pkg_nkpacket_description = Generic Erlang transport layer
pkg_nkpacket_homepage = https://github.com/Nekso/nkpacket
pkg_nkpacket_fetch = git
pkg_nkpacket_repo = https://github.com/Nekso/nkpacket
pkg_nkpacket_commit = master

PACKAGES += nodefinder
pkg_nodefinder_name = nodefinder
pkg_nodefinder_description = automatic node discovery via UDP multicast
pkg_nodefinder_homepage = https://github.com/erlanger/nodefinder
pkg_nodefinder_fetch = git
pkg_nodefinder_repo = https://github.com/okeuday/nodefinder
pkg_nodefinder_commit = master

PACKAGES += nprocreg
pkg_nprocreg_name = nprocreg
pkg_nprocreg_description = Minimal Distributed Erlang Process Registry
pkg_nprocreg_homepage = http://nitrogenproject.com/
pkg_nprocreg_fetch = git
pkg_nprocreg_repo = https://github.com/nitrogen/nprocreg
pkg_nprocreg_commit = master

PACKAGES += oauth2c
pkg_oauth2c_name = oauth2c
pkg_oauth2c_description = Erlang OAuth2 Client
pkg_oauth2c_homepage = https://github.com/kivra/oauth2_client
pkg_oauth2c_fetch = git
pkg_oauth2c_repo = https://github.com/kivra/oauth2_client
pkg_oauth2c_commit = master

PACKAGES += oauth2
pkg_oauth2_name = oauth2
pkg_oauth2_description = Erlang Oauth2 implementation
pkg_oauth2_homepage = https://github.com/kivra/oauth2
pkg_oauth2_fetch = git
pkg_oauth2_repo = https://github.com/kivra/oauth2
pkg_oauth2_commit = master

PACKAGES += oauth
pkg_oauth_name = oauth
pkg_oauth_description = An Erlang OAuth 1.0 implementation
pkg_oauth_homepage = https://github.com/tim/erlang-oauth
pkg_oauth_fetch = git
pkg_oauth_repo = https://github.com/tim/erlang-oauth
pkg_oauth_commit = master

PACKAGES += of_protocol
pkg_of_protocol_name = of_protocol
pkg_of_protocol_description = OpenFlow Protocol Library for Erlang
pkg_of_protocol_homepage = https://github.com/FlowForwarding/of_protocol
pkg_of_protocol_fetch = git
pkg_of_protocol_repo = https://github.com/FlowForwarding/of_protocol
pkg_of_protocol_commit = master

PACKAGES += openflow
pkg_openflow_name = openflow
pkg_openflow_description = An OpenFlow controller written in pure erlang
pkg_openflow_homepage = https://github.com/renatoaguiar/erlang-openflow
pkg_openflow_fetch = git
pkg_openflow_repo = https://github.com/renatoaguiar/erlang-openflow
pkg_openflow_commit = master

PACKAGES += openid
pkg_openid_name = openid
pkg_openid_description = Erlang OpenID
pkg_openid_homepage = https://github.com/brendonh/erl_openid
pkg_openid_fetch = git
pkg_openid_repo = https://github.com/brendonh/erl_openid
pkg_openid_commit = master

PACKAGES += openpoker
pkg_openpoker_name = openpoker
pkg_openpoker_description = Genesis Texas hold'em Game Server
pkg_openpoker_homepage = https://github.com/hpyhacking/openpoker
pkg_openpoker_fetch = git
pkg_openpoker_repo = https://github.com/hpyhacking/openpoker
pkg_openpoker_commit = master

PACKAGES += pal
pkg_pal_name = pal
pkg_pal_description = Pragmatic Authentication Library
pkg_pal_homepage = https://github.com/manifest/pal
pkg_pal_fetch = git
pkg_pal_repo = https://github.com/manifest/pal
pkg_pal_commit = master

PACKAGES += parse_trans
pkg_parse_trans_name = parse_trans
pkg_parse_trans_description = Parse transform utilities for Erlang
pkg_parse_trans_homepage = https://github.com/uwiger/parse_trans
pkg_parse_trans_fetch = git
pkg_parse_trans_repo = https://github.com/uwiger/parse_trans
pkg_parse_trans_commit = master

PACKAGES += parsexml
pkg_parsexml_name = parsexml
pkg_parsexml_description = Simple DOM XML parser with convenient and very simple API
pkg_parsexml_homepage = https://github.com/maxlapshin/parsexml
pkg_parsexml_fetch = git
pkg_parsexml_repo = https://github.com/maxlapshin/parsexml
pkg_parsexml_commit = master

PACKAGES += pegjs
pkg_pegjs_name = pegjs
pkg_pegjs_description = An implementation of PEG.js grammar for Erlang.
pkg_pegjs_homepage = https://github.com/dmitriid/pegjs
pkg_pegjs_fetch = git
pkg_pegjs_repo = https://github.com/dmitriid/pegjs
pkg_pegjs_commit = 0.3

PACKAGES += percept2
pkg_percept2_name = percept2
pkg_percept2_description = Concurrent profiling tool for Erlang
pkg_percept2_homepage = https://github.com/huiqing/percept2
pkg_percept2_fetch = git
pkg_percept2_repo = https://github.com/huiqing/percept2
pkg_percept2_commit = master

PACKAGES += pgsql
pkg_pgsql_name = pgsql
pkg_pgsql_description = Erlang PostgreSQL driver
pkg_pgsql_homepage = https://github.com/semiocast/pgsql
pkg_pgsql_fetch = git
pkg_pgsql_repo = https://github.com/semiocast/pgsql
pkg_pgsql_commit = master

PACKAGES += pkgx
pkg_pkgx_name = pkgx
pkg_pkgx_description = Build .deb packages from Erlang releases
pkg_pkgx_homepage = https://github.com/arjan/pkgx
pkg_pkgx_fetch = git
pkg_pkgx_repo = https://github.com/arjan/pkgx
pkg_pkgx_commit = master

PACKAGES += pkt
pkg_pkt_name = pkt
pkg_pkt_description = Erlang network protocol library
pkg_pkt_homepage = https://github.com/msantos/pkt
pkg_pkt_fetch = git
pkg_pkt_repo = https://github.com/msantos/pkt
pkg_pkt_commit = master

PACKAGES += plain_fsm
pkg_plain_fsm_name = plain_fsm
pkg_plain_fsm_description = A behaviour/support library for writing plain Erlang FSMs.
pkg_plain_fsm_homepage = https://github.com/uwiger/plain_fsm
pkg_plain_fsm_fetch = git
pkg_plain_fsm_repo = https://github.com/uwiger/plain_fsm
pkg_plain_fsm_commit = master

PACKAGES += plumtree
pkg_plumtree_name = plumtree
pkg_plumtree_description = Epidemic Broadcast Trees
pkg_plumtree_homepage = https://github.com/helium/plumtree
pkg_plumtree_fetch = git
pkg_plumtree_repo = https://github.com/helium/plumtree
pkg_plumtree_commit = master

PACKAGES += pmod_transform
pkg_pmod_transform_name = pmod_transform
pkg_pmod_transform_description = Parse transform for parameterized modules
pkg_pmod_transform_homepage = https://github.com/erlang/pmod_transform
pkg_pmod_transform_fetch = git
pkg_pmod_transform_repo = https://github.com/erlang/pmod_transform
pkg_pmod_transform_commit = master

PACKAGES += pobox
pkg_pobox_name = pobox
pkg_pobox_description = External buffer processes to protect against mailbox overflow in Erlang
pkg_pobox_homepage = https://github.com/ferd/pobox
pkg_pobox_fetch = git
pkg_pobox_repo = https://github.com/ferd/pobox
pkg_pobox_commit = master

PACKAGES += ponos
pkg_ponos_name = ponos
pkg_ponos_description = ponos is a simple yet powerful load generator written in erlang
pkg_ponos_homepage = https://github.com/klarna/ponos
pkg_ponos_fetch = git
pkg_ponos_repo = https://github.com/klarna/ponos
pkg_ponos_commit = master

PACKAGES += poolboy
pkg_poolboy_name = poolboy
pkg_poolboy_description = A hunky Erlang worker pool factory
pkg_poolboy_homepage = https://github.com/devinus/poolboy
pkg_poolboy_fetch = git
pkg_poolboy_repo = https://github.com/devinus/poolboy
pkg_poolboy_commit = master

PACKAGES += pooler
pkg_pooler_name = pooler
pkg_pooler_description = An OTP Process Pool Application
pkg_pooler_homepage = https://github.com/seth/pooler
pkg_pooler_fetch = git
pkg_pooler_repo = https://github.com/seth/pooler
pkg_pooler_commit = master

PACKAGES += pqueue
pkg_pqueue_name = pqueue
pkg_pqueue_description = Erlang Priority Queues
pkg_pqueue_homepage = https://github.com/okeuday/pqueue
pkg_pqueue_fetch = git
pkg_pqueue_repo = https://github.com/okeuday/pqueue
pkg_pqueue_commit = master

PACKAGES += procket
pkg_procket_name = procket
pkg_procket_description = Erlang interface to low level socket operations
pkg_procket_homepage = http://blog.listincomprehension.com/search/label/procket
pkg_procket_fetch = git
pkg_procket_repo = https://github.com/msantos/procket
pkg_procket_commit = master

PACKAGES += proper
pkg_proper_name = proper
pkg_proper_description = PropEr: a QuickCheck-inspired property-based testing tool for Erlang.
pkg_proper_homepage = http://proper.softlab.ntua.gr
pkg_proper_fetch = git
pkg_proper_repo = https://github.com/manopapad/proper
pkg_proper_commit = master

PACKAGES += prop
pkg_prop_name = prop
pkg_prop_description = An Erlang code scaffolding and generator system.
pkg_prop_homepage = https://github.com/nuex/prop
pkg_prop_fetch = git
pkg_prop_repo = https://github.com/nuex/prop
pkg_prop_commit = master

PACKAGES += props
pkg_props_name = props
pkg_props_description = Property structure library
pkg_props_homepage = https://github.com/greyarea/props
pkg_props_fetch = git
pkg_props_repo = https://github.com/greyarea/props
pkg_props_commit = master

PACKAGES += protobuffs
pkg_protobuffs_name = protobuffs
pkg_protobuffs_description = An implementation of Google's Protocol Buffers for Erlang, based on ngerakines/erlang_protobuffs.
pkg_protobuffs_homepage = https://github.com/basho/erlang_protobuffs
pkg_protobuffs_fetch = git
pkg_protobuffs_repo = https://github.com/basho/erlang_protobuffs
pkg_protobuffs_commit = master

PACKAGES += psycho
pkg_psycho_name = psycho
pkg_psycho_description = HTTP server that provides a WSGI-like interface for applications and middleware.
pkg_psycho_homepage = https://github.com/gar1t/psycho
pkg_psycho_fetch = git
pkg_psycho_repo = https://github.com/gar1t/psycho
pkg_psycho_commit = master

PACKAGES += ptrackerl
pkg_ptrackerl_name = ptrackerl
pkg_ptrackerl_description = Pivotal Tracker API Client written in Erlang
pkg_ptrackerl_homepage = https://github.com/inaka/ptrackerl
pkg_ptrackerl_fetch = git
pkg_ptrackerl_repo = https://github.com/inaka/ptrackerl
pkg_ptrackerl_commit = master

PACKAGES += purity
pkg_purity_name = purity
pkg_purity_description = A side-effect analyzer for Erlang
pkg_purity_homepage = https://github.com/mpitid/purity
pkg_purity_fetch = git
pkg_purity_repo = https://github.com/mpitid/purity
pkg_purity_commit = master

PACKAGES += push_service
pkg_push_service_name = push_service
pkg_push_service_description = Push service
pkg_push_service_homepage = https://github.com/hairyhum/push_service
pkg_push_service_fetch = git
pkg_push_service_repo = https://github.com/hairyhum/push_service
pkg_push_service_commit = master

PACKAGES += qdate
pkg_qdate_name = qdate
pkg_qdate_description = Date, time, and timezone parsing, formatting, and conversion for Erlang.
pkg_qdate_homepage = https://github.com/choptastic/qdate
pkg_qdate_fetch = git
pkg_qdate_repo = https://github.com/choptastic/qdate
pkg_qdate_commit = 0.4.0

PACKAGES += qrcode
pkg_qrcode_name = qrcode
pkg_qrcode_description = QR Code encoder in Erlang
pkg_qrcode_homepage = https://github.com/komone/qrcode
pkg_qrcode_fetch = git
pkg_qrcode_repo = https://github.com/komone/qrcode
pkg_qrcode_commit = master

PACKAGES += quest
pkg_quest_name = quest
pkg_quest_description = Learn Erlang through this set of challenges. An interactive system for getting to know Erlang.
pkg_quest_homepage = https://github.com/eriksoe/ErlangQuest
pkg_quest_fetch = git
pkg_quest_repo = https://github.com/eriksoe/ErlangQuest
pkg_quest_commit = master

PACKAGES += quickrand
pkg_quickrand_name = quickrand
pkg_quickrand_description = Quick Erlang Random Number Generation
pkg_quickrand_homepage = https://github.com/okeuday/quickrand
pkg_quickrand_fetch = git
pkg_quickrand_repo = https://github.com/okeuday/quickrand
pkg_quickrand_commit = master

PACKAGES += rabbit_exchange_type_riak
pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
pkg_rabbit_exchange_type_riak_homepage = https://github.com/jbrisbin/riak-exchange
pkg_rabbit_exchange_type_riak_fetch = git
pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
pkg_rabbit_exchange_type_riak_commit = master

PACKAGES += rabbit
pkg_rabbit_name = rabbit
pkg_rabbit_description = RabbitMQ Server
pkg_rabbit_homepage = https://www.rabbitmq.com/
pkg_rabbit_fetch = git
pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
pkg_rabbit_commit = master

PACKAGES += rack
pkg_rack_name = rack
pkg_rack_description = Rack handler for erlang
pkg_rack_homepage = https://github.com/erlyvideo/rack
pkg_rack_fetch = git
pkg_rack_repo = https://github.com/erlyvideo/rack
pkg_rack_commit = master

PACKAGES += radierl
pkg_radierl_name = radierl
pkg_radierl_description = RADIUS protocol stack implemented in Erlang.
pkg_radierl_homepage = https://github.com/vances/radierl
pkg_radierl_fetch = git
pkg_radierl_repo = https://github.com/vances/radierl
pkg_radierl_commit = master

PACKAGES += rafter
pkg_rafter_name = rafter
pkg_rafter_description = An Erlang library application which implements the Raft consensus protocol
pkg_rafter_homepage = https://github.com/andrewjstone/rafter
pkg_rafter_fetch = git
pkg_rafter_repo = https://github.com/andrewjstone/rafter
pkg_rafter_commit = master

PACKAGES += ranch
pkg_ranch_name = ranch
pkg_ranch_description = Socket acceptor pool for TCP protocols.
pkg_ranch_homepage = http://ninenines.eu
pkg_ranch_fetch = git
pkg_ranch_repo = https://github.com/ninenines/ranch
pkg_ranch_commit = 1.1.0

PACKAGES += rbeacon
pkg_rbeacon_name = rbeacon
pkg_rbeacon_description = LAN discovery and presence in Erlang.
pkg_rbeacon_homepage = https://github.com/refuge/rbeacon
pkg_rbeacon_fetch = git
pkg_rbeacon_repo = https://github.com/refuge/rbeacon
pkg_rbeacon_commit = master

PACKAGES += rebar
pkg_rebar_name = rebar
pkg_rebar_description = Erlang build tool that makes it easy to compile and test Erlang applications, port drivers and releases.
pkg_rebar_homepage = http://www.rebar3.org
pkg_rebar_fetch = git
pkg_rebar_repo = https://github.com/rebar/rebar3
pkg_rebar_commit = master

PACKAGES += rebus
pkg_rebus_name = rebus
pkg_rebus_description = A stupid simple, internal, pub/sub event bus written in- and for Erlang.
pkg_rebus_homepage = https://github.com/olle/rebus
pkg_rebus_fetch = git
pkg_rebus_repo = https://github.com/olle/rebus
pkg_rebus_commit = master

PACKAGES += rec2json
pkg_rec2json_name = rec2json
pkg_rec2json_description = Compile erlang record definitions into modules to convert them to/from json easily.
pkg_rec2json_homepage = https://github.com/lordnull/rec2json
pkg_rec2json_fetch = git
pkg_rec2json_repo = https://github.com/lordnull/rec2json
pkg_rec2json_commit = master

PACKAGES += recon
pkg_recon_name = recon
pkg_recon_description = Collection of functions and scripts to debug Erlang in production.
pkg_recon_homepage = https://github.com/ferd/recon
pkg_recon_fetch = git
pkg_recon_repo = https://github.com/ferd/recon
pkg_recon_commit = 2.2.1

PACKAGES += record_info
pkg_record_info_name = record_info
pkg_record_info_description = Convert between record and proplist
pkg_record_info_homepage = https://github.com/bipthelin/erlang-record_info
pkg_record_info_fetch = git
pkg_record_info_repo = https://github.com/bipthelin/erlang-record_info
pkg_record_info_commit = master

PACKAGES += redgrid
pkg_redgrid_name = redgrid
pkg_redgrid_description = automatic Erlang node discovery via redis
pkg_redgrid_homepage = https://github.com/jkvor/redgrid
pkg_redgrid_fetch = git
pkg_redgrid_repo = https://github.com/jkvor/redgrid
pkg_redgrid_commit = master

PACKAGES += redo
pkg_redo_name = redo
pkg_redo_description = pipelined erlang redis client
pkg_redo_homepage = https://github.com/jkvor/redo
pkg_redo_fetch = git
pkg_redo_repo = https://github.com/jkvor/redo
pkg_redo_commit = master

PACKAGES += reltool_util
pkg_reltool_util_name = reltool_util
pkg_reltool_util_description = Erlang reltool utility functionality application
pkg_reltool_util_homepage = https://github.com/okeuday/reltool_util
pkg_reltool_util_fetch = git
pkg_reltool_util_repo = https://github.com/okeuday/reltool_util
pkg_reltool_util_commit = master

PACKAGES += relx
pkg_relx_name = relx
pkg_relx_description = Sane, simple release creation for Erlang
pkg_relx_homepage = https://github.com/erlware/relx
pkg_relx_fetch = git
pkg_relx_repo = https://github.com/erlware/relx
pkg_relx_commit = master

PACKAGES += resource_discovery
pkg_resource_discovery_name = resource_discovery
pkg_resource_discovery_description = An application used to dynamically discover resources present in an Erlang node cluster.
pkg_resource_discovery_homepage = http://erlware.org/
pkg_resource_discovery_fetch = git
pkg_resource_discovery_repo = https://github.com/erlware/resource_discovery
pkg_resource_discovery_commit = master

PACKAGES += restc
pkg_restc_name = restc
pkg_restc_description = Erlang Rest Client
pkg_restc_homepage = https://github.com/kivra/restclient
pkg_restc_fetch = git
pkg_restc_repo = https://github.com/kivra/restclient
pkg_restc_commit = master

PACKAGES += rfc4627_jsonrpc
pkg_rfc4627_jsonrpc_name = rfc4627_jsonrpc
pkg_rfc4627_jsonrpc_description = Erlang RFC4627 (JSON) codec and JSON-RPC server implementation.
pkg_rfc4627_jsonrpc_homepage = https://github.com/tonyg/erlang-rfc4627
pkg_rfc4627_jsonrpc_fetch = git
pkg_rfc4627_jsonrpc_repo = https://github.com/tonyg/erlang-rfc4627
pkg_rfc4627_jsonrpc_commit = master

PACKAGES += riakc
pkg_riakc_name = riakc
pkg_riakc_description = Erlang clients for Riak.
pkg_riakc_homepage = https://github.com/basho/riak-erlang-client
pkg_riakc_fetch = git
pkg_riakc_repo = https://github.com/basho/riak-erlang-client
pkg_riakc_commit = master

PACKAGES += riak_core
pkg_riak_core_name = riak_core
pkg_riak_core_description = Distributed systems infrastructure used by Riak.
pkg_riak_core_homepage = https://github.com/basho/riak_core
pkg_riak_core_fetch = git
pkg_riak_core_repo = https://github.com/basho/riak_core
pkg_riak_core_commit = master

PACKAGES += riak_dt
pkg_riak_dt_name = riak_dt
pkg_riak_dt_description = Convergent replicated datatypes in Erlang
pkg_riak_dt_homepage = https://github.com/basho/riak_dt
pkg_riak_dt_fetch = git
pkg_riak_dt_repo = https://github.com/basho/riak_dt
pkg_riak_dt_commit = master

PACKAGES += riak_ensemble
pkg_riak_ensemble_name = riak_ensemble
pkg_riak_ensemble_description = Multi-Paxos framework in Erlang
pkg_riak_ensemble_homepage = https://github.com/basho/riak_ensemble
pkg_riak_ensemble_fetch = git
pkg_riak_ensemble_repo = https://github.com/basho/riak_ensemble
pkg_riak_ensemble_commit = master

PACKAGES += riakhttpc
pkg_riakhttpc_name = riakhttpc
pkg_riakhttpc_description = Riak Erlang client using the HTTP interface
pkg_riakhttpc_homepage = https://github.com/basho/riak-erlang-http-client
pkg_riakhttpc_fetch = git
pkg_riakhttpc_repo = https://github.com/basho/riak-erlang-http-client
pkg_riakhttpc_commit = master

PACKAGES += riak_kv
pkg_riak_kv_name = riak_kv
pkg_riak_kv_description = Riak Key/Value Store
pkg_riak_kv_homepage = https://github.com/basho/riak_kv
pkg_riak_kv_fetch = git
pkg_riak_kv_repo = https://github.com/basho/riak_kv
pkg_riak_kv_commit = master

PACKAGES += riaknostic
pkg_riaknostic_name = riaknostic
pkg_riaknostic_description = A diagnostic tool for Riak installations, to find common errors asap
pkg_riaknostic_homepage = https://github.com/basho/riaknostic
pkg_riaknostic_fetch = git
pkg_riaknostic_repo = https://github.com/basho/riaknostic
pkg_riaknostic_commit = master

PACKAGES += riak_pg
pkg_riak_pg_name = riak_pg
pkg_riak_pg_description = Distributed process groups with riak_core.
pkg_riak_pg_homepage = https://github.com/cmeiklejohn/riak_pg
pkg_riak_pg_fetch = git
pkg_riak_pg_repo = https://github.com/cmeiklejohn/riak_pg
pkg_riak_pg_commit = master

PACKAGES += riak_pipe
pkg_riak_pipe_name = riak_pipe
pkg_riak_pipe_description = Riak Pipelines
pkg_riak_pipe_homepage = https://github.com/basho/riak_pipe
pkg_riak_pipe_fetch = git
pkg_riak_pipe_repo = https://github.com/basho/riak_pipe
pkg_riak_pipe_commit = master

PACKAGES += riakpool
pkg_riakpool_name = riakpool
pkg_riakpool_description = erlang riak client pool
pkg_riakpool_homepage = https://github.com/dweldon/riakpool
pkg_riakpool_fetch = git
pkg_riakpool_repo = https://github.com/dweldon/riakpool
pkg_riakpool_commit = master

PACKAGES += riak_sysmon
pkg_riak_sysmon_name = riak_sysmon
pkg_riak_sysmon_description = Simple OTP app for managing Erlang VM system_monitor event messages
pkg_riak_sysmon_homepage = https://github.com/basho/riak_sysmon
pkg_riak_sysmon_fetch = git
pkg_riak_sysmon_repo = https://github.com/basho/riak_sysmon
pkg_riak_sysmon_commit = master

PACKAGES += riak_test
pkg_riak_test_name = riak_test
pkg_riak_test_description = I'm in your cluster, testing your riaks
pkg_riak_test_homepage = https://github.com/basho/riak_test
pkg_riak_test_fetch = git
pkg_riak_test_repo = https://github.com/basho/riak_test
pkg_riak_test_commit = master

PACKAGES += rivus_cep
pkg_rivus_cep_name = rivus_cep
pkg_rivus_cep_description = Complex event processing in Erlang
pkg_rivus_cep_homepage = https://github.com/vascokk/rivus_cep
pkg_rivus_cep_fetch = git
pkg_rivus_cep_repo = https://github.com/vascokk/rivus_cep
pkg_rivus_cep_commit = master

PACKAGES += rlimit
pkg_rlimit_name = rlimit
pkg_rlimit_description = Magnus Klaar's rate limiter code from etorrent
pkg_rlimit_homepage = https://github.com/jlouis/rlimit
pkg_rlimit_fetch = git
pkg_rlimit_repo = https://github.com/jlouis/rlimit
pkg_rlimit_commit = master

PACKAGES += safetyvalve
pkg_safetyvalve_name = safetyvalve
pkg_safetyvalve_description = A safety valve for your erlang node
pkg_safetyvalve_homepage = https://github.com/jlouis/safetyvalve
pkg_safetyvalve_fetch = git
pkg_safetyvalve_repo = https://github.com/jlouis/safetyvalve
pkg_safetyvalve_commit = master

PACKAGES += seestar
pkg_seestar_name = seestar
pkg_seestar_description = The Erlang client for Cassandra 1.2+ binary protocol
pkg_seestar_homepage = https://github.com/iamaleksey/seestar
pkg_seestar_fetch = git
pkg_seestar_repo = https://github.com/iamaleksey/seestar
pkg_seestar_commit = master

PACKAGES += service
pkg_service_name = service
pkg_service_description = A minimal Erlang behavior for creating CloudI internal services
pkg_service_homepage = http://cloudi.org/
pkg_service_fetch = git
pkg_service_repo = https://github.com/CloudI/service
pkg_service_commit = master

PACKAGES += setup
pkg_setup_name = setup
pkg_setup_description = Generic setup utility for Erlang-based systems
pkg_setup_homepage = https://github.com/uwiger/setup
pkg_setup_fetch = git
pkg_setup_repo = https://github.com/uwiger/setup
pkg_setup_commit = master

PACKAGES += sext
pkg_sext_name = sext
pkg_sext_description = Sortable Erlang Term Serialization
pkg_sext_homepage = https://github.com/uwiger/sext
pkg_sext_fetch = git
pkg_sext_repo = https://github.com/uwiger/sext
pkg_sext_commit = master

PACKAGES += sfmt
pkg_sfmt_name = sfmt
pkg_sfmt_description = SFMT pseudo random number generator for Erlang.
pkg_sfmt_homepage = https://github.com/jj1bdx/sfmt-erlang
pkg_sfmt_fetch = git
pkg_sfmt_repo = https://github.com/jj1bdx/sfmt-erlang
pkg_sfmt_commit = master

PACKAGES += sgte
pkg_sgte_name = sgte
pkg_sgte_description = A simple Erlang Template Engine
pkg_sgte_homepage = https://github.com/filippo/sgte
pkg_sgte_fetch = git
pkg_sgte_repo = https://github.com/filippo/sgte
pkg_sgte_commit = master

PACKAGES += sheriff
pkg_sheriff_name = sheriff
pkg_sheriff_description = Parse transform for type based validation.
pkg_sheriff_homepage = http://ninenines.eu
pkg_sheriff_fetch = git
pkg_sheriff_repo = https://github.com/extend/sheriff
pkg_sheriff_commit = master

PACKAGES += shotgun
pkg_shotgun_name = shotgun
pkg_shotgun_description = better than just a gun
pkg_shotgun_homepage = https://github.com/inaka/shotgun
pkg_shotgun_fetch = git
pkg_shotgun_repo = https://github.com/inaka/shotgun
pkg_shotgun_commit = 0.1.0

PACKAGES += sidejob
pkg_sidejob_name = sidejob
pkg_sidejob_description = Parallel worker and capacity limiting library for Erlang
pkg_sidejob_homepage = https://github.com/basho/sidejob
pkg_sidejob_fetch = git
pkg_sidejob_repo = https://github.com/basho/sidejob
pkg_sidejob_commit = master

PACKAGES += sieve
pkg_sieve_name = sieve
pkg_sieve_description = sieve is a simple TCP routing proxy (layer 7) in erlang
pkg_sieve_homepage = https://github.com/benoitc/sieve
pkg_sieve_fetch = git
pkg_sieve_repo = https://github.com/benoitc/sieve
pkg_sieve_commit = master

PACKAGES += sighandler
pkg_sighandler_name = sighandler
pkg_sighandler_description = Handle UNIX signals in Er    lang
pkg_sighandler_homepage = https://github.com/jkingsbery/sighandler
pkg_sighandler_fetch = git
pkg_sighandler_repo = https://github.com/jkingsbery/sighandler
pkg_sighandler_commit = master

PACKAGES += simhash
pkg_simhash_name = simhash
pkg_simhash_description = Simhashing for Erlang -- hashing algorithm to find near-duplicates in binary data.
pkg_simhash_homepage = https://github.com/ferd/simhash
pkg_simhash_fetch = git
pkg_simhash_repo = https://github.com/ferd/simhash
pkg_simhash_commit = master

PACKAGES += simple_bridge
pkg_simple_bridge_name = simple_bridge
pkg_simple_bridge_description = A simple, standardized interface library to Erlang HTTP Servers.
pkg_simple_bridge_homepage = https://github.com/nitrogen/simple_bridge
pkg_simple_bridge_fetch = git
pkg_simple_bridge_repo = https://github.com/nitrogen/simple_bridge
pkg_simple_bridge_commit = master

PACKAGES += simple_oauth2
pkg_simple_oauth2_name = simple_oauth2
pkg_simple_oauth2_description = Simple erlang OAuth2 client module for any http server framework (Google, Facebook, Yandex, Vkontakte are preconfigured)
pkg_simple_oauth2_homepage = https://github.com/virtan/simple_oauth2
pkg_simple_oauth2_fetch = git
pkg_simple_oauth2_repo = https://github.com/virtan/simple_oauth2
pkg_simple_oauth2_commit = master

PACKAGES += skel
pkg_skel_name = skel
pkg_skel_description = A Streaming Process-based Skeleton Library for Erlang
pkg_skel_homepage = https://github.com/ParaPhrase/skel
pkg_skel_fetch = git
pkg_skel_repo = https://github.com/ParaPhrase/skel
pkg_skel_commit = master

PACKAGES += smother
pkg_smother_name = smother
pkg_smother_description = Extended code coverage metrics for Erlang.
pkg_smother_homepage = https://ramsay-t.github.io/Smother/
pkg_smother_fetch = git
pkg_smother_repo = https://github.com/ramsay-t/Smother
pkg_smother_commit = master

PACKAGES += social
pkg_social_name = social
pkg_social_description = Cowboy handler for social login via OAuth2 providers
pkg_social_homepage = https://github.com/dvv/social
pkg_social_fetch = git
pkg_social_repo = https://github.com/dvv/social
pkg_social_commit = master

PACKAGES += spapi_router
pkg_spapi_router_name = spapi_router
pkg_spapi_router_description = Partially-connected Erlang clustering
pkg_spapi_router_homepage = https://github.com/spilgames/spapi-router
pkg_spapi_router_fetch = git
pkg_spapi_router_repo = https://github.com/spilgames/spapi-router
pkg_spapi_router_commit = master

PACKAGES += sqerl
pkg_sqerl_name = sqerl
pkg_sqerl_description = An Erlang-flavoured SQL DSL
pkg_sqerl_homepage = https://github.com/hairyhum/sqerl
pkg_sqerl_fetch = git
pkg_sqerl_repo = https://github.com/hairyhum/sqerl
pkg_sqerl_commit = master

PACKAGES += srly
pkg_srly_name = srly
pkg_srly_description = Native Erlang Unix serial interface
pkg_srly_homepage = https://github.com/msantos/srly
pkg_srly_fetch = git
pkg_srly_repo = https://github.com/msantos/srly
pkg_srly_commit = master

PACKAGES += sshrpc
pkg_sshrpc_name = sshrpc
pkg_sshrpc_description = Erlang SSH RPC module (experimental)
pkg_sshrpc_homepage = https://github.com/jj1bdx/sshrpc
pkg_sshrpc_fetch = git
pkg_sshrpc_repo = https://github.com/jj1bdx/sshrpc
pkg_sshrpc_commit = master

PACKAGES += stable
pkg_stable_name = stable
pkg_stable_description = Library of assorted helpers for Cowboy web server.
pkg_stable_homepage = https://github.com/dvv/stable
pkg_stable_fetch = git
pkg_stable_repo = https://github.com/dvv/stable
pkg_stable_commit = master

PACKAGES += statebox
pkg_statebox_name = statebox
pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
pkg_statebox_homepage = https://github.com/mochi/statebox
pkg_statebox_fetch = git
pkg_statebox_repo = https://github.com/mochi/statebox
pkg_statebox_commit = master

PACKAGES += statebox_riak
pkg_statebox_riak_name = statebox_riak
pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
pkg_statebox_riak_homepage = https://github.com/mochi/statebox_riak
pkg_statebox_riak_fetch = git
pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
pkg_statebox_riak_commit = master

PACKAGES += statman
pkg_statman_name = statman
pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
pkg_statman_homepage = https://github.com/knutin/statman
pkg_statman_fetch = git
pkg_statman_repo = https://github.com/knutin/statman
pkg_statman_commit = master

PACKAGES += statsderl
pkg_statsderl_name = statsderl
pkg_statsderl_description = StatsD client (erlang)
pkg_statsderl_homepage = https://github.com/lpgauth/statsderl
pkg_statsderl_fetch = git
pkg_statsderl_repo = https://github.com/lpgauth/statsderl
pkg_statsderl_commit = master

PACKAGES += stdinout_pool
pkg_stdinout_pool_name = stdinout_pool
pkg_stdinout_pool_description = stdinout_pool    : stuff goes in, stuff goes out. there's never any miscommunication.
pkg_stdinout_pool_homepage = https://github.com/mattsta/erlang-stdinout-pool
pkg_stdinout_pool_fetch = git
pkg_stdinout_pool_repo = https://github.com/mattsta/erlang-stdinout-pool
pkg_stdinout_pool_commit = master

PACKAGES += stockdb
pkg_stockdb_name = stockdb
pkg_stockdb_description = Database for storing Stock Exchange quotes in erlang
pkg_stockdb_homepage = https://github.com/maxlapshin/stockdb
pkg_stockdb_fetch = git
pkg_stockdb_repo = https://github.com/maxlapshin/stockdb
pkg_stockdb_commit = master

PACKAGES += stripe
pkg_stripe_name = stripe
pkg_stripe_description = Erlang interface to the stripe.com API
pkg_stripe_homepage = https://github.com/mattsta/stripe-erlang
pkg_stripe_fetch = git
pkg_stripe_repo = https://github.com/mattsta/stripe-erlang
pkg_stripe_commit = v1

PACKAGES += surrogate
pkg_surrogate_name = surrogate
pkg_surrogate_description = Proxy server written in erlang. Supports reverse proxy load balancing and forward proxy with http (including CONNECT), socks4, socks5, and transparent proxy modes.
pkg_surrogate_homepage = https://github.com/skruger/Surrogate
pkg_surrogate_fetch = git
pkg_surrogate_repo = https://github.com/skruger/Surrogate
pkg_surrogate_commit = master

PACKAGES += swab
pkg_swab_name = swab
pkg_swab_description = General purpose buffer handling module
pkg_swab_homepage = https://github.com/crownedgrouse/swab
pkg_swab_fetch = git
pkg_swab_repo = https://github.com/crownedgrouse/swab
pkg_swab_commit = master

PACKAGES += swarm
pkg_swarm_name = swarm
pkg_swarm_description = Fast and simple acceptor pool for Erlang
pkg_swarm_homepage = https://github.com/jeremey/swarm
pkg_swarm_fetch = git
pkg_swarm_repo = https://github.com/jeremey/swarm
pkg_swarm_commit = master

PACKAGES += switchboard
pkg_switchboard_name = switchboard
pkg_switchboard_description = A framework for processing email using worker plugins.
pkg_switchboard_homepage = https://github.com/thusfresh/switchboard
pkg_switchboard_fetch = git
pkg_switchboard_repo = https://github.com/thusfresh/switchboard
pkg_switchboard_commit = master

PACKAGES += sync
pkg_sync_name = sync
pkg_sync_description = On-the-fly recompiling and reloading in Erlang.
pkg_sync_homepage = https://github.com/rustyio/sync
pkg_sync_fetch = git
pkg_sync_repo = https://github.com/rustyio/sync
pkg_sync_commit = master

PACKAGES += syn
pkg_syn_name = syn
pkg_syn_description = A global process registry for Erlang.
pkg_syn_homepage = https://github.com/ostinelli/syn
pkg_syn_fetch = git
pkg_syn_repo = https://github.com/ostinelli/syn
pkg_syn_commit = master

PACKAGES += syntaxerl
pkg_syntaxerl_name = syntaxerl
pkg_syntaxerl_description = Syntax checker for Erlang
pkg_syntaxerl_homepage = https://github.com/ten0s/syntaxerl
pkg_syntaxerl_fetch = git
pkg_syntaxerl_repo = https://github.com/ten0s/syntaxerl
pkg_syntaxerl_commit = master

PACKAGES += syslog
pkg_syslog_name = syslog
pkg_syslog_description = Erlang port driver for interacting with syslog via syslog(3)
pkg_syslog_homepage = https://github.com/Vagabond/erlang-syslog
pkg_syslog_fetch = git
pkg_syslog_repo = https://github.com/Vagabond/erlang-syslog
pkg_syslog_commit = master

PACKAGES += taskforce
pkg_taskforce_name = taskforce
pkg_taskforce_description = Erlang worker pools for controlled parallelisation of arbitrary tasks.
pkg_taskforce_homepage = https://github.com/g-andrade/taskforce
pkg_taskforce_fetch = git
pkg_taskforce_repo = https://github.com/g-andrade/taskforce
pkg_taskforce_commit = master

PACKAGES += tddreloader
pkg_tddreloader_name = tddreloader
pkg_tddreloader_description = Shell utility for recompiling, reloading, and testing code as it changes
pkg_tddreloader_homepage = https://github.com/version2beta/tddreloader
pkg_tddreloader_fetch = git
pkg_tddreloader_repo = https://github.com/version2beta/tddreloader
pkg_tddreloader_commit = master

PACKAGES += tempo
pkg_tempo_name = tempo
pkg_tempo_description = NIF-based date and time parsing and formatting for Erlang.
pkg_tempo_homepage = https://github.com/selectel/tempo
pkg_tempo_fetch = git
pkg_tempo_repo = https://github.com/selectel/tempo
pkg_tempo_commit = master

PACKAGES += ticktick
pkg_ticktick_name = ticktick
pkg_ticktick_description = Ticktick is an id generator for message service.
pkg_ticktick_homepage = https://github.com/ericliang/ticktick
pkg_ticktick_fetch = git
pkg_ticktick_repo = https://github.com/ericliang/ticktick
pkg_ticktick_commit = master

PACKAGES += tinymq
pkg_tinymq_name = tinymq
pkg_tinymq_description = TinyMQ - a diminutive, in-memory message queue
pkg_tinymq_homepage = https://github.com/ChicagoBoss/tinymq
pkg_tinymq_fetch = git
pkg_tinymq_repo = https://github.com/ChicagoBoss/tinymq
pkg_tinymq_commit = master

PACKAGES += tinymt
pkg_tinymt_name = tinymt
pkg_tinymt_description = TinyMT pseudo random number generator for Erlang.
pkg_tinymt_homepage = https://github.com/jj1bdx/tinymt-erlang
pkg_tinymt_fetch = git
pkg_tinymt_repo = https://github.com/jj1bdx/tinymt-erlang
pkg_tinymt_commit = master

PACKAGES += tirerl
pkg_tirerl_name = tirerl
pkg_tirerl_description = Erlang interface to Elastic Search
pkg_tirerl_homepage = https://github.com/inaka/tirerl
pkg_tirerl_fetch = git
pkg_tirerl_repo = https://github.com/inaka/tirerl
pkg_tirerl_commit = master

PACKAGES += traffic_tools
pkg_traffic_tools_name = traffic_tools
pkg_traffic_tools_description = Simple traffic limiting library
pkg_traffic_tools_homepage = https://github.com/systra/traffic_tools
pkg_traffic_tools_fetch = git
pkg_traffic_tools_repo = https://github.com/systra/traffic_tools
pkg_traffic_tools_commit = master

PACKAGES += trails
pkg_trails_name = trails
pkg_trails_description = A couple of improvements over Cowboy Routes
pkg_trails_homepage = http://inaka.github.io/cowboy-trails/
pkg_trails_fetch = git
pkg_trails_repo = https://github.com/inaka/cowboy-trails
pkg_trails_commit = master

PACKAGES += trane
pkg_trane_name = trane
pkg_trane_description = SAX style broken HTML parser in Erlang
pkg_trane_homepage = https://github.com/massemanet/trane
pkg_trane_fetch = git
pkg_trane_repo = https://github.com/massemanet/trane
pkg_trane_commit = master

PACKAGES += transit
pkg_transit_name = transit
pkg_transit_description = transit format for erlang
pkg_transit_homepage = https://github.com/isaiah/transit-erlang
pkg_transit_fetch = git
pkg_transit_repo = https://github.com/isaiah/transit-erlang
pkg_transit_commit = master

PACKAGES += trie
pkg_trie_name = trie
pkg_trie_description = Erlang Trie Implementation
pkg_trie_homepage = https://github.com/okeuday/trie
pkg_trie_fetch = git
pkg_trie_repo = https://github.com/okeuday/trie
pkg_trie_commit = master

PACKAGES += triq
pkg_triq_name = triq
pkg_triq_description = Trifork QuickCheck
pkg_triq_homepage = https://github.com/krestenkrab/triq
pkg_triq_fetch = git
pkg_triq_repo = https://github.com/krestenkrab/triq
pkg_triq_commit = master

PACKAGES += tunctl
pkg_tunctl_name = tunctl
pkg_tunctl_description = Erlang TUN/TAP interface
pkg_tunctl_homepage = https://github.com/msantos/tunctl
pkg_tunctl_fetch = git
pkg_tunctl_repo = https://github.com/msantos/tunctl
pkg_tunctl_commit = master

PACKAGES += twerl
pkg_twerl_name = twerl
pkg_twerl_description = Erlang client for the Twitter Streaming API
pkg_twerl_homepage = https://github.com/lucaspiller/twerl
pkg_twerl_fetch = git
pkg_twerl_repo = https://github.com/lucaspiller/twerl
pkg_twerl_commit = oauth

PACKAGES += twitter_erlang
pkg_twitter_erlang_name = twitter_erlang
pkg_twitter_erlang_description = An Erlang twitter client
pkg_twitter_erlang_homepage = https://github.com/ngerakines/erlang_twitter
pkg_twitter_erlang_fetch = git
pkg_twitter_erlang_repo = https://github.com/ngerakines/erlang_twitter
pkg_twitter_erlang_commit = master

PACKAGES += ucol_nif
pkg_ucol_nif_name = ucol_nif
pkg_ucol_nif_description = ICU based collation Erlang module
pkg_ucol_nif_homepage = https://github.com/refuge/ucol_nif
pkg_ucol_nif_fetch = git
pkg_ucol_nif_repo = https://github.com/refuge/ucol_nif
pkg_ucol_nif_commit = master

PACKAGES += unicorn
pkg_unicorn_name = unicorn
pkg_unicorn_description = Generic configuration server
pkg_unicorn_homepage = https://github.com/shizzard/unicorn
pkg_unicorn_fetch = git
pkg_unicorn_repo = https://github.com/shizzard/unicorn
pkg_unicorn_commit = 0.3.0

PACKAGES += unsplit
pkg_unsplit_name = unsplit
pkg_unsplit_description = Resolves conflicts in Mnesia after network splits
pkg_unsplit_homepage = https://github.com/uwiger/unsplit
pkg_unsplit_fetch = git
pkg_unsplit_repo = https://github.com/uwiger/unsplit
pkg_unsplit_commit = master

PACKAGES += uuid
pkg_uuid_name = uuid
pkg_uuid_description = Erlang UUID Implementation
pkg_uuid_homepage = https://github.com/okeuday/uuid
pkg_uuid_fetch = git
pkg_uuid_repo = https://github.com/okeuday/uuid
pkg_uuid_commit = v1.4.0

PACKAGES += ux
pkg_ux_name = ux
pkg_ux_description = Unicode eXtention for Erlang (Strings, Collation)
pkg_ux_homepage = https://github.com/erlang-unicode/ux
pkg_ux_fetch = git
pkg_ux_repo = https://github.com/erlang-unicode/ux
pkg_ux_commit = master

PACKAGES += vert
pkg_vert_name = vert
pkg_vert_description = Erlang binding to libvirt virtualization API
pkg_vert_homepage = https://github.com/msantos/erlang-libvirt
pkg_vert_fetch = git
pkg_vert_repo = https://github.com/msantos/erlang-libvirt
pkg_vert_commit = master

PACKAGES += verx
pkg_verx_name = verx
pkg_verx_description = Erlang implementation of the libvirtd remote protocol
pkg_verx_homepage = https://github.com/msantos/verx
pkg_verx_fetch = git
pkg_verx_repo = https://github.com/msantos/verx
pkg_verx_commit = master

PACKAGES += vmq_acl
pkg_vmq_acl_name = vmq_acl
pkg_vmq_acl_description = Component of VerneMQ: A distributed MQTT message broker
pkg_vmq_acl_homepage = https://verne.mq/
pkg_vmq_acl_fetch = git
pkg_vmq_acl_repo = https://github.com/erlio/vmq_acl
pkg_vmq_acl_commit = master

PACKAGES += vmq_bridge
pkg_vmq_bridge_name = vmq_bridge
pkg_vmq_bridge_description = Component of VerneMQ: A distributed MQTT message broker
pkg_vmq_bridge_homepage = https://verne.mq/
pkg_vmq_bridge_fetch = git
pkg_vmq_bridge_repo = https://github.com/erlio/vmq_bridge
pkg_vmq_bridge_commit = master

PACKAGES += vmq_graphite
pkg_vmq_graphite_name = vmq_graphite
pkg_vmq_graphite_description = Component of VerneMQ: A distributed MQTT message broker
pkg_vmq_graphite_homepage = https://verne.mq/
pkg_vmq_graphite_fetch = git
pkg_vmq_graphite_repo = https://github.com/erlio/vmq_graphite
pkg_vmq_graphite_commit = master

PACKAGES += vmq_passwd
pkg_vmq_passwd_name = vmq_passwd
pkg_vmq_passwd_description = Component of VerneMQ: A distributed MQTT message broker
pkg_vmq_passwd_homepage = https://verne.mq/
pkg_vmq_passwd_fetch = git
pkg_vmq_passwd_repo = https://github.com/erlio/vmq_passwd
pkg_vmq_passwd_commit = master

PACKAGES += vmq_server
pkg_vmq_server_name = vmq_server
pkg_vmq_server_description = Component of VerneMQ: A distributed MQTT message broker
pkg_vmq_server_homepage = https://verne.mq/
pkg_vmq_server_fetch = git
pkg_vmq_server_repo = https://github.com/erlio/vmq_server
pkg_vmq_server_commit = master

PACKAGES += vmq_snmp
pkg_vmq_snmp_name = vmq_snmp
pkg_vmq_snmp_description = Component of VerneMQ: A distributed MQTT message broker
pkg_vmq_snmp_homepage = https://verne.mq/
pkg_vmq_snmp_fetch = git
pkg_vmq_snmp_repo = https://github.com/erlio/vmq_snmp
pkg_vmq_snmp_commit = master

PACKAGES += vmq_systree
pkg_vmq_systree_name = vmq_systree
pkg_vmq_systree_description = Component of VerneMQ: A distributed MQTT message broker
pkg_vmq_systree_homepage = https://verne.mq/
pkg_vmq_systree_fetch = git
pkg_vmq_systree_repo = https://github.com/erlio/vmq_systree
pkg_vmq_systree_commit = master

PACKAGES += vmstats
pkg_vmstats_name = vmstats
pkg_vmstats_description = tiny Erlang app that works in conjunction with statsderl in order to generate information on the Erlang VM for graphite logs.
pkg_vmstats_homepage = https://github.com/ferd/vmstats
pkg_vmstats_fetch = git
pkg_vmstats_repo = https://github.com/ferd/vmstats
pkg_vmstats_commit = master

PACKAGES += walrus
pkg_walrus_name = walrus
pkg_walrus_description = Walrus - Mustache-like Templating
pkg_walrus_homepage = https://github.com/devinus/walrus
pkg_walrus_fetch = git
pkg_walrus_repo = https://github.com/devinus/walrus
pkg_walrus_commit = master

PACKAGES += webmachine
pkg_webmachine_name = webmachine
pkg_webmachine_description = A REST-based system for building web applications.
pkg_webmachine_homepage = https://github.com/basho/webmachine
pkg_webmachine_fetch = git
pkg_webmachine_repo = https://github.com/basho/webmachine
pkg_webmachine_commit = master

PACKAGES += websocket_client
pkg_websocket_client_name = websocket_client
pkg_websocket_client_description = Erlang websocket client (ws and wss supported)
pkg_websocket_client_homepage = https://github.com/jeremyong/websocket_client
pkg_websocket_client_fetch = git
pkg_websocket_client_repo = https://github.com/jeremyong/websocket_client
pkg_websocket_client_commit = master

PACKAGES += worker_pool
pkg_worker_pool_name = worker_pool
pkg_worker_pool_description = a simple erlang worker pool
pkg_worker_pool_homepage = https://github.com/inaka/worker_pool
pkg_worker_pool_fetch = git
pkg_worker_pool_repo = https://github.com/inaka/worker_pool
pkg_worker_pool_commit = 1.0.2

PACKAGES += wrangler
pkg_wrangler_name = wrangler
pkg_wrangler_description = Import of the Wrangler svn repository.
pkg_wrangler_homepage = http://www.cs.kent.ac.uk/projects/wrangler/Home.html
pkg_wrangler_fetch = git
pkg_wrangler_repo = https://github.com/RefactoringTools/wrangler
pkg_wrangler_commit = master

PACKAGES += wsock
pkg_wsock_name = wsock
pkg_wsock_description = Erlang library to build WebSocket clients and servers
pkg_wsock_homepage = https://github.com/madtrick/wsock
pkg_wsock_fetch = git
pkg_wsock_repo = https://github.com/madtrick/wsock
pkg_wsock_commit = master

PACKAGES += xhttpc
pkg_xhttpc_name = xhttpc
pkg_xhttpc_description = Extensible HTTP Client for Erlang
pkg_xhttpc_homepage = https://github.com/seriyps/xhttpc
pkg_xhttpc_fetch = git
pkg_xhttpc_repo = https://github.com/seriyps/xhttpc
pkg_xhttpc_commit = master

PACKAGES += xref_runner
pkg_xref_runner_name = xref_runner
pkg_xref_runner_description = Erlang Xref Runner (inspired in rebar xref)
pkg_xref_runner_homepage = https://github.com/inaka/xref_runner
pkg_xref_runner_fetch = git
pkg_xref_runner_repo = https://github.com/inaka/xref_runner
pkg_xref_runner_commit = 0.2.0

PACKAGES += yamerl
pkg_yamerl_name = yamerl
pkg_yamerl_description = YAML 1.2 parser in pure Erlang
pkg_yamerl_homepage = https://github.com/yakaz/yamerl
pkg_yamerl_fetch = git
pkg_yamerl_repo = https://github.com/yakaz/yamerl
pkg_yamerl_commit = master

PACKAGES += yamler
pkg_yamler_name = yamler
pkg_yamler_description = libyaml-based yaml loader for Erlang
pkg_yamler_homepage = https://github.com/goertzenator/yamler
pkg_yamler_fetch = git
pkg_yamler_repo = https://github.com/goertzenator/yamler
pkg_yamler_commit = master

PACKAGES += yaws
pkg_yaws_name = yaws
pkg_yaws_description = Yaws webserver
pkg_yaws_homepage = http://yaws.hyber.org
pkg_yaws_fetch = git
pkg_yaws_repo = https://github.com/klacke/yaws
pkg_yaws_commit = master

PACKAGES += zab_engine
pkg_zab_engine_name = zab_engine
pkg_zab_engine_description = zab propotocol implement by erlang
pkg_zab_engine_homepage = https://github.com/xinmingyao/zab_engine
pkg_zab_engine_fetch = git
pkg_zab_engine_repo = https://github.com/xinmingyao/zab_engine
pkg_zab_engine_commit = master

PACKAGES += zeta
pkg_zeta_name = zeta
pkg_zeta_description = HTTP access log parser in Erlang
pkg_zeta_homepage = https://github.com/s1n4/zeta
pkg_zeta_fetch = git
pkg_zeta_repo = https://github.com/s1n4/zeta
pkg_zeta_commit =  

PACKAGES += zippers
pkg_zippers_name = zippers
pkg_zippers_description = A library for functional zipper data structures in Erlang. Read more on zippers
pkg_zippers_homepage = https://github.com/ferd/zippers
pkg_zippers_fetch = git
pkg_zippers_repo = https://github.com/ferd/zippers
pkg_zippers_commit = master

PACKAGES += zlists
pkg_zlists_name = zlists
pkg_zlists_description = Erlang lazy lists library.
pkg_zlists_homepage = https://github.com/vjache/erlang-zlists
pkg_zlists_fetch = git
pkg_zlists_repo = https://github.com/vjache/erlang-zlists
pkg_zlists_commit = master

PACKAGES += zraft_lib
pkg_zraft_lib_name = zraft_lib
pkg_zraft_lib_description = Erlang raft consensus protocol implementation
pkg_zraft_lib_homepage = https://github.com/dreyk/zraft_lib
pkg_zraft_lib_fetch = git
pkg_zraft_lib_repo = https://github.com/dreyk/zraft_lib
pkg_zraft_lib_commit = master

PACKAGES += zucchini
pkg_zucchini_name = zucchini
pkg_zucchini_description = An Erlang INI parser
pkg_zucchini_homepage = https://github.com/devinus/zucchini
pkg_zucchini_fetch = git
pkg_zucchini_repo = https://github.com/devinus/zucchini
pkg_zucchini_commit = master

# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: search

define pkg_print
	$(verbose) printf "%s\n" \
		$(if $(call core_eq,$(1),$(pkg_$(1)_name)),,"Pkg name:    $(1)") \
		"App name:    $(pkg_$(1)_name)" \
		"Description: $(pkg_$(1)_description)" \
		"Home page:   $(pkg_$(1)_homepage)" \
		"Fetch with:  $(pkg_$(1)_fetch)" \
		"Repository:  $(pkg_$(1)_repo)" \
		"Commit:      $(pkg_$(1)_commit)" \
		""

endef

search:
ifdef q
	$(foreach p,$(PACKAGES), \
		$(if $(findstring $(call core_lc,$(q)),$(call core_lc,$(pkg_$(p)_name) $(pkg_$(p)_description))), \
			$(call pkg_print,$(p))))
else
	$(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
endif

# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: distclean-deps distclean-pkg

# Configuration.

IGNORE_DEPS ?=

DEPS_DIR ?= $(CURDIR)/deps
export DEPS_DIR

REBAR_DEPS_DIR = $(DEPS_DIR)
export REBAR_DEPS_DIR

ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(filter-out $(IGNORE_DEPS),$(DEPS)))

ifeq ($(filter $(DEPS_DIR),$(subst :, ,$(ERL_LIBS))),)
ifeq ($(ERL_LIBS),)
	ERL_LIBS = $(DEPS_DIR)
else
	ERL_LIBS := $(ERL_LIBS):$(DEPS_DIR)
endif
endif
export ERL_LIBS

# Verbosity.

dep_verbose_0 = @echo " DEP   " $(1);
dep_verbose = $(dep_verbose_$(V))

# Core targets.

ifneq ($(SKIP_DEPS),)
deps::
else
deps:: $(ALL_DEPS_DIRS)
ifneq ($(IS_DEP),1)
	$(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
endif
	$(verbose) mkdir -p $(ERLANG_MK_TMP)
	$(verbose) for dep in $(ALL_DEPS_DIRS) ; do \
		if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
			echo -n; \
		else \
			echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
			if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
				$(MAKE) -C $$dep IS_DEP=1 || exit $$?; \
			else \
				echo "ERROR: No Makefile to build dependency $$dep."; \
				exit 1; \
			fi \
		fi \
	done
endif

distclean:: distclean-deps distclean-pkg

# Deps related targets.

# @todo rename GNUmakefile and makefile into Makefile first, if they exist
# While Makefile file could be GNUmakefile or makefile,
# in practice only Makefile is needed so far.
define dep_autopatch
	if [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
		if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
			$(call dep_autopatch2,$(1)); \
		elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \
			$(call dep_autopatch2,$(1)); \
		elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk | xargs -r grep -i rebar`" ]; then \
			$(call dep_autopatch2,$(1)); \
		else \
			if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
				$(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
				$(call dep_autopatch_erlang_mk,$(1)); \
			else \
				$(call erlang,$(call dep_autopatch_app.erl,$(1))); \
			fi \
		fi \
	else \
		if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
			$(call dep_autopatch_noop,$(1)); \
		else \
			$(call dep_autopatch2,$(1)); \
		fi \
	fi
endef

define dep_autopatch2
	$(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
	if [ -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
		$(call dep_autopatch_fetch_rebar); \
		$(call dep_autopatch_rebar,$(1)); \
	else \
		$(call dep_autopatch_gen,$(1)); \
	fi
endef

define dep_autopatch_noop
	printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
endef

# Overwrite erlang.mk with the current file by default.
ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
define dep_autopatch_erlang_mk
	echo "include $(ERLANG_MK_FILENAME)" > $(DEPS_DIR)/$(1)/erlang.mk
endef
else
define dep_autopatch_erlang_mk
	echo -n
endef
endif

define dep_autopatch_gen
	printf "%s\n" \
		"ERLC_OPTS = +debug_info" \
		"include ../../erlang.mk" > $(DEPS_DIR)/$(1)/Makefile
endef

define dep_autopatch_fetch_rebar
	mkdir -p $(ERLANG_MK_TMP); \
	if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
		git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
		cd $(ERLANG_MK_TMP)/rebar; \
		git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
		$(MAKE); \
		cd -; \
	fi
endef

define dep_autopatch_rebar
	if [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
		mv $(DEPS_DIR)/$(1)/Makefile $(DEPS_DIR)/$(1)/Makefile.orig.mk; \
	fi; \
	$(call erlang,$(call dep_autopatch_rebar.erl,$(1))); \
	rm -f $(DEPS_DIR)/$(1)/ebin/$(1).app
endef

define dep_autopatch_rebar.erl
	application:set_env(rebar, log_level, debug),
	Conf1 = case file:consult("$(DEPS_DIR)/$(1)/rebar.config") of
		{ok, Conf0} -> Conf0;
		_ -> []
	end,
	{Conf, OsEnv} = fun() ->
		case filelib:is_file("$(DEPS_DIR)/$(1)/rebar.config.script") of
			false -> {Conf1, []};
			true ->
				Bindings0 = erl_eval:new_bindings(),
				Bindings1 = erl_eval:add_binding('CONFIG', Conf1, Bindings0),
				Bindings = erl_eval:add_binding('SCRIPT', "$(DEPS_DIR)/$(1)/rebar.config.script", Bindings1),
				Before = os:getenv(),
				{ok, Conf2} = file:script("$(DEPS_DIR)/$(1)/rebar.config.script", Bindings),
				{Conf2, lists:foldl(fun(E, Acc) -> lists:delete(E, Acc) end, os:getenv(), Before)}
		end
	end(),
	Write = fun (Text) ->
		file:write_file("$(DEPS_DIR)/$(1)/Makefile", Text, [append])
	end,
	Escape = fun (Text) ->
		re:replace(Text, "\\\\$$$$", "\$$$$$$$$", [global, {return, list}])
	end,
	Write("IGNORE_DEPS = edown eper eunit_formatters meck node_package "
		"rebar_lock_deps_plugin rebar_vsn_plugin reltool_util\n"),
	Write("C_SRC_DIR = /path/do/not/exist\n"),
	Write("DRV_CFLAGS = -fPIC\nexport DRV_CFLAGS\n"),
	Write(["ERLANG_ARCH = ", rebar_utils:wordsize(), "\nexport ERLANG_ARCH\n"]),
	fun() ->
		Write("ERLC_OPTS = +debug_info\nexport ERLC_OPTS\n"),
		case lists:keyfind(erl_opts, 1, Conf) of
			false -> ok;
			{_, ErlOpts} ->
				lists:foreach(fun
					({d, D}) ->
						Write("ERLC_OPTS += -D" ++ atom_to_list(D) ++ "=1\n");
					({i, I}) ->
						Write(["ERLC_OPTS += -I ", I, "\n"]);
					({platform_define, Regex, D}) ->
						case rebar_utils:is_arch(Regex) of
							true -> Write("ERLC_OPTS += -D" ++ atom_to_list(D) ++ "=1\n");
							false -> ok
						end;
					({parse_transform, PT}) ->
						Write("ERLC_OPTS += +'{parse_transform, " ++ atom_to_list(PT) ++ "}'\n");
					(_) -> ok
				end, ErlOpts)
		end,
		Write("\n")
	end(),
	fun() ->
		File = case lists:keyfind(deps, 1, Conf) of
			false -> [];
			{_, Deps} ->
				[begin case case Dep of
							{N, S} when is_atom(N), is_list(S) -> {N, {hex, S}};
							{N, S} when is_tuple(S) -> {N, S};
							{N, _, S} -> {N, S};
							{N, _, S, _} -> {N, S};
							_ -> false
						end of
					false -> ok;
					{Name, Source} ->
						{Method, Repo, Commit} = case Source of
							{hex, V} -> {hex, undefined, V};
							{git, R} -> {git, R, master};
							{M, R, {branch, C}} -> {M, R, C};
							{M, R, {ref, C}} -> {M, R, C};
							{M, R, {tag, C}} -> {M, R, C};
							{M, R, C} -> {M, R, C}
						end,
						Write(io_lib:format("DEPS += ~s\ndep_~s = ~s ~s ~s~n", [Name, Name, Method, Repo, Commit]))
				end end || Dep <- Deps]
		end
	end(),
	fun() ->
		case lists:keyfind(erl_first_files, 1, Conf) of
			false -> ok;
			{_, Files} ->
				Names = [[" ", case lists:reverse(F) of
					"lre." ++ Elif -> lists:reverse(Elif);
					Elif -> lists:reverse(Elif)
				end] || "src/" ++ F <- Files],
				Write(io_lib:format("COMPILE_FIRST +=~s\n", [Names]))
		end
	end(),
	FindFirst = fun(F, Fd) ->
		case io:parse_erl_form(Fd, undefined) of
			{ok, {attribute, _, compile, {parse_transform, PT}}, _} ->
				[PT, F(F, Fd)];
			{ok, {attribute, _, compile, CompileOpts}, _} when is_list(CompileOpts) ->
				case proplists:get_value(parse_transform, CompileOpts) of
					undefined -> [F(F, Fd)];
					PT -> [PT, F(F, Fd)]
				end;
			{ok, {attribute, _, include, Hrl}, _} ->
				case file:open("$(DEPS_DIR)/$(1)/include/" ++ Hrl, [read]) of
					{ok, HrlFd} -> [F(F, HrlFd), F(F, Fd)];
					_ ->
						case file:open("$(DEPS_DIR)/$(1)/src/" ++ Hrl, [read]) of
							{ok, HrlFd} -> [F(F, HrlFd), F(F, Fd)];
							_ -> [F(F, Fd)]
						end
				end;
			{ok, {attribute, _, include_lib, "$(1)/include/" ++ Hrl}, _} ->
				{ok, HrlFd} = file:open("$(DEPS_DIR)/$(1)/include/" ++ Hrl, [read]),
				[F(F, HrlFd), F(F, Fd)];
			{ok, {attribute, _, include_lib, Hrl}, _} ->
				case file:open("$(DEPS_DIR)/$(1)/include/" ++ Hrl, [read]) of
					{ok, HrlFd} -> [F(F, HrlFd), F(F, Fd)];
					_ -> [F(F, Fd)]
				end;
			{ok, {attribute, _, import, {Imp, _}}, _} ->
				case file:open("$(DEPS_DIR)/$(1)/src/" ++ atom_to_list(Imp) ++ ".erl", [read]) of
					{ok, ImpFd} -> [Imp, F(F, ImpFd), F(F, Fd)];
					_ -> [F(F, Fd)]
				end;
			{eof, _} ->
				file:close(Fd),
				[];
			_ ->
				F(F, Fd)
		end
	end,
	fun() ->
		ErlFiles = filelib:wildcard("$(DEPS_DIR)/$(1)/src/*.erl"),
		First0 = lists:usort(lists:flatten([begin
			{ok, Fd} = file:open(F, [read]),
			FindFirst(FindFirst, Fd)
		end || F <- ErlFiles])),
		First = lists:flatten([begin
			{ok, Fd} = file:open("$(DEPS_DIR)/$(1)/src/" ++ atom_to_list(M) ++ ".erl", [read]),
			FindFirst(FindFirst, Fd)
		end || M <- First0, lists:member("$(DEPS_DIR)/$(1)/src/" ++ atom_to_list(M) ++ ".erl", ErlFiles)]) ++ First0,
		Write(["COMPILE_FIRST +=", [[" ", atom_to_list(M)] || M <- First,
			lists:member("$(DEPS_DIR)/$(1)/src/" ++ atom_to_list(M) ++ ".erl", ErlFiles)], "\n"])
	end(),
	Write("\n\nrebar_dep: preprocess pre-deps deps pre-app app\n"),
	Write("\npreprocess::\n"),
	Write("\npre-deps::\n"),
	Write("\npre-app::\n"),
	PatchHook = fun(Cmd) ->
		case Cmd of
			"make -C" ++ Cmd1 -> "$$$$\(MAKE) -C" ++ Escape(Cmd1);
			"gmake -C" ++ Cmd1 -> "$$$$\(MAKE) -C" ++ Escape(Cmd1);
			"make " ++ Cmd1 -> "$$$$\(MAKE) -f Makefile.orig.mk " ++ Escape(Cmd1);
			"gmake " ++ Cmd1 -> "$$$$\(MAKE) -f Makefile.orig.mk " ++ Escape(Cmd1);
			_ -> Escape(Cmd)
		end
	end,
	fun() ->
		case lists:keyfind(pre_hooks, 1, Conf) of
			false -> ok;
			{_, Hooks} ->
				[case H of
					{'get-deps', Cmd} ->
						Write("\npre-deps::\n\t" ++ PatchHook(Cmd) ++ "\n");
					{compile, Cmd} ->
						Write("\npre-app::\n\tCC=$$$$\(CC) " ++ PatchHook(Cmd) ++ "\n");
					{Regex, compile, Cmd} ->
						case rebar_utils:is_arch(Regex) of
							true -> Write("\npre-app::\n\tCC=$$$$\(CC) " ++ PatchHook(Cmd) ++ "\n");
							false -> ok
						end;
					_ -> ok
				end || H <- Hooks]
		end
	end(),
	ShellToMk = fun(V) ->
		re:replace(re:replace(V, "(\\\\$$$$)(\\\\w*)", "\\\\1(\\\\2)", [global]),
			"-Werror\\\\b", "", [{return, list}, global])
	end,
	PortSpecs = fun() ->
		case lists:keyfind(port_specs, 1, Conf) of
			false ->
				case filelib:is_dir("$(DEPS_DIR)/$(1)/c_src") of
					false -> [];
					true ->
						[{"priv/" ++ proplists:get_value(so_name, Conf, "$(1)_drv.so"),
							proplists:get_value(port_sources, Conf, ["c_src/*.c"]), []}]
				end;
			{_, Specs} ->
				lists:flatten([case S of
					{Output, Input} -> {ShellToMk(Output), Input, []};
					{Regex, Output, Input} ->
						case rebar_utils:is_arch(Regex) of
							true -> {ShellToMk(Output), Input, []};
							false -> []
						end;
					{Regex, Output, Input, [{env, Env}]} ->
						case rebar_utils:is_arch(Regex) of
							true -> {ShellToMk(Output), Input, Env};
							false -> []
						end
				end || S <- Specs])
		end
	end(),
	PortSpecWrite = fun (Text) ->
		file:write_file("$(DEPS_DIR)/$(1)/c_src/Makefile.erlang.mk", Text, [append])
	end,
	case PortSpecs of
		[] -> ok;
		_ ->
			Write("\npre-app::\n\t$$$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
			PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I ~s/erts-~s/include -I ~s\n",
				[code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
			PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L ~s -lerl_interface -lei\n",
				[code:lib_dir(erl_interface, lib)])),
			[PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
			FilterEnv = fun(Env) ->
				lists:flatten([case E of
					{_, _} -> E;
					{Regex, K, V} ->
						case rebar_utils:is_arch(Regex) of
							true -> {K, V};
							false -> []
						end
				end || E <- Env])
			end,
			MergeEnv = fun(Env) ->
				lists:foldl(fun ({K, V}, Acc) ->
					case lists:keyfind(K, 1, Acc) of
						false -> [{K, rebar_utils:expand_env_variable(V, K, "")}|Acc];
						{_, V0} -> [{K, rebar_utils:expand_env_variable(V, K, V0)}|Acc]
					end
				end, [], Env)
			end,
			PortEnv = case lists:keyfind(port_env, 1, Conf) of
				false -> [];
				{_, PortEnv0} -> FilterEnv(PortEnv0)
			end,
			PortSpec = fun ({Output, Input0, Env}) ->
				filelib:ensure_dir("$(DEPS_DIR)/$(1)/" ++ Output),
				Input = [[" ", I] || I <- Input0],
				PortSpecWrite([
					[["\n", K, " = ", ShellToMk(V)] || {K, V} <- lists:reverse(MergeEnv(PortEnv))],
					case $(PLATFORM) of
						darwin -> "\n\nLDFLAGS += -flat_namespace -undefined suppress";
						_ -> ""
					end,
					"\n\nall:: ", Output, "\n\n",
					"%.o: %.c\n\t$$$$\(CC) -c -o $$$$\@ $$$$\< $$$$\(CFLAGS) $$$$\(ERL_CFLAGS) $$$$\(DRV_CFLAGS) $$$$\(EXE_CFLAGS)\n\n",
					"%.o: %.C\n\t$$$$\(CXX) -c -o $$$$\@ $$$$\< $$$$\(CXXFLAGS) $$$$\(ERL_CFLAGS) $$$$\(DRV_CFLAGS) $$$$\(EXE_CFLAGS)\n\n",
					"%.o: %.cc\n\t$$$$\(CXX) -c -o $$$$\@ $$$$\< $$$$\(CXXFLAGS) $$$$\(ERL_CFLAGS) $$$$\(DRV_CFLAGS) $$$$\(EXE_CFLAGS)\n\n",
					"%.o: %.cpp\n\t$$$$\(CXX) -c -o $$$$\@ $$$$\< $$$$\(CXXFLAGS) $$$$\(ERL_CFLAGS) $$$$\(DRV_CFLAGS) $$$$\(EXE_CFLAGS)\n\n",
					[[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
					Output, ": $$$$\(foreach ext,.c .C .cc .cpp,",
						"$$$$\(patsubst %$$$$\(ext),%.o,$$$$\(filter %$$$$\(ext),$$$$\(wildcard", Input, "))))\n",
					"\t$$$$\(CC) -o $$$$\@ $$$$\? $$$$\(LDFLAGS) $$$$\(ERL_LDFLAGS) $$$$\(DRV_LDFLAGS) $$$$\(EXE_LDFLAGS)",
					case filename:extension(Output) of
						[] -> "\n";
						_ -> " -shared\n"
					end])
			end,
			[PortSpec(S) || S <- PortSpecs]
	end,
	Write("\ninclude $(ERLANG_MK_FILENAME)"),
	RunPlugin = fun(Plugin, Step) ->
		case erlang:function_exported(Plugin, Step, 2) of
			false -> ok;
			true ->
				c:cd("$(DEPS_DIR)/$(1)/"),
				Ret = Plugin:Step({config, "", Conf, dict:new(), dict:new(), dict:new(),
					dict:store(base_dir, "", dict:new())}, undefined),
				io:format("rebar plugin ~p step ~p ret ~p~n", [Plugin, Step, Ret])
		end
	end,
	fun() ->
		case lists:keyfind(plugins, 1, Conf) of
			false -> ok;
			{_, Plugins} ->
				[begin
					case lists:keyfind(deps, 1, Conf) of
						false -> ok;
						{_, Deps} ->
							case lists:keyfind(P, 1, Deps) of
								false -> ok;
								_ ->
									Path = "$(DEPS_DIR)/" ++ atom_to_list(P),
									io:format("~s", [os:cmd("$(MAKE) -C $(DEPS_DIR)/$(1) " ++ Path)]),
									io:format("~s", [os:cmd("$(MAKE) -C " ++ Path ++ " IS_DEP=1")]),
									code:add_patha(Path ++ "/ebin")
							end
					end
				end || P <- Plugins],
				[case code:load_file(P) of
					{module, P} -> ok;
					_ ->
						case lists:keyfind(plugin_dir, 1, Conf) of
							false -> ok;
							{_, PluginsDir} ->
								ErlFile = "$(DEPS_DIR)/$(1)/" ++ PluginsDir ++ "/" ++ atom_to_list(P) ++ ".erl",
								{ok, P, Bin} = compile:file(ErlFile, [binary]),
								{module, P} = code:load_binary(P, ErlFile, Bin)
						end
				end || P <- Plugins],
				[RunPlugin(P, preprocess) || P <- Plugins],
				[RunPlugin(P, pre_compile) || P <- Plugins]
		end
	end(),
	halt()
endef

define dep_autopatch_app.erl
	UpdateModules = fun(App) ->
		case filelib:is_regular(App) of
			false -> ok;
			true ->
				{ok, [{application, $(1), L0}]} = file:consult(App),
				Mods = filelib:fold_files("$(DEPS_DIR)/$(1)/src", "\\\\.erl$$$$", true,
					fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
				L = lists:keystore(modules, 1, L0, {modules, Mods}),
				ok = file:write_file(App, io_lib:format("~p.~n", [{application, $(1), L}]))
		end
	end,
	UpdateModules("$(DEPS_DIR)/$(1)/ebin/$(1).app"),
	halt()
endef

define dep_autopatch_appsrc.erl
	AppSrcOut = "$(DEPS_DIR)/$(1)/src/$(1).app.src",
	AppSrcIn = case filelib:is_regular(AppSrcOut) of false -> "$(DEPS_DIR)/$(1)/ebin/$(1).app"; true -> AppSrcOut end,
	case filelib:is_regular(AppSrcIn) of
		false -> ok;
		true ->
			{ok, [{application, $(1), L0}]} = file:consult(AppSrcIn),
			L1 = lists:keystore(modules, 1, L0, {modules, []}),
			L2 = case lists:keyfind(vsn, 1, L1) of {_, git} -> lists:keyreplace(vsn, 1, L1, {vsn, "git"}); _ -> L1 end,
			L3 = case lists:keyfind(registered, 1, L2) of false -> [{registered, []}|L2]; _ -> L2 end,
			ok = file:write_file(AppSrcOut, io_lib:format("~p.~n", [{application, $(1), L3}])),
			case AppSrcOut of AppSrcIn -> ok; _ -> ok = file:delete(AppSrcIn) end
	end,
	halt()
endef

define hex_fetch.erl
	ssl:start(),
	inets:start(),
	{ok, {{_, 200, _}, _, Body}} = httpc:request(get,
		{"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
		[], [{body_format, binary}]),
	{ok, Files} = erl_tar:extract({binary, Body}, [memory]),
	{_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
	ok = erl_tar:extract({binary, Source}, [{cwd, "$(DEPS_DIR)/$(1)"}, compressed]),
	halt()
endef

define dep_fetch
	if [ "$(2)" = "git" ]; then \
		git clone -q -n -- $(3) $(DEPS_DIR)/$(1); \
		cd $(DEPS_DIR)/$(1) && git checkout -q $(4); \
	elif [ "$(2)" = "hg" ]; then \
		hg clone -q -U $(3) $(DEPS_DIR)/$(1); \
		cd $(DEPS_DIR)/$(1) && hg update -q $(4); \
	elif [ "$(2)" = "svn" ]; then \
		svn checkout -q $(3) $(DEPS_DIR)/$(1); \
	elif [ "$(2)" = "cp" ]; then \
		cp -R $(3) $(DEPS_DIR)/$(1); \
	elif [ "$(2)" = "hex" ]; then \
		$(call erlang,$(call hex_fetch.erl,$(1),$(strip $(4)))); \
	else \
		echo "Unknown or invalid dependency: $(1). Please consult the erlang.mk README for instructions." >&2; \
		exit 78; \
	fi
endef

define dep_target
$(DEPS_DIR)/$(1):
	$(verbose) mkdir -p $(DEPS_DIR)
ifeq (,$(dep_$(1)))
	$(dep_verbose) $(call dep_fetch,$(pkg_$(1)_name),$(pkg_$(1)_fetch), \
		$(patsubst git://github.com/%,https://github.com/%,$(pkg_$(1)_repo)), \
		$(pkg_$(1)_commit))
else
ifeq (1,$(words $(dep_$(1))))
	$(dep_verbose) $(call dep_fetch,$(1),git, \
		$(patsubst git://github.com/%,https://github.com/%,$(dep_$(1))), \
		master)
else
ifeq (2,$(words $(dep_$(1))))
	$(dep_verbose) $(call dep_fetch,$(1),git, \
		$(patsubst git://github.com/%,https://github.com/%,$(word 1,$(dep_$(1)))), \
		$(word 2,$(dep_$(1))))
else
	$(dep_verbose) $(call dep_fetch,$(1),$(word 1,$(dep_$(1))), \
		$(patsubst git://github.com/%,https://github.com/%,$(word 2,$(dep_$(1)))), \
		$(word 3,$(dep_$(1))))
endif
endif
endif
	$(verbose) if [ -f $(DEPS_DIR)/$(1)/configure.ac -o -f $(DEPS_DIR)/$(1)/configure.in ]; then \
		echo " AUTO  " $(1); \
		cd $(DEPS_DIR)/$(1) && autoreconf -Wall -vif -I m4; \
	fi
	- $(verbose) if [ -f $(DEPS_DIR)/$(1)/configure ]; then \
		echo " CONF  " $(1); \
		cd $(DEPS_DIR)/$(1) && ./configure; \
	fi
ifeq ($(filter $(1),$(NO_AUTOPATCH)),)
	$(verbose) if [ "$(1)" = "amqp_client" -a "$(RABBITMQ_CLIENT_PATCH)" ]; then \
		if [ ! -d $(DEPS_DIR)/rabbitmq-codegen ]; then \
			echo " PATCH  Downloading rabbitmq-codegen"; \
			git clone https://github.com/rabbitmq/rabbitmq-codegen.git $(DEPS_DIR)/rabbitmq-codegen; \
		fi; \
		if [ ! -d $(DEPS_DIR)/rabbitmq-server ]; then \
			echo " PATCH  Downloading rabbitmq-server"; \
			git clone https://github.com/rabbitmq/rabbitmq-server.git $(DEPS_DIR)/rabbitmq-server; \
		fi; \
		ln -s $(DEPS_DIR)/amqp_client/deps/rabbit_common-0.0.0 $(DEPS_DIR)/rabbit_common; \
	elif [ "$(1)" = "rabbit" -a "$(RABBITMQ_SERVER_PATCH)" ]; then \
		if [ ! -d $(DEPS_DIR)/rabbitmq-codegen ]; then \
			echo " PATCH  Downloading rabbitmq-codegen"; \
			git clone https://github.com/rabbitmq/rabbitmq-codegen.git $(DEPS_DIR)/rabbitmq-codegen; \
		fi \
	else \
		$(call dep_autopatch,$(1)) \
	fi
endif
endef

$(foreach dep,$(DEPS),$(eval $(call dep_target,$(dep))))

distclean-deps:
	$(gen_verbose) rm -rf $(DEPS_DIR)

# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

# Verbosity.

proto_verbose_0 = @echo " PROTO " $(filter %.proto,$(?F));
proto_verbose = $(proto_verbose_$(V))

# Core targets.

define compile_proto
	$(verbose) mkdir -p ebin/ include/
	$(proto_verbose) $(call erlang,$(call compile_proto.erl,$(1)))
	$(proto_verbose) erlc +debug_info -o ebin/ ebin/*.erl
	$(verbose) rm ebin/*.erl
endef

define compile_proto.erl
	[begin
		Dir = filename:dirname(filename:dirname(F)),
		protobuffs_compile:generate_source(F,
			[{output_include_dir, Dir ++ "/include"},
				{output_src_dir, Dir ++ "/ebin"}])
	end || F <- string:tokens("$(1)", " ")],
	halt().
endef

ifneq ($(wildcard src/),)
ebin/$(PROJECT).app:: $(sort $(call core_find,src/,*.proto))
	$(if $(strip $?),$(call compile_proto,$?))
endif

# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: clean-app

# Configuration.

ERLC_OPTS ?= -Werror +debug_info +warn_export_vars +warn_shadow_vars \
	+warn_obsolete_guard # +bin_opt_info +warn_export_all +warn_missing_spec
COMPILE_FIRST ?=
COMPILE_FIRST_PATHS = $(addprefix src/,$(addsuffix .erl,$(COMPILE_FIRST)))
ERLC_EXCLUDE ?=
ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))

ERLC_MIB_OPTS ?=
COMPILE_MIB_FIRST ?=
COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))

# Verbosity.

app_verbose_0 = @echo " APP   " $(PROJECT);
app_verbose = $(app_verbose_$(V))

appsrc_verbose_0 = @echo " APP   " $(PROJECT).app.src;
appsrc_verbose = $(appsrc_verbose_$(V))

erlc_verbose_0 = @echo " ERLC  " $(filter-out $(patsubst %,%.erl,$(ERLC_EXCLUDE)),\
	$(filter %.erl %.core,$(?F)));
erlc_verbose = $(erlc_verbose_$(V))

xyrl_verbose_0 = @echo " XYRL  " $(filter %.xrl %.yrl,$(?F));
xyrl_verbose = $(xyrl_verbose_$(V))

asn1_verbose_0 = @echo " ASN1  " $(filter %.asn1,$(?F));
asn1_verbose = $(asn1_verbose_$(V))

mib_verbose_0 = @echo " MIB   " $(filter %.bin %.mib,$(?F));
mib_verbose = $(mib_verbose_$(V))

# Targets.

ifeq ($(wildcard ebin/test),)
app:: app-build
else
app:: clean app-build
endif

ifeq ($(wildcard src/$(PROJECT)_app.erl),)
define app_file
{application, $(PROJECT), [
	{description, "$(PROJECT_DESCRIPTION)"},
	{vsn, "$(PROJECT_VERSION)"},
	{id, "$(1)"},
	{modules, [$(call comma_list,$(2))]},
	{registered, []},
	{applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(DEPS))]}
]}.
endef
else
define app_file
{application, $(PROJECT), [
	{description, "$(PROJECT_DESCRIPTION)"},
	{vsn, "$(PROJECT_VERSION)"},
	{id, "$(1)"},
	{modules, [$(call comma_list,$(2))]},
	{registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
	{applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(DEPS))]},
	{mod, {$(PROJECT)_app, []}}
]}.
endef
endif

app-build: erlc-include ebin/$(PROJECT).app
	$(eval GITDESCRIBE := $(shell git describe --dirty --abbrev=7 --tags --always --first-parent 2>/dev/null || true))
	$(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename $(wildcard ebin/*.beam))))))
ifeq ($(wildcard src/$(PROJECT).app.src),)
	$(app_verbose) echo $(subst $(newline),,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES)))) \
		> ebin/$(PROJECT).app
else
	$(verbose) if [ -z "$$(grep -E '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
		echo "Empty modules entry not found in $(PROJECT).app.src. Please consult the erlang.mk README for instructions." >&2; \
		exit 1; \
	fi
	$(appsrc_verbose) cat src/$(PROJECT).app.src \
		| sed "s/{[[:space:]]*modules[[:space:]]*,[[:space:]]*\[\]}/{modules, \[$(call comma_list,$(MODULES))\]}/" \
		| sed "s/{id,[[:space:]]*\"git\"}/{id, \"$(GITDESCRIBE)\"}/" \
		> ebin/$(PROJECT).app
endif

erlc-include:
	- $(verbose) if [ -d ebin/ ]; then \
		find include/ src/ -type f -name \*.hrl -newer ebin -exec touch $(shell find src/ -type f -name "*.erl") \; 2>/dev/null || printf ''; \
	fi

define compile_erl
	$(erlc_verbose) erlc -v $(if $(IS_DEP),$(filter-out -Werror,$(ERLC_OPTS)),$(ERLC_OPTS)) -o ebin/ \
		-pa ebin/ -I include/ $(filter-out $(ERLC_EXCLUDE_PATHS),\
		$(COMPILE_FIRST_PATHS) $(1))
endef

define compile_xyrl
	$(xyrl_verbose) erlc -v -o ebin/ $(1)
	$(xyrl_verbose) erlc $(ERLC_OPTS) -o ebin/ ebin/*.erl
	$(verbose) rm ebin/*.erl
endef

define compile_asn1
	$(asn1_verbose) erlc -v -I include/ -o ebin/ $(1)
	$(verbose) mv ebin/*.hrl include/
	$(verbose) mv ebin/*.asn1db include/
	$(verbose) rm ebin/*.erl
endef

define compile_mib
	$(mib_verbose) erlc -v $(ERLC_MIB_OPTS) -o priv/mibs/ \
		-I priv/mibs/ $(COMPILE_MIB_FIRST_PATHS) $(1)
	$(mib_verbose) erlc -o include/ -- priv/mibs/*.bin
endef

ifneq ($(wildcard src/),)
ebin/$(PROJECT).app::
	$(verbose) mkdir -p ebin/

ifneq ($(wildcard asn1/),)
ebin/$(PROJECT).app:: $(sort $(call core_find,asn1/,*.asn1))
	$(verbose) mkdir -p include
	$(if $(strip $?),$(call compile_asn1,$?))
endif

ifneq ($(wildcard mibs/),)
ebin/$(PROJECT).app:: $(sort $(call core_find,mibs/,*.mib))
	$(verbose) mkdir -p priv/mibs/ include
	$(if $(strip $?),$(call compile_mib,$?))
endif

ebin/$(PROJECT).app:: $(sort $(call core_find,src/,*.erl *.core))
	$(if $(strip $?),$(call compile_erl,$?))

ebin/$(PROJECT).app:: $(sort $(call core_find,src/,*.xrl *.yrl))
	$(if $(strip $?),$(call compile_xyrl,$?))
endif

clean:: clean-app

clean-app:
	$(gen_verbose) rm -rf ebin/ priv/mibs/ \
		$(addprefix include/,$(addsuffix .hrl,$(notdir $(basename $(call core_find,mibs/,*.mib)))))

# Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: docs-deps

# Configuration.

ALL_DOC_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(DOC_DEPS))

# Targets.

$(foreach dep,$(DOC_DEPS),$(eval $(call dep_target,$(dep))))

ifneq ($(SKIP_DEPS),)
doc-deps:
else
doc-deps: $(ALL_DOC_DEPS_DIRS)
	$(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
endif

# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: test-deps test-dir test-build clean-test-dir

# Configuration.

TEST_DIR ?= $(CURDIR)/test

ALL_TEST_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(TEST_DEPS))

TEST_ERLC_OPTS ?= +debug_info +warn_export_vars +warn_shadow_vars +warn_obsolete_guard
TEST_ERLC_OPTS += -DTEST=1

# Targets.

$(foreach dep,$(TEST_DEPS),$(eval $(call dep_target,$(dep))))

ifneq ($(SKIP_DEPS),)
test-deps:
else
test-deps: $(ALL_TEST_DEPS_DIRS)
	$(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
endif

ifneq ($(wildcard $(TEST_DIR)),)
test-dir:
	$(gen_verbose) erlc -v $(TEST_ERLC_OPTS) -I include/ -o $(TEST_DIR) \
		$(call core_find,$(TEST_DIR)/,*.erl) -pa ebin/
endif

ifeq ($(wildcard ebin/test),)
test-build:: ERLC_OPTS=$(TEST_ERLC_OPTS)
test-build:: clean deps test-deps
	$(verbose) $(MAKE) --no-print-directory app-build test-dir ERLC_OPTS="$(TEST_ERLC_OPTS)"
	$(gen_verbose) touch ebin/test
else
test-build:: ERLC_OPTS=$(TEST_ERLC_OPTS)
test-build:: deps test-deps
	$(verbose) $(MAKE) --no-print-directory app-build test-dir ERLC_OPTS="$(TEST_ERLC_OPTS)"
endif

clean:: clean-test-dir

clean-test-dir:
ifneq ($(wildcard $(TEST_DIR)/*.beam),)
	$(gen_verbose) rm -f $(TEST_DIR)/*.beam
endif

# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc

MAN_INSTALL_PATH ?= /usr/local/share/man
MAN_SECTIONS ?= 3 7

docs:: asciidoc

asciidoc: distclean-asciidoc doc-deps asciidoc-guide asciidoc-manual

ifeq ($(wildcard doc/src/guide/book.asciidoc),)
asciidoc-guide:
else
asciidoc-guide:
	a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
	a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
endif

ifeq ($(wildcard doc/src/manual/*.asciidoc),)
asciidoc-manual:
else
asciidoc-manual:
	for f in doc/src/manual/*.asciidoc ; do \
		a2x -v -f manpage $$f ; \
	done
	for s in $(MAN_SECTIONS); do \
		mkdir -p doc/man$$s/ ; \
		mv doc/src/manual/*.$$s doc/man$$s/ ; \
		gzip doc/man$$s/*.$$s ; \
	done

install-docs:: install-asciidoc

install-asciidoc: asciidoc-manual
	for s in $(MAN_SECTIONS); do \
		mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
		install -g 0 -o 0 -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
	done
endif

distclean:: distclean-asciidoc

distclean-asciidoc:
	$(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/

# Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates

# Core targets.

help::
	$(verbose) printf "%s\n" "" \
		"Bootstrap targets:" \
		"  bootstrap          Generate a skeleton of an OTP application" \
		"  bootstrap-lib      Generate a skeleton of an OTP library" \
		"  bootstrap-rel      Generate the files needed to build a release" \
		"  new t=TPL n=NAME   Generate a module NAME based on the template TPL" \
		"  list-templates     List available templates"

# Bootstrap templates.

define bs_appsrc
{application, $(PROJECT), [
	{description, ""},
	{vsn, "0.1.0"},
	{id, "git"},
	{modules, []},
	{registered, []},
	{applications, [
		kernel,
		stdlib
	]},
	{mod, {$(PROJECT)_app, []}},
	{env, []}
]}.
endef

define bs_appsrc_lib
{application, $(PROJECT), [
	{description, ""},
	{vsn, "0.1.0"},
	{id, "git"},
	{modules, []},
	{registered, []},
	{applications, [
		kernel,
		stdlib
	]}
]}.
endef

ifdef SP
define bs_Makefile
PROJECT = $(PROJECT)

# Whitespace to be used when creating files from templates.
SP = $(SP)

include erlang.mk
endef
else
define bs_Makefile
PROJECT = $(PROJECT)
include erlang.mk
endef
endif

define bs_app
-module($(PROJECT)_app).
-behaviour(application).

-export([start/2]).
-export([stop/1]).

start(_Type, _Args) ->
	$(PROJECT)_sup:start_link().

stop(_State) ->
	ok.
endef

define bs_relx_config
{release, {$(PROJECT)_release, "1"}, [$(PROJECT)]}.
{extended_start_script, true}.
{sys_config, "rel/sys.config"}.
{vm_args, "rel/vm.args"}.
endef

define bs_sys_config
[
].
endef

define bs_vm_args
-name $(PROJECT)@127.0.0.1
-setcookie $(PROJECT)
-heart
endef

# Normal templates.

define tpl_supervisor
-module($(n)).
-behaviour(supervisor).

-export([start_link/0]).
-export([init/1]).

start_link() ->
	supervisor:start_link({local, ?MODULE}, ?MODULE, []).

init([]) ->
	Procs = [],
	{ok, {{one_for_one, 1, 5}, Procs}}.
endef

define tpl_gen_server
-module($(n)).
-behaviour(gen_server).

%% API.
-export([start_link/0]).

%% gen_server.
-export([init/1]).
-export([handle_call/3]).
-export([handle_cast/2]).
-export([handle_info/2]).
-export([terminate/2]).
-export([code_change/3]).

-record(state, {
}).

%% API.

-spec start_link() -> {ok, pid()}.
start_link() ->
	gen_server:start_link(?MODULE, [], []).

%% gen_server.

init([]) ->
	{ok, #state{}}.

handle_call(_Request, _From, State) ->
	{reply, ignored, State}.

handle_cast(_Msg, State) ->
	{noreply, State}.

handle_info(_Info, State) ->
	{noreply, State}.

terminate(_Reason, _State) ->
	ok.

code_change(_OldVsn, State, _Extra) ->
	{ok, State}.
endef

define tpl_cowboy_http
-module($(n)).
-behaviour(cowboy_http_handler).

-export([init/3]).
-export([handle/2]).
-export([terminate/3]).

-record(state, {
}).

init(_, Req, _Opts) ->
	{ok, Req, #state{}}.

handle(Req, State=#state{}) ->
	{ok, Req2} = cowboy_req:reply(200, Req),
	{ok, Req2, State}.

terminate(_Reason, _Req, _State) ->
	ok.
endef

define tpl_gen_fsm
-module($(n)).
-behaviour(gen_fsm).

%% API.
-export([start_link/0]).

%% gen_fsm.
-export([init/1]).
-export([state_name/2]).
-export([handle_event/3]).
-export([state_name/3]).
-export([handle_sync_event/4]).
-export([handle_info/3]).
-export([terminate/3]).
-export([code_change/4]).

-record(state, {
}).

%% API.

-spec start_link() -> {ok, pid()}.
start_link() ->
	gen_fsm:start_link(?MODULE, [], []).

%% gen_fsm.

init([]) ->
	{ok, state_name, #state{}}.

state_name(_Event, StateData) ->
	{next_state, state_name, StateData}.

handle_event(_Event, StateName, StateData) ->
	{next_state, StateName, StateData}.

state_name(_Event, _From, StateData) ->
	{reply, ignored, state_name, StateData}.

handle_sync_event(_Event, _From, StateName, StateData) ->
	{reply, ignored, StateName, StateData}.

handle_info(_Info, StateName, StateData) ->
	{next_state, StateName, StateData}.

terminate(_Reason, _StateName, _StateData) ->
	ok.

code_change(_OldVsn, StateName, StateData, _Extra) ->
	{ok, StateName, StateData}.
endef

define tpl_cowboy_loop
-module($(n)).
-behaviour(cowboy_loop_handler).

-export([init/3]).
-export([info/3]).
-export([terminate/3]).

-record(state, {
}).

init(_, Req, _Opts) ->
	{loop, Req, #state{}, 5000, hibernate}.

info(_Info, Req, State) ->
	{loop, Req, State, hibernate}.

terminate(_Reason, _Req, _State) ->
	ok.
endef

define tpl_cowboy_rest
-module($(n)).

-export([init/3]).
-export([content_types_provided/2]).
-export([get_html/2]).

init(_, _Req, _Opts) ->
	{upgrade, protocol, cowboy_rest}.

content_types_provided(Req, State) ->
	{[{{<<"text">>, <<"html">>, '*'}, get_html}], Req, State}.

get_html(Req, State) ->
	{<<"<html><body>This is REST!</body></html>">>, Req, State}.
endef

define tpl_cowboy_ws
-module($(n)).
-behaviour(cowboy_websocket_handler).

-export([init/3]).
-export([websocket_init/3]).
-export([websocket_handle/3]).
-export([websocket_info/3]).
-export([websocket_terminate/3]).

-record(state, {
}).

init(_, _, _) ->
	{upgrade, protocol, cowboy_websocket}.

websocket_init(_, Req, _Opts) ->
	Req2 = cowboy_req:compact(Req),
	{ok, Req2, #state{}}.

websocket_handle({text, Data}, Req, State) ->
	{reply, {text, Data}, Req, State};
websocket_handle({binary, Data}, Req, State) ->
	{reply, {binary, Data}, Req, State};
websocket_handle(_Frame, Req, State) ->
	{ok, Req, State}.

websocket_info(_Info, Req, State) ->
	{ok, Req, State}.

websocket_terminate(_Reason, _Req, _State) ->
	ok.
endef

define tpl_ranch_protocol
-module($(n)).
-behaviour(ranch_protocol).

-export([start_link/4]).
-export([init/4]).

-type opts() :: [].
-export_type([opts/0]).

-record(state, {
	socket :: inet:socket(),
	transport :: module()
}).

start_link(Ref, Socket, Transport, Opts) ->
	Pid = spawn_link(?MODULE, init, [Ref, Socket, Transport, Opts]),
	{ok, Pid}.

-spec init(ranch:ref(), inet:socket(), module(), opts()) -> ok.
init(Ref, Socket, Transport, _Opts) ->
	ok = ranch:accept_ack(Ref),
	loop(#state{socket=Socket, transport=Transport}).

loop(State) ->
	loop(State).
endef

# Plugin-specific targets.

define render_template
	$(verbose) echo "$${_$(1)}" > $(2)
endef

ifndef WS
ifdef SP
WS = $(subst a,,a $(wordlist 1,$(SP),a a a a a a a a a a a a a a a a a a a a))
else
WS = $(tab)
endif
endif

$(foreach template,$(filter bs_% tpl_%,$(.VARIABLES)), \
	$(eval _$(template) = $$(subst $$(tab),$$(WS),$$($(template)))) \
	$(eval export _$(template)))

bootstrap:
ifneq ($(wildcard src/),)
	$(error Error: src/ directory already exists)
endif
	$(call render_template,bs_Makefile,Makefile)
	$(verbose) mkdir src/
	$(call render_template,bs_appsrc,src/$(PROJECT).app.src)
	$(call render_template,bs_app,src/$(PROJECT)_app.erl)
	$(eval n := $(PROJECT)_sup)
	$(call render_template,tpl_supervisor,src/$(PROJECT)_sup.erl)

bootstrap-lib:
ifneq ($(wildcard src/),)
	$(error Error: src/ directory already exists)
endif
	$(call render_template,bs_Makefile,Makefile)
	$(verbose) mkdir src/
	$(call render_template,bs_appsrc_lib,src/$(PROJECT).app.src)

bootstrap-rel:
ifneq ($(wildcard relx.config),)
	$(error Error: relx.config already exists)
endif
ifneq ($(wildcard rel/),)
	$(error Error: rel/ directory already exists)
endif
	$(call render_template,bs_relx_config,relx.config)
	$(verbose) mkdir rel/
	$(call render_template,bs_sys_config,rel/sys.config)
	$(call render_template,bs_vm_args,rel/vm.args)

new:
ifeq ($(wildcard src/),)
	$(error Error: src/ directory does not exist)
endif
ifndef t
	$(error Usage: $(MAKE) new t=TEMPLATE n=NAME)
endif
ifndef tpl_$(t)
	$(error Unknown template)
endif
ifndef n
	$(error Usage: $(MAKE) new t=TEMPLATE n=NAME)
endif
	$(call render_template,tpl_$(t),src/$(n).erl)

list-templates:
	$(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))

# Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: clean-c_src distclean-c_src-env

# Configuration.

C_SRC_DIR ?= $(CURDIR)/c_src
C_SRC_ENV ?= $(C_SRC_DIR)/env.mk
C_SRC_OUTPUT ?= $(CURDIR)/priv/$(PROJECT).so
C_SRC_TYPE ?= shared

# System type and C compiler/flags.

ifeq ($(PLATFORM),darwin)
	CC ?= cc
	CFLAGS ?= -O3 -std=c99 -arch x86_64 -finline-functions -Wall -Wmissing-prototypes
	CXXFLAGS ?= -O3 -arch x86_64 -finline-functions -Wall
	LDFLAGS ?= -arch x86_64 -flat_namespace -undefined suppress
else ifeq ($(PLATFORM),freebsd)
	CC ?= cc
	CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes
	CXXFLAGS ?= -O3 -finline-functions -Wall
else ifeq ($(PLATFORM),linux)
	CC ?= gcc
	CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes
	CXXFLAGS ?= -O3 -finline-functions -Wall
endif

CFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR)
CXXFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR)

LDLIBS += -L $(ERL_INTERFACE_LIB_DIR) -lerl_interface -lei

ifeq ($(C_SRC_TYPE),shared)
LDFLAGS += -shared
endif

# Verbosity.

c_verbose_0 = @echo " C     " $(?F);
c_verbose = $(c_verbose_$(V))

cpp_verbose_0 = @echo " CPP   " $(?F);
cpp_verbose = $(cpp_verbose_$(V))

link_verbose_0 = @echo " LD    " $(@F);
link_verbose = $(link_verbose_$(V))

# Targets.

ifeq ($(wildcard $(C_SRC_DIR)),)
else ifneq ($(wildcard $(C_SRC_DIR)/Makefile),)
app:: app-c_src

test-build:: app-c_src

app-c_src:
	$(MAKE) -C $(C_SRC_DIR)

clean::
	$(MAKE) -C $(C_SRC_DIR) clean

else

ifeq ($(SOURCES),)
SOURCES := $(sort $(call core_find,$(C_SRC_DIR)/,*.c *.C *.cc *.cpp))
endif
OBJECTS = $(addsuffix .o, $(basename $(SOURCES)))

COMPILE_C = $(c_verbose) $(CC) $(CFLAGS) $(CPPFLAGS) -c
COMPILE_CPP = $(cpp_verbose) $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c

app:: $(C_SRC_ENV) $(C_SRC_OUTPUT)

test-build:: $(C_SRC_ENV) $(C_SRC_OUTPUT)

$(C_SRC_OUTPUT): $(OBJECTS)
	$(verbose) mkdir -p priv/
	$(link_verbose) $(CC) $(OBJECTS) $(LDFLAGS) $(LDLIBS) -o $(C_SRC_OUTPUT)

%.o: %.c
	$(COMPILE_C) $(OUTPUT_OPTION) $<

%.o: %.cc
	$(COMPILE_CPP) $(OUTPUT_OPTION) $<

%.o: %.C
	$(COMPILE_CPP) $(OUTPUT_OPTION) $<

%.o: %.cpp
	$(COMPILE_CPP) $(OUTPUT_OPTION) $<

clean:: clean-c_src

clean-c_src:
	$(gen_verbose) rm -f $(C_SRC_OUTPUT) $(OBJECTS)

endif

ifneq ($(wildcard $(C_SRC_DIR)),)
$(C_SRC_ENV):
	$(verbose) $(ERL) -eval "file:write_file(\"$(C_SRC_ENV)\", \
		io_lib:format( \
			\"ERTS_INCLUDE_DIR ?= ~s/erts-~s/include/~n\" \
			\"ERL_INTERFACE_INCLUDE_DIR ?= ~s~n\" \
			\"ERL_INTERFACE_LIB_DIR ?= ~s~n\", \
			[code:root_dir(), erlang:system_info(version), \
			code:lib_dir(erl_interface, include), \
			code:lib_dir(erl_interface, lib)])), \
		halt()."

distclean:: distclean-c_src-env

distclean-c_src-env:
	$(gen_verbose) rm -f $(C_SRC_ENV)

-include $(C_SRC_ENV)
endif

# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: ci ci-setup distclean-kerl

KERL ?= $(CURDIR)/kerl
export KERL

KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl

OTP_GIT ?= https://github.com/erlang/otp

CI_INSTALL_DIR ?= $(HOME)/erlang
CI_OTP ?=

ifeq ($(strip $(CI_OTP)),)
ci::
else
ci:: $(addprefix ci-,$(CI_OTP))

ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))

ci-setup::

ci_verbose_0 = @echo " CI    " $(1);
ci_verbose = $(ci_verbose_$(V))

define ci_target
ci-$(1): $(CI_INSTALL_DIR)/$(1)
	$(ci_verbose) \
		PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
		CI_OTP_RELEASE="$(1)" \
		CT_OPTS="-label $(1)" \
		$(MAKE) clean ci-setup tests
endef

$(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))

define ci_otp_target
ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
$(CI_INSTALL_DIR)/$(1): $(KERL)
	$(KERL) build git $(OTP_GIT) $(1) $(1)
	$(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
endif
endef

$(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))

$(KERL):
	$(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL))
	$(verbose) chmod +x $(KERL)

help::
	$(verbose) printf "%s\n" "" \
		"Continuous Integration targets:" \
		"  ci          Run '$(MAKE) tests' on all configured Erlang versions." \
		"" \
		"The CI_OTP variable must be defined with the Erlang versions" \
		"that must be tested. For example: CI_OTP = OTP-17.3.4 OTP-17.5.3"

distclean:: distclean-kerl

distclean-kerl:
	$(gen_verbose) rm -rf $(KERL)
endif

# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: ct distclean-ct

# Configuration.

CT_OPTS ?=
ifneq ($(wildcard $(TEST_DIR)),)
	CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
else
	CT_SUITES ?=
endif

# Core targets.

tests:: ct

distclean:: distclean-ct

help::
	$(verbose) printf "%s\n" "" \
		"Common_test targets:" \
		"  ct          Run all the common_test suites for this project" \
		"" \
		"All your common_test suites have their associated targets." \
		"A suite named http_SUITE can be ran using the ct-http target."

# Plugin-specific targets.

CT_RUN = ct_run \
	-no_auto_compile \
	-noinput \
	-pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(TEST_DIR) \
	-dir $(TEST_DIR) \
	-logdir $(CURDIR)/logs

ifeq ($(CT_SUITES),)
ct:
else
ct: test-build
	$(verbose) mkdir -p $(CURDIR)/logs/
	$(gen_verbose) $(CT_RUN) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
endif

define ct_suite_target
ct-$(1): test-build
	$(verbose) mkdir -p $(CURDIR)/logs/
	$(gen_verbose) $(CT_RUN) -suite $(addsuffix _SUITE,$(1)) $(CT_OPTS)
endef

$(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))

distclean-ct:
	$(gen_verbose) rm -rf $(CURDIR)/logs/

# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: plt distclean-plt dialyze

# Configuration.

DIALYZER_PLT ?= $(CURDIR)/.$(PROJECT).plt
export DIALYZER_PLT

PLT_APPS ?=
DIALYZER_DIRS ?= --src -r src
DIALYZER_OPTS ?= -Werror_handling -Wrace_conditions \
	-Wunmatched_returns # -Wunderspecs

# Core targets.

check:: dialyze

distclean:: distclean-plt

help::
	$(verbose) printf "%s\n" "" \
		"Dialyzer targets:" \
		"  plt         Build a PLT file for this project" \
		"  dialyze     Analyze the project using Dialyzer"

# Plugin-specific targets.

$(DIALYZER_PLT): deps app
	$(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(ALL_DEPS_DIRS)

plt: $(DIALYZER_PLT)

distclean-plt:
	$(gen_verbose) rm -f $(DIALYZER_PLT)

ifneq ($(wildcard $(DIALYZER_PLT)),)
dialyze:
else
dialyze: $(DIALYZER_PLT)
endif
	$(verbose) dialyzer --no_native $(DIALYZER_DIRS) $(DIALYZER_OPTS)

# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: distclean-edoc edoc

# Configuration.

EDOC_OPTS ?=

# Core targets.

docs:: distclean-edoc edoc

distclean:: distclean-edoc

# Plugin-specific targets.

edoc: doc-deps
	$(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'

distclean-edoc:
	$(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info

# Copyright (c) 2015, Erlang Solutions Ltd.
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: elvis distclean-elvis

# Configuration.

ELVIS_CONFIG ?= $(CURDIR)/elvis.config

ELVIS ?= $(CURDIR)/elvis
export ELVIS

ELVIS_URL ?= https://github.com/inaka/elvis/releases/download/0.2.5-beta2/elvis
ELVIS_CONFIG_URL ?= https://github.com/inaka/elvis/releases/download/0.2.5-beta2/elvis.config
ELVIS_OPTS ?=

# Core targets.

help::
	$(verbose) printf "%s\n" "" \
		"Elvis targets:" \
		"  elvis       Run Elvis using the local elvis.config or download the default otherwise"

distclean:: distclean-elvis

# Plugin-specific targets.

$(ELVIS):
	$(gen_verbose) $(call core_http_get,$(ELVIS),$(ELVIS_URL))
	$(verbose) chmod +x $(ELVIS)

$(ELVIS_CONFIG):
	$(verbose) $(call core_http_get,$(ELVIS_CONFIG),$(ELVIS_CONFIG_URL))

elvis: $(ELVIS) $(ELVIS_CONFIG)
	$(verbose) $(ELVIS) rock -c $(ELVIS_CONFIG) $(ELVIS_OPTS)

distclean-elvis:
	$(gen_verbose) rm -rf $(ELVIS)

# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

# Configuration.

DTL_FULL_PATH ?= 0

# Verbosity.

dtl_verbose_0 = @echo " DTL   " $(filter %.dtl,$(?F));
dtl_verbose = $(dtl_verbose_$(V))

# Core targets.

define compile_erlydtl
	$(dtl_verbose) $(ERL) -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/ -eval ' \
		Compile = fun(F) -> \
			S = fun (1) -> re:replace(filename:rootname(string:sub_string(F, 11), ".dtl"), "/",  "_",  [{return, list}, global]); \
				(0) -> filename:basename(F, ".dtl") \
			end, \
			Module = list_to_atom(string:to_lower(S($(DTL_FULL_PATH))) ++ "_dtl"), \
			{ok, _} = erlydtl:compile(F, Module, [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) \
		end, \
		_ = [Compile(F) || F <- string:tokens("$(1)", " ")], \
		halt().'
endef

ifneq ($(wildcard src/),)
ebin/$(PROJECT).app:: $(sort $(call core_find,templates/,*.dtl))
	$(if $(strip $?),$(call compile_erlydtl,$?))
endif

# Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: distclean-escript escript

# Configuration.

ESCRIPT_NAME ?= $(PROJECT)
ESCRIPT_COMMENT ?= This is an -*- erlang -*- file

ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
ESCRIPT_EMU_ARGS ?= -pa . \
	-sasl errlog_type error \
	-escript main $(ESCRIPT_NAME)
ESCRIPT_SHEBANG ?= /usr/bin/env escript
ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"

# Core targets.

distclean:: distclean-escript

help::
	$(verbose) printf "%s\n" "" \
		"Escript targets:" \
		"  escript     Build an executable escript archive" \

# Plugin-specific targets.

# Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
# Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
# Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
# Software may only be used for the great good and the true happiness of all
# sentient beings.

define ESCRIPT_RAW
'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
'  [F || F <- A, not filelib:is_dir(F) ] end,'\
'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
'Ez = fun(Escript) ->'\
'  Static = Files([$(ESCRIPT_STATIC)]),'\
'  Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
'  Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
'  escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
'    {archive, Archive, [memory]},'\
'    {shebang, "$(ESCRIPT_SHEBANG)"},'\
'    {comment, "$(ESCRIPT_COMMENT)"},'\
'    {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
'  ]),'\
'  file:change_mode(Escript, 8#755)'\
'end,'\
'Ez("$(ESCRIPT_NAME)"),'\
'halt().'
endef

ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))

escript:: distclean-escript deps app
	$(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)

distclean-escript:
	$(gen_verbose) rm -f $(ESCRIPT_NAME)

# Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
# This file is contributed to erlang.mk and subject to the terms of the ISC License.

.PHONY: eunit

# Configuration

EUNIT_OPTS ?=

# Core targets.

tests:: eunit

help::
	$(verbose) printf "%s\n" "" \
		"EUnit targets:" \
		"  eunit       Run all the EUnit tests for this project"

# Plugin-specific targets.

define eunit.erl
	case "$(COVER)" of
		"" -> ok;
		_ ->
			case cover:compile_beam_directory("ebin") of
				{error, _} -> halt(1);
				_ -> ok
			end
	end,
	case eunit:test([$(call comma_list,$(1))], [$(EUNIT_OPTS)]) of
		ok -> ok;
		error -> halt(2)
	end,
	case "$(COVER)" of
		"" -> ok;
		_ ->
			cover:export("eunit.coverdata")
	end,
	halt()
endef

EUNIT_EBIN_MODS = $(notdir $(basename $(call core_find,ebin/,*.beam)))
EUNIT_TEST_MODS = $(notdir $(basename $(call core_find,$(TEST_DIR)/,*.beam)))
EUNIT_MODS = $(foreach mod,$(EUNIT_EBIN_MODS) $(filter-out \
	$(patsubst %,%_tests,$(EUNIT_EBIN_MODS)),$(EUNIT_TEST_MODS)),{module,'$(mod)'})

eunit: test-build
	$(gen_verbose) $(ERL) -pa $(TEST_DIR) $(DEPS_DIR)/*/ebin ebin \
		-eval "$(subst $(newline),,$(subst ",\",$(call eunit.erl,$(EUNIT_MODS))))"

# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: relx-rel distclean-relx-rel distclean-relx run

# Configuration.

RELX_CONFIG ?= $(CURDIR)/relx.config

RELX ?= $(CURDIR)/relx
export RELX

RELX_URL ?= https://github.com/erlware/relx/releases/download/v2.0.0/relx
RELX_OPTS ?=
RELX_OUTPUT_DIR ?= _rel

ifeq ($(firstword $(RELX_OPTS)),-o)
	RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
else
	RELX_OPTS += -o $(RELX_OUTPUT_DIR)
endif

# Core targets.

ifeq ($(IS_DEP),)
ifneq ($(wildcard $(RELX_CONFIG)),)
rel:: distclean-relx-rel relx-rel
endif
endif

distclean:: distclean-relx-rel distclean-relx

# Plugin-specific targets.

$(RELX):
	$(gen_verbose) $(call core_http_get,$(RELX),$(RELX_URL))
	$(verbose) chmod +x $(RELX)

relx-rel: $(RELX)
	$(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)

distclean-relx-rel:
	$(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)

distclean-relx:
	$(gen_verbose) rm -rf $(RELX)

# Run target.

ifeq ($(wildcard $(RELX_CONFIG)),)
run:
else

define get_relx_release.erl
	{ok, Config} = file:consult("$(RELX_CONFIG)"),
	{release, {Name, _}, _} = lists:keyfind(release, 1, Config),
	io:format("~s", [Name]),
	halt(0).
endef

RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`

run: all
	$(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console

help::
	$(verbose) printf "%s\n" "" \
		"Relx targets:" \
		"  run         Compile the project, build the release and run it"

endif

# Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
# This file is contributed to erlang.mk and subject to the terms of the ISC License.

.PHONY: shell

# Configuration.

SHELL_PATH ?= -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin
SHELL_OPTS ?=

ALL_SHELL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(SHELL_DEPS))

# Core targets

help::
	$(verbose) printf "%s\n" "" \
		"Shell targets:" \
		"  shell       Run an erlang shell with SHELL_OPTS or reasonable default"

# Plugin-specific targets.

$(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))

build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
	$(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done

shell: build-shell-deps
	$(gen_verbose) erl $(SHELL_PATH) $(SHELL_OPTS)

# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

ifneq ($(wildcard $(DEPS_DIR)/triq),)
.PHONY: triq

# Targets.

tests:: triq

define triq_check.erl
	code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
	try
		case $(1) of
			all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
			module -> triq:check($(2));
			function -> triq:check($(2))
		end
	of
		true -> halt(0);
		_ -> halt(1)
	catch error:undef ->
		io:format("Undefined property or module~n"),
		halt(0)
	end.
endef

ifdef t
ifeq (,$(findstring :,$(t)))
triq: test-build
	$(verbose) $(call erlang,$(call triq_check.erl,module,$(t)))
else
triq: test-build
	$(verbose) echo Testing $(t)/0
	$(verbose) $(call erlang,$(call triq_check.erl,function,$(t)()))
endif
else
triq: test-build
	$(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename $(wildcard ebin/*.beam))))))
	$(gen_verbose) $(call erlang,$(call triq_check.erl,all,undefined,$(MODULES)))
endif
endif

# Copyright (c) 2015, Erlang Solutions Ltd.
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: xref distclean-xref

# Configuration.

ifeq ($(XREF_CONFIG),)
	XREF_ARGS :=
else
	XREF_ARGS := -c $(XREF_CONFIG)
endif

XREFR ?= $(CURDIR)/xrefr
export XREFR

XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr

# Core targets.

help::
	$(verbose) printf "%s\n" "" \
		"Xref targets:" \
		"  xref        Run Xrefr using $XREF_CONFIG as config file if defined"

distclean:: distclean-xref

# Plugin-specific targets.

$(XREFR):
	$(gen_verbose) $(call core_http_get,$(XREFR),$(XREFR_URL))
	$(verbose) chmod +x $(XREFR)

xref: deps app $(XREFR)
	$(gen_verbose) $(XREFR) $(XREFR_ARGS)

distclean-xref:
	$(gen_verbose) rm -rf $(XREFR)

# Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
# This file is part of erlang.mk and subject to the terms of the ISC License.

COVER_REPORT_DIR = cover

# Hook in coverage to ct

ifdef COVER
ifdef CT_RUN
# All modules in 'ebin'
COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))

test-build:: $(TEST_DIR)/ct.cover.spec

$(TEST_DIR)/ct.cover.spec:
	$(verbose) echo Cover mods: $(COVER_MODS)
	$(gen_verbose) printf "%s\n" \
		'{incl_mods,[$(subst $(space),$(comma),$(COVER_MODS))]}.' \
		'{export,"$(CURDIR)/ct.coverdata"}.' > $@

CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
endif
endif

# Core targets

ifdef COVER
ifneq ($(COVER_REPORT_DIR),)
tests::
	$(verbose) $(MAKE) --no-print-directory cover-report
endif
endif

clean:: coverdata-clean

ifneq ($(COVER_REPORT_DIR),)
distclean:: cover-report-clean
endif

help::
	$(verbose) printf "%s\n" "" \
		"Cover targets:" \
		"  cover-report  Generate a HTML coverage report from previously collected" \
		"                cover data." \
		"  all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
		"" \
		"If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
		"target tests additionally generates a HTML coverage report from the combined" \
		"coverdata files from each of these testing tools. HTML reports can be disabled" \
		"by setting COVER_REPORT_DIR to empty."

# Plugin specific targets

COVERDATA = $(filter-out all.coverdata,$(wildcard *.coverdata))

.PHONY: coverdata-clean
coverdata-clean:
	$(gen_verbose) rm -f *.coverdata ct.cover.spec

# Merge all coverdata files into one.
all.coverdata: $(COVERDATA)
	$(gen_verbose) $(ERL) -eval ' \
		$(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
		cover:export("$@"), halt(0).'

# These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
# empty if you want the coverdata files but not the HTML report.
ifneq ($(COVER_REPORT_DIR),)

.PHONY: cover-report-clean cover-report

cover-report-clean:
	$(gen_verbose) rm -rf $(COVER_REPORT_DIR)

ifeq ($(COVERDATA),)
cover-report:
else

# Modules which include eunit.hrl always contain one line without coverage
# because eunit defines test/0 which is never called. We compensate for this.
EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
	grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
	| sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))

define cover_report.erl
	$(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
	Ms = cover:imported_modules(),
	[cover:analyse_to_file(M, "$(COVER_REPORT_DIR)/" ++ atom_to_list(M)
		++ ".COVER.html", [html])  || M <- Ms],
	Report = [begin {ok, R} = cover:analyse(M, module), R end || M <- Ms],
	EunitHrlMods = [$(EUNIT_HRL_MODS)],
	Report1 = [{M, {Y, case lists:member(M, EunitHrlMods) of
		true -> N - 1; false -> N end}} || {M, {Y, N}} <- Report],
	TotalY = lists:sum([Y || {_, {Y, _}} <- Report1]),
	TotalN = lists:sum([N || {_, {_, N}} <- Report1]),
	TotalPerc = round(100 * TotalY / (TotalY + TotalN)),
	{ok, F} = file:open("$(COVER_REPORT_DIR)/index.html", [write]),
	io:format(F, "<!DOCTYPE html><html>~n"
		"<head><meta charset=\"UTF-8\">~n"
		"<title>Coverage report</title></head>~n"
		"<body>~n", []),
	io:format(F, "<h1>Coverage</h1>~n<p>Total: ~p%</p>~n", [TotalPerc]),
	io:format(F, "<table><tr><th>Module</th><th>Coverage</th></tr>~n", []),
	[io:format(F, "<tr><td><a href=\"~p.COVER.html\">~p</a></td>"
		"<td>~p%</td></tr>~n",
		[M, M, round(100 * Y / (Y + N))]) || {M, {Y, N}} <- Report1],
	How = "$(subst $(space),$(comma)$(space),$(basename $(COVERDATA)))",
	Date = "$(shell date -u "+%Y-%m-%dT%H:%M:%SZ")",
	io:format(F, "</table>~n"
		"<p>Generated using ~s and erlang.mk on ~s.</p>~n"
		"</body></html>", [How, Date]),
	halt().
endef

cover-report:
	$(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
	$(gen_verbose) $(call erlang,$(cover_report.erl))

endif
endif # ifneq ($(COVER_REPORT_DIR),)
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted deps/cowboy/rebar.config.

1
2
3
4
{deps, [
	{cowlib, ".*", {git, "https://github.com/ninenines/cowlib.git", "1.0.0"}},
	{ranch, ".*", {git, "https://github.com/ninenines/ranch.git", "1.0.0"}}
]}.
<
<
<
<








Deleted deps/cowboy/src/cowboy.app.src.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
%% Copyright (c) 2011-2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

{application, cowboy, [
	{description, "Small, fast, modular HTTP server."},
	{vsn, "1.0.4"},
	{id, "git"},
	{modules, []},
	{registered, [cowboy_clock, cowboy_sup]},
	{applications, [
		kernel,
		stdlib,
		ranch,
		cowlib,
		crypto
	]},
	{mod, {cowboy_app, []}},
	{env, []}
]}.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




























































Deleted deps/cowboy/src/cowboy.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
%% Copyright (c) 2011-2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(cowboy).

-export([start_http/4]).
-export([start_https/4]).
-export([start_spdy/4]).
-export([stop_listener/1]).
-export([set_env/3]).

-type http_headers() :: [{binary(), iodata()}].
-export_type([http_headers/0]).

-type http_status() :: non_neg_integer() | binary().
-export_type([http_status/0]).

-type http_version() :: 'HTTP/1.1' | 'HTTP/1.0'.
-export_type([http_version/0]).

-type onrequest_fun() :: fun((Req) -> Req).
-export_type([onrequest_fun/0]).

-type onresponse_fun() ::
	fun((http_status(), http_headers(), iodata(), Req) -> Req).
-export_type([onresponse_fun/0]).

-spec start_http(ranch:ref(), non_neg_integer(), ranch_tcp:opts(),
	cowboy_protocol:opts()) -> {ok, pid()} | {error, any()}.
start_http(Ref, NbAcceptors, TransOpts, ProtoOpts)
		when is_integer(NbAcceptors), NbAcceptors > 0 ->
	ranch:start_listener(Ref, NbAcceptors,
		ranch_tcp, TransOpts, cowboy_protocol, ProtoOpts).

-spec start_https(ranch:ref(), non_neg_integer(), ranch_ssl:opts(),
	cowboy_protocol:opts()) -> {ok, pid()} | {error, any()}.
start_https(Ref, NbAcceptors, TransOpts, ProtoOpts)
		when is_integer(NbAcceptors), NbAcceptors > 0 ->
	ranch:start_listener(Ref, NbAcceptors,
		ranch_ssl, TransOpts, cowboy_protocol, ProtoOpts).

-spec start_spdy(ranch:ref(), non_neg_integer(), ranch_ssl:opts(),
	cowboy_spdy:opts()) -> {ok, pid()} | {error, any()}.
start_spdy(Ref, NbAcceptors, TransOpts, ProtoOpts)
		when is_integer(NbAcceptors), NbAcceptors > 0 ->
	TransOpts2 = [
		{connection_type, supervisor},
		{next_protocols_advertised,
			[<<"spdy/3">>, <<"http/1.1">>, <<"http/1.0">>]}
	|TransOpts],
	ranch:start_listener(Ref, NbAcceptors,
		ranch_ssl, TransOpts2, cowboy_spdy, ProtoOpts).

-spec stop_listener(ranch:ref()) -> ok | {error, not_found}.
stop_listener(Ref) ->
	ranch:stop_listener(Ref).

-spec set_env(ranch:ref(), atom(), any()) -> ok.
set_env(Ref, Name, Value) ->
	Opts = ranch:get_protocol_options(Ref),
	{_, Env} = lists:keyfind(env, 1, Opts),
	Opts2 = lists:keyreplace(env, 1, Opts,
		{env, lists:keystore(Name, 1, Env, {Name, Value})}),
	ok = ranch:set_protocol_options(Ref, Opts2).
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






















































































































































Deleted deps/cowboy/src/cowboy_app.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
%% Copyright (c) 2011-2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(cowboy_app).
-behaviour(application).

-export([start/2]).
-export([stop/1]).

-spec start(_, _) -> {ok, pid()}.
start(_, _) ->
	cowboy_sup:start_link().

-spec stop(_) -> ok.
stop(_) ->
	ok.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






















































Deleted deps/cowboy/src/cowboy_bstr.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
%% Copyright (c) 2011-2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(cowboy_bstr).

%% Binary strings.
-export([capitalize_token/1]).
-export([to_lower/1]).
-export([to_upper/1]).

%% Characters.
-export([char_to_lower/1]).
-export([char_to_upper/1]).

%% The first letter and all letters after a dash are capitalized.
%% This is the form seen for header names in the HTTP/1.1 RFC and
%% others. Note that using this form isn't required, as header names
%% are case insensitive, and it is only provided for use with eventual
%% badly implemented clients.
-spec capitalize_token(B) -> B when B::binary().
capitalize_token(B) ->
	capitalize_token(B, true, <<>>).
capitalize_token(<<>>, _, Acc) ->
	Acc;
capitalize_token(<< $-, Rest/bits >>, _, Acc) ->
	capitalize_token(Rest, true, << Acc/binary, $- >>);
capitalize_token(<< C, Rest/bits >>, true, Acc) ->
	capitalize_token(Rest, false, << Acc/binary, (char_to_upper(C)) >>);
capitalize_token(<< C, Rest/bits >>, false, Acc) ->
	capitalize_token(Rest, false, << Acc/binary, (char_to_lower(C)) >>).

-spec to_lower(B) -> B when B::binary().
to_lower(B) ->
	<< << (char_to_lower(C)) >> || << C >> <= B >>.

-spec to_upper(B) -> B when B::binary().
to_upper(B) ->
	<< << (char_to_upper(C)) >> || << C >> <= B >>.

-spec char_to_lower(char()) -> char().
char_to_lower($A) -> $a;
char_to_lower($B) -> $b;
char_to_lower($C) -> $c;
char_to_lower($D) -> $d;
char_to_lower($E) -> $e;
char_to_lower($F) -> $f;
char_to_lower($G) -> $g;
char_to_lower($H) -> $h;
char_to_lower($I) -> $i;
char_to_lower($J) -> $j;
char_to_lower($K) -> $k;
char_to_lower($L) -> $l;
char_to_lower($M) -> $m;
char_to_lower($N) -> $n;
char_to_lower($O) -> $o;
char_to_lower($P) -> $p;
char_to_lower($Q) -> $q;
char_to_lower($R) -> $r;
char_to_lower($S) -> $s;
char_to_lower($T) -> $t;
char_to_lower($U) -> $u;
char_to_lower($V) -> $v;
char_to_lower($W) -> $w;
char_to_lower($X) -> $x;
char_to_lower($Y) -> $y;
char_to_lower($Z) -> $z;
char_to_lower(Ch) -> Ch.

-spec char_to_upper(char()) -> char().
char_to_upper($a) -> $A;
char_to_upper($b) -> $B;
char_to_upper($c) -> $C;
char_to_upper($d) -> $D;
char_to_upper($e) -> $E;
char_to_upper($f) -> $F;
char_to_upper($g) -> $G;
char_to_upper($h) -> $H;
char_to_upper($i) -> $I;
char_to_upper($j) -> $J;
char_to_upper($k) -> $K;
char_to_upper($l) -> $L;
char_to_upper($m) -> $M;
char_to_upper($n) -> $N;
char_to_upper($o) -> $O;
char_to_upper($p) -> $P;
char_to_upper($q) -> $Q;
char_to_upper($r) -> $R;
char_to_upper($s) -> $S;
char_to_upper($t) -> $T;
char_to_upper($u) -> $U;
char_to_upper($v) -> $V;
char_to_upper($w) -> $W;
char_to_upper($x) -> $X;
char_to_upper($y) -> $Y;
char_to_upper($z) -> $Z;
char_to_upper(Ch) -> Ch.

%% Tests.

-ifdef(TEST).
capitalize_token_test_() ->
	Tests = [
		{<<"heLLo-woRld">>, <<"Hello-World">>},
		{<<"Sec-Websocket-Version">>, <<"Sec-Websocket-Version">>},
		{<<"Sec-WebSocket-Version">>, <<"Sec-Websocket-Version">>},
		{<<"sec-websocket-version">>, <<"Sec-Websocket-Version">>},
		{<<"SEC-WEBSOCKET-VERSION">>, <<"Sec-Websocket-Version">>},
		{<<"Sec-WebSocket--Version">>, <<"Sec-Websocket--Version">>},
		{<<"Sec-WebSocket---Version">>, <<"Sec-Websocket---Version">>}
	],
	[{H, fun() -> R = capitalize_token(H) end} || {H, R} <- Tests].
-endif.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






















































































































































































































































Deleted deps/cowboy/src/cowboy_clock.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
%% Copyright (c) 2011-2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

%% While a gen_server process runs in the background to update
%% the cache of formatted dates every second, all API calls are
%% local and directly read from the ETS cache table, providing
%% fast time and date computations.
-module(cowboy_clock).
-behaviour(gen_server).

%% API.
-export([start_link/0]).
-export([stop/0]).
-export([rfc1123/0]).
-export([rfc1123/1]).

%% gen_server.
-export([init/1]).
-export([handle_call/3]).
-export([handle_cast/2]).
-export([handle_info/2]).
-export([terminate/2]).
-export([code_change/3]).

-record(state, {
	universaltime = undefined :: undefined | calendar:datetime(),
	rfc1123 = <<>> :: binary(),
	tref = undefined :: undefined | reference()
}).

%% API.

-spec start_link() -> {ok, pid()}.
start_link() ->
	gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

-spec stop() -> stopped.
stop() ->
	gen_server:call(?MODULE, stop).

-spec rfc1123() -> binary().
rfc1123() ->
	ets:lookup_element(?MODULE, rfc1123, 2).

-spec rfc1123(calendar:datetime()) -> binary().
rfc1123(DateTime) ->
	update_rfc1123(<<>>, undefined, DateTime).

%% gen_server.

-spec init([]) -> {ok, #state{}}.
init([]) ->
	?MODULE = ets:new(?MODULE, [set, protected,
		named_table, {read_concurrency, true}]),
	T = erlang:universaltime(),
	B = update_rfc1123(<<>>, undefined, T),
	TRef = erlang:send_after(1000, self(), update),
	ets:insert(?MODULE, {rfc1123, B}),
	{ok, #state{universaltime=T, rfc1123=B, tref=TRef}}.

-type from() :: {pid(), term()}.
-spec handle_call
	(stop, from(), State) -> {stop, normal, stopped, State}
	when State::#state{}.
handle_call(stop, _From, State) ->
	{stop, normal, stopped, State};
handle_call(_Request, _From, State) ->
	{reply, ignored, State}.

-spec handle_cast(_, State) -> {noreply, State} when State::#state{}.
handle_cast(_Msg, State) ->
	{noreply, State}.

-spec handle_info(any(), State) -> {noreply, State} when State::#state{}.
handle_info(update, #state{universaltime=Prev, rfc1123=B1, tref=TRef0}) ->
	%% Cancel the timer in case an external process sent an update message.
	_ = erlang:cancel_timer(TRef0),
	T = erlang:universaltime(),
	B2 = update_rfc1123(B1, Prev, T),
	ets:insert(?MODULE, {rfc1123, B2}),
	TRef = erlang:send_after(1000, self(), update),
	{noreply, #state{universaltime=T, rfc1123=B2, tref=TRef}};
handle_info(_Info, State) ->
	{noreply, State}.

-spec terminate(_, _) -> ok.
terminate(_Reason, _State) ->
	ok.

-spec code_change(_, State, _) -> {ok, State} when State::#state{}.
code_change(_OldVsn, State, _Extra) ->
	{ok, State}.

%% Internal.

-spec update_rfc1123(binary(), undefined | calendar:datetime(),
	calendar:datetime()) -> binary().
update_rfc1123(Bin, Now, Now) ->
	Bin;
update_rfc1123(<< Keep:23/binary, _/bits >>,
		{Date, {H, M, _}}, {Date, {H, M, S}}) ->
	<< Keep/binary, (pad_int(S))/binary, " GMT" >>;
update_rfc1123(<< Keep:20/binary, _/bits >>,
		{Date, {H, _, _}}, {Date, {H, M, S}}) ->
	<< Keep/binary, (pad_int(M))/binary, $:, (pad_int(S))/binary, " GMT" >>;
update_rfc1123(<< Keep:17/binary, _/bits >>, {Date, _}, {Date, {H, M, S}}) ->
	<< Keep/binary, (pad_int(H))/binary, $:, (pad_int(M))/binary,
		$:, (pad_int(S))/binary, " GMT" >>;
update_rfc1123(<< _:7/binary, Keep:10/binary, _/bits >>,
		{{Y, Mo, _}, _}, {Date = {Y, Mo, D}, {H, M, S}}) ->
	Wday = calendar:day_of_the_week(Date),
	<< (weekday(Wday))/binary, ", ", (pad_int(D))/binary, Keep/binary,
		(pad_int(H))/binary, $:, (pad_int(M))/binary,
		$:, (pad_int(S))/binary, " GMT" >>;
update_rfc1123(<< _:11/binary, Keep:6/binary, _/bits >>,
		{{Y, _, _}, _}, {Date = {Y, Mo, D}, {H, M, S}}) ->
	Wday = calendar:day_of_the_week(Date),
	<< (weekday(Wday))/binary, ", ", (pad_int(D))/binary, " ",
		(month(Mo))/binary, Keep/binary,
		(pad_int(H))/binary, $:, (pad_int(M))/binary,
		$:, (pad_int(S))/binary, " GMT" >>;
update_rfc1123(_, _, {Date = {Y, Mo, D}, {H, M, S}}) ->
	Wday = calendar:day_of_the_week(Date),
	<< (weekday(Wday))/binary, ", ", (pad_int(D))/binary, " ",
		(month(Mo))/binary, " ", (integer_to_binary(Y))/binary,
		" ", (pad_int(H))/binary, $:, (pad_int(M))/binary,
		$:, (pad_int(S))/binary, " GMT" >>.

%% Following suggestion by MononcQc on #erlounge.
-spec pad_int(0..59) -> binary().
pad_int(X) when X < 10 ->
	<< $0, ($0 + X) >>;
pad_int(X) ->
	integer_to_binary(X).

-spec weekday(1..7) -> <<_:24>>.
weekday(1) -> <<"Mon">>;
weekday(2) -> <<"Tue">>;
weekday(3) -> <<"Wed">>;
weekday(4) -> <<"Thu">>;
weekday(5) -> <<"Fri">>;
weekday(6) -> <<"Sat">>;
weekday(7) -> <<"Sun">>.

-spec month(1..12) -> <<_:24>>.
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">>.

%% Tests.

-ifdef(TEST).
update_rfc1123_test_() ->
	Tests = [
		{<<"Sat, 14 May 2011 14:25:33 GMT">>, undefined,
			{{2011, 5, 14}, {14, 25, 33}}, <<>>},
		{<<"Sat, 14 May 2011 14:25:33 GMT">>, {{2011, 5, 14}, {14, 25, 33}},
			{{2011, 5, 14}, {14, 25, 33}}, <<"Sat, 14 May 2011 14:25:33 GMT">>},
		{<<"Sat, 14 May 2011 14:25:34 GMT">>, {{2011, 5, 14}, {14, 25, 33}},
			{{2011, 5, 14}, {14, 25, 34}}, <<"Sat, 14 May 2011 14:25:33 GMT">>},
		{<<"Sat, 14 May 2011 14:26:00 GMT">>, {{2011, 5, 14}, {14, 25, 59}},
			{{2011, 5, 14}, {14, 26,  0}}, <<"Sat, 14 May 2011 14:25:59 GMT">>},
		{<<"Sat, 14 May 2011 15:00:00 GMT">>, {{2011, 5, 14}, {14, 59, 59}},
			{{2011, 5, 14}, {15,  0,  0}}, <<"Sat, 14 May 2011 14:59:59 GMT">>},
		{<<"Sun, 15 May 2011 00:00:00 GMT">>, {{2011, 5, 14}, {23, 59, 59}},
			{{2011, 5, 15}, { 0,  0,  0}}, <<"Sat, 14 May 2011 23:59:59 GMT">>},
		{<<"Wed, 01 Jun 2011 00:00:00 GMT">>, {{2011, 5, 31}, {23, 59, 59}},
			{{2011, 6,  1}, { 0,  0,  0}}, <<"Tue, 31 May 2011 23:59:59 GMT">>},
		{<<"Sun, 01 Jan 2012 00:00:00 GMT">>, {{2011, 5, 31}, {23, 59, 59}},
			{{2012, 1,  1}, { 0,  0,  0}}, <<"Sat, 31 Dec 2011 23:59:59 GMT">>}
	],
	[{R, fun() -> R = update_rfc1123(B, P, N) end} || {R, P, N, B} <- Tests].

pad_int_test_() ->
	Tests = [
		{ 0, <<"00">>}, { 1, <<"01">>}, { 2, <<"02">>}, { 3, <<"03">>},
		{ 4, <<"04">>}, { 5, <<"05">>}, { 6, <<"06">>}, { 7, <<"07">>},
		{ 8, <<"08">>}, { 9, <<"09">>}, {10, <<"10">>}, {11, <<"11">>},
		{12, <<"12">>}, {13, <<"13">>}, {14, <<"14">>}, {15, <<"15">>},
		{16, <<"16">>}, {17, <<"17">>}, {18, <<"18">>}, {19, <<"19">>},
		{20, <<"20">>}, {21, <<"21">>}, {22, <<"22">>}, {23, <<"23">>},
		{24, <<"24">>}, {25, <<"25">>}, {26, <<"26">>}, {27, <<"27">>},
		{28, <<"28">>}, {29, <<"29">>}, {30, <<"30">>}, {31, <<"31">>},
		{32, <<"32">>}, {33, <<"33">>}, {34, <<"34">>}, {35, <<"35">>},
		{36, <<"36">>}, {37, <<"37">>}, {38, <<"38">>}, {39, <<"39">>},
		{40, <<"40">>}, {41, <<"41">>}, {42, <<"42">>}, {43, <<"43">>},
		{44, <<"44">>}, {45, <<"45">>}, {46, <<"46">>}, {47, <<"47">>},
		{48, <<"48">>}, {49, <<"49">>}, {50, <<"50">>}, {51, <<"51">>},
		{52, <<"52">>}, {53, <<"53">>}, {54, <<"54">>}, {55, <<"55">>},
		{56, <<"56">>}, {57, <<"57">>}, {58, <<"58">>}, {59, <<"59">>}
	],
	[{I, fun() -> O = pad_int(I) end} || {I, O} <- Tests].
-endif.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<










































































































































































































































































































































































































































Deleted deps/cowboy/src/cowboy_handler.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
%% Copyright (c) 2011-2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

%% Handler middleware.
%%
%% Execute the handler given by the <em>handler</em> and <em>handler_opts</em>
%% environment values. The result of this execution is added to the
%% environment under the <em>result</em> value.
%%
%% When using loop handlers, we are receiving data from the socket because we
%% want to know when the socket gets closed. This is generally not an issue
%% because these kinds of requests are generally not pipelined, and don't have
%% a body. If they do have a body, this body is often read in the
%% <em>init/3</em> callback and this is no problem. Otherwise, this data
%% accumulates in a buffer until we reach a certain threshold of 5000 bytes
%% by default. This can be configured through the <em>loop_max_buffer</em>
%% environment value. The request will be terminated with an
%% <em>{error, overflow}</em> reason if this threshold is reached.
-module(cowboy_handler).
-behaviour(cowboy_middleware).

-export([execute/2]).
-export([handler_loop/4]).

-record(state, {
	env :: cowboy_middleware:env(),
	hibernate = false :: boolean(),
	loop_buffer_size = 0 :: non_neg_integer(),
	loop_max_buffer = 5000 :: non_neg_integer() | infinity,
	loop_timeout = infinity :: timeout(),
	loop_timeout_ref = undefined :: undefined | reference(),
	resp_sent = false :: boolean()
}).

-spec execute(Req, Env)
	-> {ok, Req, Env} | {suspend, ?MODULE, handler_loop, [any()]}
	when Req::cowboy_req:req(), Env::cowboy_middleware:env().
execute(Req, Env) ->
	{_, Handler} = lists:keyfind(handler, 1, Env),
	{_, HandlerOpts} = lists:keyfind(handler_opts, 1, Env),
	MaxBuffer = case lists:keyfind(loop_max_buffer, 1, Env) of
		false -> 5000;
		{_, MaxBuffer0} -> MaxBuffer0
	end,
	handler_init(Req, #state{env=Env, loop_max_buffer=MaxBuffer},
		Handler, HandlerOpts).

-spec handler_init(Req, #state{}, module(), any())
	-> {ok, Req, cowboy_middleware:env()} | {suspend, module(), atom(), [any()]}
	when Req::cowboy_req:req().
handler_init(Req, State, Handler, HandlerOpts) ->
	Transport = cowboy_req:get(transport, Req),
	try Handler:init({Transport:name(), http}, Req, HandlerOpts) of
		{ok, Req2, HandlerState} ->
			handler_handle(Req2, State, Handler, HandlerState);
		{loop, Req2, HandlerState} ->
			handler_after_callback(Req2, State, Handler, HandlerState);
		{loop, Req2, HandlerState, hibernate} ->
			handler_after_callback(Req2, State#state{hibernate=true},
				Handler, HandlerState);
		{loop, Req2, HandlerState, Timeout} ->
			State2 = handler_loop_timeout(State#state{loop_timeout=Timeout}),
			handler_after_callback(Req2, State2, Handler, HandlerState);
		{loop, Req2, HandlerState, Timeout, hibernate} ->
			State2 = handler_loop_timeout(State#state{
				hibernate=true, loop_timeout=Timeout}),
			handler_after_callback(Req2, State2, Handler, HandlerState);
		{shutdown, Req2, HandlerState} ->
			terminate_request(Req2, State, Handler, HandlerState,
				{normal, shutdown});
		{upgrade, protocol, Module} ->
			upgrade_protocol(Req, State, Handler, HandlerOpts, Module);
		{upgrade, protocol, Module, Req2, HandlerOpts2} ->
			upgrade_protocol(Req2, State, Handler, HandlerOpts2, Module)
	catch Class:Reason ->
		Stacktrace = erlang:get_stacktrace(),
		cowboy_req:maybe_reply(Stacktrace, Req),
		erlang:Class([
			{reason, Reason},
			{mfa, {Handler, init, 3}},
			{stacktrace, Stacktrace},
			{req, cowboy_req:to_list(Req)},
			{opts, HandlerOpts}
		])
	end.

-spec upgrade_protocol(Req, #state{}, module(), any(), module())
	-> {ok, Req, Env}
	| {suspend, module(), atom(), any()}
	| {halt, Req}
	| {error, cowboy:http_status(), Req}
	when Req::cowboy_req:req(), Env::cowboy_middleware:env().
upgrade_protocol(Req, #state{env=Env},
		Handler, HandlerOpts, Module) ->
	Module:upgrade(Req, Env, Handler, HandlerOpts).

-spec handler_handle(Req, #state{}, module(), any())
	-> {ok, Req, cowboy_middleware:env()} when Req::cowboy_req:req().
handler_handle(Req, State, Handler, HandlerState) ->
	try Handler:handle(Req, HandlerState) of
		{ok, Req2, HandlerState2} ->
			terminate_request(Req2, State, Handler, HandlerState2,
				{normal, shutdown})
	catch Class:Reason ->
		Stacktrace = erlang:get_stacktrace(),
		cowboy_req:maybe_reply(Stacktrace, Req),
		handler_terminate(Req, Handler, HandlerState, Reason),
		erlang:Class([
			{reason, Reason},
			{mfa, {Handler, handle, 2}},
			{stacktrace, Stacktrace},
			{req, cowboy_req:to_list(Req)},
			{state, HandlerState}
		])
	end.

%% Update the state if the response was sent in the callback.
-spec handler_after_callback(Req, #state{}, module(), any())
	-> {ok, Req, cowboy_middleware:env()} | {suspend, module(), atom(), [any()]}
	when Req::cowboy_req:req().
handler_after_callback(Req, State=#state{resp_sent=false}, Handler,
		HandlerState) ->
	receive
		{cowboy_req, resp_sent} ->
			handler_before_loop(Req, State#state{resp_sent=true}, Handler,
				HandlerState)
	after 0 ->
		handler_before_loop(Req, State, Handler, HandlerState)
	end;
handler_after_callback(Req, State, Handler, HandlerState) ->
	handler_before_loop(Req, State, Handler, HandlerState).

-spec handler_before_loop(Req, #state{}, module(), any())
	-> {ok, Req, cowboy_middleware:env()} | {suspend, module(), atom(), [any()]}
	when Req::cowboy_req:req().
handler_before_loop(Req, State=#state{hibernate=true}, Handler, HandlerState) ->
	[Socket, Transport] = cowboy_req:get([socket, transport], Req),
	Transport:setopts(Socket, [{active, once}]),
	{suspend, ?MODULE, handler_loop,
		[Req, State#state{hibernate=false}, Handler, HandlerState]};
handler_before_loop(Req, State, Handler, HandlerState) ->
	[Socket, Transport] = cowboy_req:get([socket, transport], Req),
	Transport:setopts(Socket, [{active, once}]),
	handler_loop(Req, State, Handler, HandlerState).

%% Almost the same code can be found in cowboy_websocket.
-spec handler_loop_timeout(#state{}) -> #state{}.
handler_loop_timeout(State=#state{loop_timeout=infinity}) ->
	State#state{loop_timeout_ref=undefined};
handler_loop_timeout(State=#state{loop_timeout=Timeout,
		loop_timeout_ref=PrevRef}) ->
	_ = case PrevRef of
		undefined -> ignore;
		PrevRef -> erlang:cancel_timer(PrevRef)
	end,
	TRef = erlang:start_timer(Timeout, self(), ?MODULE),
	State#state{loop_timeout_ref=TRef}.

-spec handler_loop(Req, #state{}, module(), any())
	-> {ok, Req, cowboy_middleware:env()} | {suspend, module(), atom(), [any()]}
	when Req::cowboy_req:req().
handler_loop(Req, State=#state{loop_buffer_size=NbBytes,
		loop_max_buffer=Threshold, loop_timeout_ref=TRef,
		resp_sent=RespSent}, Handler, HandlerState) ->
	[Socket, Transport] = cowboy_req:get([socket, transport], Req),
	{OK, Closed, Error} = Transport:messages(),
	receive
		{OK, Socket, Data} ->
			NbBytes2 = NbBytes + byte_size(Data),
			if	NbBytes2 > Threshold ->
					_ = handler_terminate(Req, Handler, HandlerState,
						{error, overflow}),
					_ = if RespSent -> ok; true ->
						cowboy_req:reply(500, Req)
					end,
					exit(normal);
				true ->
					Req2 = cowboy_req:append_buffer(Data, Req),
					State2 = handler_loop_timeout(State#state{
						loop_buffer_size=NbBytes2}),
					handler_before_loop(Req2, State2, Handler, HandlerState)
			end;
		{Closed, Socket} ->
			terminate_request(Req, State, Handler, HandlerState,
				{error, closed});
		{Error, Socket, Reason} ->
			terminate_request(Req, State, Handler, HandlerState,
				{error, Reason});
		{timeout, TRef, ?MODULE} ->
			handler_after_loop(Req, State, Handler, HandlerState,
				{normal, timeout});
		{timeout, OlderTRef, ?MODULE} when is_reference(OlderTRef) ->
			handler_loop(Req, State, Handler, HandlerState);
		Message ->
			%% We set the socket back to {active, false} mode in case
			%% the handler is going to call recv. We also flush any
			%% data received after that and put it into the buffer.
			%% We do not check the size here, if data keeps coming
			%% we'll error out on the next packet received.
			Transport:setopts(Socket, [{active, false}]),
			Req2 = receive {OK, Socket, Data} ->
				cowboy_req:append_buffer(Data, Req)
			after 0 ->
				Req
			end,
			handler_call(Req2, State, Handler, HandlerState, Message)
	end.

-spec handler_call(Req, #state{}, module(), any(), any())
	-> {ok, Req, cowboy_middleware:env()} | {suspend, module(), atom(), [any()]}
	when Req::cowboy_req:req().
handler_call(Req, State=#state{resp_sent=RespSent},
		Handler, HandlerState, Message) ->
	try Handler:info(Message, Req, HandlerState) of
		{ok, Req2, HandlerState2} ->
			handler_after_loop(Req2, State, Handler, HandlerState2,
				{normal, shutdown});
		{loop, Req2, HandlerState2} ->
			handler_after_callback(Req2, State, Handler, HandlerState2);
		{loop, Req2, HandlerState2, hibernate} ->
			handler_after_callback(Req2, State#state{hibernate=true},
				Handler, HandlerState2)
	catch Class:Reason ->
		Stacktrace = erlang:get_stacktrace(),
		if RespSent -> ok; true ->
			cowboy_req:maybe_reply(Stacktrace, Req)
		end,
		handler_terminate(Req, Handler, HandlerState, Reason),
		erlang:Class([
			{reason, Reason},
			{mfa, {Handler, info, 3}},
			{stacktrace, Stacktrace},
			{req, cowboy_req:to_list(Req)},
			{state, HandlerState}
		])
	end.

%% It is sometimes important to make a socket passive as it was initially
%% and as it is expected to be by cowboy_protocol, right after we're done
%% with loop handling. The browser may freely pipeline a bunch of requests
%% if previous one was, say, a JSONP long-polling request.
-spec handler_after_loop(Req, #state{}, module(), any(),
	{normal, timeout | shutdown} | {error, atom()}) ->
	{ok, Req, cowboy_middleware:env()} when Req::cowboy_req:req().
handler_after_loop(Req, State, Handler, HandlerState, Reason) ->
	[Socket, Transport] = cowboy_req:get([socket, transport], Req),
	Transport:setopts(Socket, [{active, false}]),
	{OK, _Closed, _Error} = Transport:messages(),
	Req2 = receive
		{OK, Socket, Data} ->
			cowboy_req:append_buffer(Data, Req)
	after 0 ->
		Req
	end,
	terminate_request(Req2, State, Handler, HandlerState, Reason).

-spec terminate_request(Req, #state{}, module(), any(),
	{normal, timeout | shutdown} | {error, atom()}) ->
	{ok, Req, cowboy_middleware:env()} when Req::cowboy_req:req().
terminate_request(Req, #state{env=Env, loop_timeout_ref=TRef},
		Handler, HandlerState, Reason) ->
	HandlerRes = handler_terminate(Req, Handler, HandlerState, Reason),
	_ = case TRef of
		undefined -> ignore;
		TRef -> erlang:cancel_timer(TRef)
	end,
	flush_timeouts(),
	{ok, Req, [{result, HandlerRes}|Env]}.

-spec handler_terminate(cowboy_req:req(), module(), any(),
	{normal, timeout | shutdown} | {error, atom()}) -> ok.
handler_terminate(Req, Handler, HandlerState, Reason) ->
	try
		Handler:terminate(Reason, cowboy_req:lock(Req), HandlerState)
	catch Class:Reason2 ->
		erlang:Class([
			{reason, Reason2},
			{mfa, {Handler, terminate, 3}},
			{stacktrace, erlang:get_stacktrace()},
			{req, cowboy_req:to_list(Req)},
			{state, HandlerState},
			{terminate_reason, Reason}
		])
	end.

-spec flush_timeouts() -> ok.
flush_timeouts() ->
	receive
		{timeout, TRef, ?MODULE} when is_reference(TRef) ->
			flush_timeouts()
	after 0 ->
		ok
	end.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
































































































































































































































































































































































































































































































































































































































Deleted deps/cowboy/src/cowboy_http.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
%% Copyright (c) 2011-2014, Loïc Hoguin <essen@ninenines.eu>
%% Copyright (c) 2011, Anthony Ramine <nox@dev-extend.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

%% Deprecated HTTP parsing API.
-module(cowboy_http).

%% Parsing.
-export([list/2]).
-export([nonempty_list/2]).
-export([content_type/1]).
-export([media_range/2]).
-export([conneg/2]).
-export([language_range/2]).
-export([entity_tag_match/1]).
-export([expectation/2]).
-export([params/2]).
-export([http_date/1]).
-export([rfc1123_date/1]).
-export([rfc850_date/1]).
-export([asctime_date/1]).
-export([whitespace/2]).
-export([digits/1]).
-export([token/2]).
-export([token_ci/2]).
-export([quoted_string/2]).
-export([authorization/2]).
-export([range/1]).
-export([parameterized_tokens/1]).

%% Decoding.
-export([ce_identity/1]).

%% Parsing.

-spec nonempty_list(binary(), fun()) -> [any(), ...] | {error, badarg}.
nonempty_list(Data, Fun) ->
	case list(Data, Fun, []) of
		{error, badarg} -> {error, badarg};
		[] -> {error, badarg};
		L -> lists:reverse(L)
	end.

-spec list(binary(), fun()) -> list() | {error, badarg}.
list(Data, Fun) ->
	case list(Data, Fun, []) of
		{error, badarg} -> {error, badarg};
		L -> lists:reverse(L)
	end.

-spec list(binary(), fun(), [binary()]) -> [any()] | {error, badarg}.
%% From the RFC:
%% <blockquote>Wherever this construct is used, null elements are allowed,
%% but do not contribute to the count of elements present.
%% That is, "(element), , (element) " is permitted, but counts
%% as only two elements. Therefore, where at least one element is required,
%% at least one non-null element MUST be present.</blockquote>
list(Data, Fun, Acc) ->
	whitespace(Data,
		fun (<<>>) -> Acc;
			(<< $,, Rest/binary >>) -> list(Rest, Fun, Acc);
			(Rest) -> Fun(Rest,
				fun (D, I) -> whitespace(D,
						fun (<<>>) -> [I|Acc];
							(<< $,, R/binary >>) -> list(R, Fun, [I|Acc]);
							(_Any) -> {error, badarg}
						end)
				end)
		end).

%% We lowercase the charset header as we know it's case insensitive.
-spec content_type(binary()) -> any().
content_type(Data) ->
	media_type(Data,
		fun (Rest, Type, SubType) ->
			params(Rest,
				fun (<<>>, Params) ->
						case lists:keyfind(<<"charset">>, 1, Params) of
							false ->
								{Type, SubType, Params};
							{_, Charset} ->
								Charset2 = cowboy_bstr:to_lower(Charset),
								Params2 = lists:keyreplace(<<"charset">>,
									1, Params, {<<"charset">>, Charset2}),
								{Type, SubType, Params2}
						end;
					(_Rest2, _) ->
						{error, badarg}
				end)
		end).

-spec media_range(binary(), fun()) -> any().
media_range(Data, Fun) ->
	media_type(Data,
		fun (Rest, Type, SubType) ->
			media_range_params(Rest, Fun, Type, SubType, [])
		end).

-spec media_range_params(binary(), fun(), binary(), binary(),
	[{binary(), binary()}]) -> any().
media_range_params(Data, Fun, Type, SubType, Acc) ->
	whitespace(Data,
		fun (<< $;, Rest/binary >>) ->
				whitespace(Rest,
					fun (Rest2) ->
						media_range_param_attr(Rest2, Fun, Type, SubType, Acc)
					end);
			(Rest) -> Fun(Rest, {{Type, SubType, lists:reverse(Acc)}, 1000, []})
		end).

-spec media_range_param_attr(binary(), fun(), binary(), binary(),
	[{binary(), binary()}]) -> any().
media_range_param_attr(Data, Fun, Type, SubType, Acc) ->
	token_ci(Data,
		fun (_Rest, <<>>) -> {error, badarg};
			(<< $=, Rest/binary >>, Attr) ->
				media_range_param_value(Rest, Fun, Type, SubType, Acc, Attr)
		end).

-spec media_range_param_value(binary(), fun(), binary(), binary(),
	[{binary(), binary()}], binary()) -> any().
media_range_param_value(Data, Fun, Type, SubType, Acc, <<"q">>) ->
	qvalue(Data,
		fun (Rest, Quality) ->
			accept_ext(Rest, Fun, Type, SubType, Acc, Quality, [])
		end);
media_range_param_value(Data, Fun, Type, SubType, Acc, Attr) ->
	word(Data,
		fun (Rest, Value) ->
			media_range_params(Rest, Fun,
				Type, SubType, [{Attr, Value}|Acc])
		end).

-spec media_type(binary(), fun()) -> any().
media_type(Data, Fun) ->
	token_ci(Data,
		fun (_Rest, <<>>) -> {error, badarg};
			(<< $/, Rest/binary >>, Type) ->
				token_ci(Rest,
					fun (_Rest2, <<>>) -> {error, badarg};
						(Rest2, SubType) -> Fun(Rest2, Type, SubType)
					end);
			%% This is a non-strict parsing clause required by some user agents
			%% that use * instead of */* in the list of media types.
			(Rest, <<"*">> = Type) ->
				token_ci(<<"*", Rest/binary>>,
					fun (_Rest2, <<>>) -> {error, badarg};
						(Rest2, SubType) -> Fun(Rest2, Type, SubType)
					end);
			(_Rest, _Type) -> {error, badarg}
		end).

-spec accept_ext(binary(), fun(), binary(), binary(),
	[{binary(), binary()}], 0..1000,
	[{binary(), binary()} | binary()]) -> any().
accept_ext(Data, Fun, Type, SubType, Params, Quality, Acc) ->
	whitespace(Data,
		fun (<< $;, Rest/binary >>) ->
				whitespace(Rest,
					fun (Rest2) ->
						accept_ext_attr(Rest2, Fun,
							Type, SubType, Params, Quality, Acc)
					end);
			(Rest) ->
				Fun(Rest, {{Type, SubType, lists:reverse(Params)},
					Quality, lists:reverse(Acc)})
		end).

-spec accept_ext_attr(binary(), fun(), binary(), binary(),
	[{binary(), binary()}], 0..1000,
	[{binary(), binary()} | binary()]) -> any().
accept_ext_attr(Data, Fun, Type, SubType, Params, Quality, Acc) ->
	token_ci(Data,
		fun (_Rest, <<>>) -> {error, badarg};
			(<< $=, Rest/binary >>, Attr) ->
				accept_ext_value(Rest, Fun, Type, SubType, Params,
					Quality, Acc, Attr);
			(Rest, Attr) ->
				accept_ext(Rest, Fun, Type, SubType, Params,
					Quality, [Attr|Acc])
		end).

-spec accept_ext_value(binary(), fun(), binary(), binary(),
	[{binary(), binary()}], 0..1000,
	[{binary(), binary()} | binary()], binary()) -> any().
accept_ext_value(Data, Fun, Type, SubType, Params, Quality, Acc, Attr) ->
	word(Data,
		fun (Rest, Value) ->
				accept_ext(Rest, Fun,
					Type, SubType, Params, Quality, [{Attr, Value}|Acc])
		end).

-spec conneg(binary(), fun()) -> any().
conneg(Data, Fun) ->
	token_ci(Data,
		fun (_Rest, <<>>) -> {error, badarg};
			(Rest, Conneg) ->
				maybe_qparam(Rest,
					fun (Rest2, Quality) ->
						Fun(Rest2, {Conneg, Quality})
					end)
		end).

-spec language_range(binary(), fun()) -> any().
language_range(<< $*, Rest/binary >>, Fun) ->
	language_range_ret(Rest, Fun, '*');
language_range(Data, Fun) ->
	language_tag(Data,
		fun (Rest, LanguageTag) ->
				language_range_ret(Rest, Fun, LanguageTag)
		end).

-spec language_range_ret(binary(), fun(), '*' | {binary(), [binary()]}) -> any().
language_range_ret(Data, Fun, LanguageTag) ->
	maybe_qparam(Data,
		fun (Rest, Quality) ->
				Fun(Rest, {LanguageTag, Quality})
		end).

-spec language_tag(binary(), fun()) -> any().
language_tag(Data, Fun) ->
	alpha(Data,
		fun (_Rest, Tag) when byte_size(Tag) =:= 0; byte_size(Tag) > 8 ->
				{error, badarg};
			(<< $-, Rest/binary >>, Tag) ->
				language_subtag(Rest, Fun, Tag, []);
			(Rest, Tag) ->
				Fun(Rest, Tag)
		end).

-spec language_subtag(binary(), fun(), binary(), [binary()]) -> any().
language_subtag(Data, Fun, Tag, Acc) ->
	alphanumeric(Data,
		fun (_Rest, SubTag) when byte_size(SubTag) =:= 0;
				byte_size(SubTag) > 8 -> {error, badarg};
			(<< $-, Rest/binary >>, SubTag) ->
				language_subtag(Rest, Fun, Tag, [SubTag|Acc]);
			(Rest, SubTag) ->
				%% Rebuild the full tag now that we know it's correct
				Sub = << << $-, S/binary >> || S <- lists:reverse([SubTag|Acc]) >>,
				Fun(Rest, << Tag/binary, Sub/binary >>)
		end).

-spec maybe_qparam(binary(), fun()) -> any().
maybe_qparam(Data, Fun) ->
	whitespace(Data,
		fun (<< $;, Rest/binary >>) ->
			whitespace(Rest,
				fun (Rest2) ->
					%% This is a non-strict parsing clause required by some user agents
					%% that use the wrong delimiter putting a charset where a qparam is
					%% expected.
					try qparam(Rest2, Fun) of
						Result -> Result
					catch
						error:function_clause ->
							Fun(<<",", Rest2/binary>>, 1000)
					end
				end);
			(Rest) ->
				Fun(Rest, 1000)
		end).

-spec qparam(binary(), fun()) -> any().
qparam(<< Q, $=, Data/binary >>, Fun) when Q =:= $q; Q =:= $Q ->
	qvalue(Data, Fun).

-spec entity_tag_match(binary()) -> any().
entity_tag_match(<< $*, Rest/binary >>) ->
	whitespace(Rest,
		fun (<<>>) -> '*';
			(_Any) -> {error, badarg}
		end);
entity_tag_match(Data) ->
	nonempty_list(Data, fun entity_tag/2).

-spec entity_tag(binary(), fun()) -> any().
entity_tag(<< "W/", Rest/binary >>, Fun) ->
	opaque_tag(Rest, Fun, weak);
entity_tag(Data, Fun) ->
	opaque_tag(Data, Fun, strong).

-spec opaque_tag(binary(), fun(), weak | strong) -> any().
opaque_tag(Data, Fun, Strength) ->
	quoted_string(Data,
		fun (_Rest, <<>>) -> {error, badarg};
			(Rest, OpaqueTag) -> Fun(Rest, {Strength, OpaqueTag})
		end).

-spec expectation(binary(), fun()) -> any().
expectation(Data, Fun) ->
	token_ci(Data,
		fun (_Rest, <<>>) -> {error, badarg};
			(<< $=, Rest/binary >>, Expectation) ->
				word(Rest,
					fun (Rest2, ExtValue) ->
						params(Rest2, fun (Rest3, ExtParams) ->
							Fun(Rest3, {Expectation, ExtValue, ExtParams})
						end)
					end);
			(Rest, Expectation) ->
				Fun(Rest, Expectation)
		end).

-spec params(binary(), fun()) -> any().
params(Data, Fun) ->
	params(Data, Fun, []).

-spec params(binary(), fun(), [{binary(), binary()}]) -> any().
params(Data, Fun, Acc) ->
	whitespace(Data,
		fun (<< $;, Rest/binary >>) ->
				param(Rest,
					fun (Rest2, Attr, Value) ->
							params(Rest2, Fun, [{Attr, Value}|Acc])
					end);
			(Rest) ->
				Fun(Rest, lists:reverse(Acc))
		end).

-spec param(binary(), fun()) -> any().
param(Data, Fun) ->
	whitespace(Data,
		fun (Rest) ->
				token_ci(Rest,
					fun (_Rest2, <<>>) -> {error, badarg};
						(<< $=, Rest2/binary >>, Attr) ->
							word(Rest2,
								fun (Rest3, Value) ->
										Fun(Rest3, Attr, Value)
								end);
						(_Rest2, _Attr) -> {error, badarg}
					end)
		end).

%% While this may not be the most efficient date parsing we can do,
%% it should work fine for our purposes because all HTTP dates should
%% be sent as RFC1123 dates in HTTP/1.1.
-spec http_date(binary()) -> any().
http_date(Data) ->
	case rfc1123_date(Data) of
		{error, badarg} ->
			case rfc850_date(Data) of
				{error, badarg} ->
					case asctime_date(Data) of
						{error, badarg} ->
							{error, badarg};
						HTTPDate ->
							HTTPDate
					end;
				HTTPDate ->
					HTTPDate
			end;
		HTTPDate ->
			HTTPDate
	end.

-spec rfc1123_date(binary()) -> any().
rfc1123_date(Data) ->
	wkday(Data,
		fun (<< ", ", Rest/binary >>, _WkDay) ->
				date1(Rest,
					fun (<< " ", Rest2/binary >>, Date) ->
							time(Rest2,
								fun (<< " GMT", Rest3/binary >>, Time) ->
										http_date_ret(Rest3, {Date, Time});
									(_Any, _Time) ->
										{error, badarg}
								end);
						(_Any, _Date) ->
							{error, badarg}
					end);
			(_Any, _WkDay) ->
				{error, badarg}
		end).

-spec rfc850_date(binary()) -> any().
%% From the RFC:
%% HTTP/1.1 clients and caches SHOULD assume that an RFC-850 date
%% which appears to be more than 50 years in the future is in fact
%% in the past (this helps solve the "year 2000" problem).
rfc850_date(Data) ->
	weekday(Data,
		fun (<< ", ", Rest/binary >>, _WeekDay) ->
				date2(Rest,
					fun (<< " ", Rest2/binary >>, Date) ->
							time(Rest2,
								fun (<< " GMT", Rest3/binary >>, Time) ->
										http_date_ret(Rest3, {Date, Time});
									(_Any, _Time) ->
										{error, badarg}
								end);
						(_Any, _Date) ->
							{error, badarg}
					end);
			(_Any, _WeekDay) ->
				{error, badarg}
		end).

-spec asctime_date(binary()) -> any().
asctime_date(Data) ->
	wkday(Data,
		fun (<< " ", Rest/binary >>, _WkDay) ->
				date3(Rest,
					fun (<< " ", Rest2/binary >>, PartialDate) ->
							time(Rest2,
								fun (<< " ", Rest3/binary >>, Time) ->
										asctime_year(Rest3,
											PartialDate, Time);
									(_Any, _Time) ->
										{error, badarg}
								end);
						(_Any, _PartialDate) ->
							{error, badarg}
					end);
			(_Any, _WkDay) ->
				{error, badarg}
		end).

-spec asctime_year(binary(), tuple(), tuple()) -> any().
asctime_year(<< Y1, Y2, Y3, Y4, Rest/binary >>, {Month, Day}, Time)
		when Y1 >= $0, Y1 =< $9, Y2 >= $0, Y2 =< $9,
			 Y3 >= $0, Y3 =< $9, Y4 >= $0, Y4 =< $9 ->
	Year = (Y1 - $0) * 1000 + (Y2 - $0) * 100 + (Y3 - $0) * 10 + (Y4 - $0),
	http_date_ret(Rest, {{Year, Month, Day}, Time}).

-spec http_date_ret(binary(), tuple()) -> any().
http_date_ret(Data, DateTime = {Date, _Time}) ->
	whitespace(Data,
		fun (<<>>) ->
				case calendar:valid_date(Date) of
					true -> DateTime;
					false -> {error, badarg}
				end;
			(_Any) ->
				{error, badarg}
		end).

%% We never use it, pretty much just checks the wkday is right.
-spec wkday(binary(), fun()) -> any().
wkday(<< WkDay:3/binary, Rest/binary >>, Fun)
		when WkDay =:= <<"Mon">>; WkDay =:= <<"Tue">>; WkDay =:= <<"Wed">>;
			 WkDay =:= <<"Thu">>; WkDay =:= <<"Fri">>; WkDay =:= <<"Sat">>;
			 WkDay =:= <<"Sun">> ->
	Fun(Rest, WkDay);
wkday(_Any, _Fun) ->
	{error, badarg}.

%% We never use it, pretty much just checks the weekday is right.
-spec weekday(binary(), fun()) -> any().
weekday(<< "Monday", Rest/binary >>, Fun) ->
	Fun(Rest, <<"Monday">>);
weekday(<< "Tuesday", Rest/binary >>, Fun) ->
	Fun(Rest, <<"Tuesday">>);
weekday(<< "Wednesday", Rest/binary >>, Fun) ->
	Fun(Rest, <<"Wednesday">>);
weekday(<< "Thursday", Rest/binary >>, Fun) ->
	Fun(Rest, <<"Thursday">>);
weekday(<< "Friday", Rest/binary >>, Fun) ->
	Fun(Rest, <<"Friday">>);
weekday(<< "Saturday", Rest/binary >>, Fun) ->
	Fun(Rest, <<"Saturday">>);
weekday(<< "Sunday", Rest/binary >>, Fun) ->
	Fun(Rest, <<"Sunday">>);
weekday(_Any, _Fun) ->
	{error, badarg}.

-spec date1(binary(), fun()) -> any().
date1(<< D1, D2, " ", M:3/binary, " ", Y1, Y2, Y3, Y4, Rest/binary >>, Fun)
		when D1 >= $0, D1 =< $9, D2 >= $0, D2 =< $9,
			 Y1 >= $0, Y1 =< $9, Y2 >= $0, Y2 =< $9,
			 Y3 >= $0, Y3 =< $9, Y4 >= $0, Y4 =< $9 ->
	case month(M) of
		{error, badarg} ->
			{error, badarg};
		Month ->
			Fun(Rest, {
				(Y1 - $0) * 1000 + (Y2 - $0) * 100 + (Y3 - $0) * 10 + (Y4 - $0),
				Month,
				(D1 - $0) * 10 + (D2 - $0)
			})
	end;
date1(_Data, _Fun) ->
	{error, badarg}.

-spec date2(binary(), fun()) -> any().
date2(<< D1, D2, "-", M:3/binary, "-", Y1, Y2, Rest/binary >>, Fun)
		when D1 >= $0, D1 =< $9, D2 >= $0, D2 =< $9,
			 Y1 >= $0, Y1 =< $9, Y2 >= $0, Y2 =< $9 ->
	case month(M) of
		{error, badarg} ->
			{error, badarg};
		Month ->
			Year = (Y1 - $0) * 10 + (Y2 - $0),
			Year2 = case Year > 50 of
				true -> Year + 1900;
				false -> Year + 2000
			end,
			Fun(Rest, {
				Year2,
				Month,
				(D1 - $0) * 10 + (D2 - $0)
			})
	end;
date2(_Data, _Fun) ->
	{error, badarg}.

-spec date3(binary(), fun()) -> any().
date3(<< M:3/binary, " ", D1, D2, Rest/binary >>, Fun)
		when (D1 >= $0 andalso D1 =< $3) orelse D1 =:= $\s,
			 D2 >= $0, D2 =< $9 ->
	case month(M) of
		{error, badarg} ->
			{error, badarg};
		Month ->
			Day = case D1 of
				$\s -> D2 - $0;
				D1 -> (D1 - $0) * 10 + (D2 - $0)
			end,
			Fun(Rest, {Month, Day})
	end;
date3(_Data, _Fun) ->
	{error, badarg}.

-spec month(<< _:24 >>) -> 1..12 | {error, badarg}.
month(<<"Jan">>) -> 1;
month(<<"Feb">>) -> 2;
month(<<"Mar">>) -> 3;
month(<<"Apr">>) -> 4;
month(<<"May">>) -> 5;
month(<<"Jun">>) -> 6;
month(<<"Jul">>) -> 7;
month(<<"Aug">>) -> 8;
month(<<"Sep">>) -> 9;
month(<<"Oct">>) -> 10;
month(<<"Nov">>) -> 11;
month(<<"Dec">>) -> 12;
month(_Any) -> {error, badarg}.

-spec time(binary(), fun()) -> any().
time(<< H1, H2, ":", M1, M2, ":", S1, S2, Rest/binary >>, Fun)
		when H1 >= $0, H1 =< $2, H2 >= $0, H2 =< $9,
			 M1 >= $0, M1 =< $5, M2 >= $0, M2 =< $9,
			 S1 >= $0, S1 =< $5, S2 >= $0, S2 =< $9 ->
	Hour = (H1 - $0) * 10 + (H2 - $0),
	case Hour < 24 of
		true ->
			Time = {
				Hour,
				(M1 - $0) * 10 + (M2 - $0),
				(S1 - $0) * 10 + (S2 - $0)
			},
			Fun(Rest, Time);
		false ->
			{error, badarg}
	end.

-spec whitespace(binary(), fun()) -> any().
whitespace(<< C, Rest/binary >>, Fun)
		when C =:= $\s; C =:= $\t ->
	whitespace(Rest, Fun);
whitespace(Data, Fun) ->
	Fun(Data).

-spec digits(binary()) -> non_neg_integer() | {error, badarg}.
digits(Data) ->
	digits(Data,
		fun (Rest, I) ->
			whitespace(Rest,
				fun (<<>>) ->
						I;
					(_Rest2) ->
						{error, badarg}
				end)
		end).

-spec digits(binary(), fun()) -> any().
digits(<< C, Rest/binary >>, Fun)
		when C >= $0, C =< $9 ->
	digits(Rest, Fun, C - $0);
digits(_Data, _Fun) ->
	{error, badarg}.

-spec digits(binary(), fun(), non_neg_integer()) -> any().
digits(<< C, Rest/binary >>, Fun, Acc)
		when C >= $0, C =< $9 ->
	digits(Rest, Fun, Acc * 10 + (C - $0));
digits(Data, Fun, Acc) ->
	Fun(Data, Acc).

%% Changes all characters to lowercase.
-spec alpha(binary(), fun()) -> any().
alpha(Data, Fun) ->
	alpha(Data, Fun, <<>>).

-spec alpha(binary(), fun(), binary()) -> any().
alpha(<<>>, Fun, Acc) ->
	Fun(<<>>, Acc);
alpha(<< C, Rest/binary >>, Fun, Acc)
		when C >= $a andalso C =< $z;
			 C >= $A andalso C =< $Z ->
	C2 = cowboy_bstr:char_to_lower(C),
	alpha(Rest, Fun, << Acc/binary, C2 >>);
alpha(Data, Fun, Acc) ->
	Fun(Data, Acc).

-spec alphanumeric(binary(), fun()) -> any().
alphanumeric(Data, Fun) ->
	alphanumeric(Data, Fun, <<>>).

-spec alphanumeric(binary(), fun(), binary()) -> any().
alphanumeric(<<>>, Fun, Acc) ->
	Fun(<<>>, Acc);
alphanumeric(<< C, Rest/binary >>, Fun, Acc)
		when C >= $a andalso C =< $z;
			 C >= $A andalso C =< $Z;
			 C >= $0 andalso C =< $9 ->
	C2 = cowboy_bstr:char_to_lower(C),
	alphanumeric(Rest, Fun, << Acc/binary, C2 >>);
alphanumeric(Data, Fun, Acc) ->
	Fun(Data, Acc).

%% @doc Parse either a token or a quoted string.
-spec word(binary(), fun()) -> any().
word(Data = << $", _/binary >>, Fun) ->
	quoted_string(Data, Fun);
word(Data, Fun) ->
	token(Data,
		fun (_Rest, <<>>) -> {error, badarg};
			(Rest, Token) -> Fun(Rest, Token)
		end).

%% Changes all characters to lowercase.
-spec token_ci(binary(), fun()) -> any().
token_ci(Data, Fun) ->
	token(Data, Fun, ci, <<>>).

-spec token(binary(), fun()) -> any().
token(Data, Fun) ->
	token(Data, Fun, cs, <<>>).

-spec token(binary(), fun(), ci | cs, binary()) -> any().
token(<<>>, Fun, _Case, Acc) ->
	Fun(<<>>, Acc);
token(Data = << C, _Rest/binary >>, Fun, _Case, Acc)
		when C =:= $(; C =:= $); C =:= $<; C =:= $>; C =:= $@;
			 C =:= $,; C =:= $;; C =:= $:; C =:= $\\; C =:= $";
			 C =:= $/; C =:= $[; C =:= $]; C =:= $?; C =:= $=;
			 C =:= ${; C =:= $}; C =:= $\s; C =:= $\t;
			 C < 32; C =:= 127 ->
	Fun(Data, Acc);
token(<< C, Rest/binary >>, Fun, Case = ci, Acc) ->
	C2 = cowboy_bstr:char_to_lower(C),
	token(Rest, Fun, Case, << Acc/binary, C2 >>);
token(<< C, Rest/binary >>, Fun, Case, Acc) ->
	token(Rest, Fun, Case, << Acc/binary, C >>).

-spec quoted_string(binary(), fun()) -> any().
quoted_string(<< $", Rest/binary >>, Fun) ->
	quoted_string(Rest, Fun, <<>>);
quoted_string(_, _Fun) ->
    {error, badarg}.

-spec quoted_string(binary(), fun(), binary()) -> any().
quoted_string(<<>>, _Fun, _Acc) ->
	{error, badarg};
quoted_string(<< $", Rest/binary >>, Fun, Acc) ->
	Fun(Rest, Acc);
quoted_string(<< $\\, C, Rest/binary >>, Fun, Acc) ->
	quoted_string(Rest, Fun, << Acc/binary, C >>);
quoted_string(<< C, Rest/binary >>, Fun, Acc) ->
	quoted_string(Rest, Fun, << Acc/binary, C >>).

-spec qvalue(binary(), fun()) -> any().
qvalue(<< $0, $., Rest/binary >>, Fun) ->
	qvalue(Rest, Fun, 0, 100);
%% Some user agents use q=.x instead of q=0.x
qvalue(<< $., Rest/binary >>, Fun) ->
	qvalue(Rest, Fun, 0, 100);
qvalue(<< $0, Rest/binary >>, Fun) ->
	Fun(Rest, 0);
qvalue(<< $1, $., $0, $0, $0, Rest/binary >>, Fun) ->
	Fun(Rest, 1000);
qvalue(<< $1, $., $0, $0, Rest/binary >>, Fun) ->
	Fun(Rest, 1000);
qvalue(<< $1, $., $0, Rest/binary >>, Fun) ->
	Fun(Rest, 1000);
qvalue(<< $1, Rest/binary >>, Fun) ->
	Fun(Rest, 1000);
qvalue(_Data, _Fun) ->
	{error, badarg}.

-spec qvalue(binary(), fun(), integer(), 1 | 10 | 100) -> any().
qvalue(Data, Fun, Q, 0) ->
	Fun(Data, Q);
qvalue(<< C, Rest/binary >>, Fun, Q, M)
		when C >= $0, C =< $9 ->
	qvalue(Rest, Fun, Q + (C - $0) * M, M div 10);
qvalue(Data, Fun, Q, _M) ->
	Fun(Data, Q).

%% Only RFC2617 Basic authorization is supported so far.
-spec authorization(binary(), binary()) -> {binary(), any()} | {error, badarg}.
authorization(UserPass, Type = <<"basic">>) ->
	whitespace(UserPass,
		fun(D) ->
			authorization_basic_userid(base64:mime_decode(D),
				fun(Rest, Userid) ->
					authorization_basic_password(Rest,
						fun(Password) ->
							{Type, {Userid, Password}}
						end)
				end)
		end);
authorization(String, Type) ->
	whitespace(String, fun(Rest) -> {Type, Rest} end).

-spec authorization_basic_userid(binary(), fun()) -> any().
authorization_basic_userid(Data, Fun) ->
	authorization_basic_userid(Data, Fun, <<>>).

authorization_basic_userid(<<>>, _Fun, _Acc) ->
	{error, badarg};
authorization_basic_userid(<<C, _Rest/binary>>, _Fun, Acc)
		when C < 32; C =:= 127; (C =:=$: andalso Acc =:= <<>>) ->
	{error, badarg};
authorization_basic_userid(<<$:, Rest/binary>>, Fun, Acc) ->
	Fun(Rest, Acc);
authorization_basic_userid(<<C, Rest/binary>>, Fun, Acc) ->
	authorization_basic_userid(Rest, Fun, <<Acc/binary, C>>).

-spec authorization_basic_password(binary(), fun()) -> any().
authorization_basic_password(Data, Fun) ->
	authorization_basic_password(Data, Fun, <<>>).

authorization_basic_password(<<C, _Rest/binary>>, _Fun, _Acc)
		when C < 32; C=:= 127 ->
	{error, badarg};
authorization_basic_password(<<>>, Fun, Acc) ->
	Fun(Acc);
authorization_basic_password(<<C, Rest/binary>>, Fun, Acc) ->
	authorization_basic_password(Rest, Fun, <<Acc/binary, C>>).

-spec range(binary()) -> {Unit, [Range]} | {error, badarg} when
		Unit :: binary(),
		Range :: {non_neg_integer(), non_neg_integer() | infinity} | neg_integer().
range(Data) ->
	token_ci(Data, fun range/2).

range(Data, Token) ->
	whitespace(Data,
		fun(<<"=", Rest/binary>>) ->
			case list(Rest, fun range_beginning/2) of
				{error, badarg} ->
					{error, badarg};
				Ranges ->
					{Token, Ranges}
			end;
		   (_) ->
			{error, badarg}
		end).

range_beginning(Data, Fun) ->
	range_digits(Data, suffix,
		fun(D, RangeBeginning) ->
			range_ending(D, Fun, RangeBeginning)
		end).

range_ending(Data, Fun, RangeBeginning) ->
	whitespace(Data,
		fun(<<"-", R/binary>>) ->
			case RangeBeginning of
				suffix ->
					range_digits(R, fun(D, RangeEnding) -> Fun(D, -RangeEnding) end);
				_ ->
					range_digits(R, infinity,
						fun(D, RangeEnding) ->
							Fun(D, {RangeBeginning, RangeEnding})
						end)
			end;
		   (_) ->
			{error, badarg}
		end).

-spec range_digits(binary(), fun()) -> any().
range_digits(Data, Fun) ->
	whitespace(Data,
		fun(D) ->
			digits(D, Fun)
		end).

-spec range_digits(binary(), any(), fun()) -> any().
range_digits(Data, Default, Fun) ->
	whitespace(Data,
		fun(<< C, Rest/binary >>) when C >= $0, C =< $9 ->
			digits(Rest, Fun, C - $0);
		   (_) ->
			Fun(Data, Default)
		end).

-spec parameterized_tokens(binary()) -> any().
parameterized_tokens(Data) ->
	nonempty_list(Data,
		fun (D, Fun) ->
			token(D,
				fun (_Rest, <<>>) -> {error, badarg};
					(Rest, Token) ->
						parameterized_tokens_params(Rest,
							fun (Rest2, Params) ->
								Fun(Rest2, {Token, Params})
							end, [])
				end)
		end).

-spec parameterized_tokens_params(binary(), fun(), [binary() | {binary(), binary()}]) -> any().
parameterized_tokens_params(Data, Fun, Acc) ->
	whitespace(Data,
		fun (<< $;, Rest/binary >>) ->
				parameterized_tokens_param(Rest,
					fun (Rest2, Param) ->
							parameterized_tokens_params(Rest2, Fun, [Param|Acc])
					end);
			(Rest) ->
				Fun(Rest, lists:reverse(Acc))
		end).

-spec parameterized_tokens_param(binary(), fun()) -> any().
parameterized_tokens_param(Data, Fun) ->
	whitespace(Data,
		fun (Rest) ->
				token(Rest,
					fun (_Rest2, <<>>) -> {error, badarg};
						(<< $=, Rest2/binary >>, Attr) ->
							word(Rest2,
								fun (Rest3, Value) ->
										Fun(Rest3, {Attr, Value})
								end);
						(Rest2, Attr) ->
							Fun(Rest2, Attr)
					end)
		end).

%% Decoding.

%% @todo Move this to cowlib too I suppose. :-)
-spec ce_identity(binary()) -> {ok, binary()}.
ce_identity(Data) ->
	{ok, Data}.

%% Tests.

-ifdef(TEST).
nonempty_charset_list_test_() ->
	Tests = [
		{<<>>, {error, badarg}},
		{<<"iso-8859-5, unicode-1-1;q=0.8">>, [
			{<<"iso-8859-5">>, 1000},
			{<<"unicode-1-1">>, 800}
		]},
		%% Some user agents send this invalid value for the Accept-Charset header
		{<<"ISO-8859-1;utf-8;q=0.7,*;q=0.7">>, [
			{<<"iso-8859-1">>, 1000},
			{<<"utf-8">>, 700},
			{<<"*">>, 700}
		]}
	],
	[{V, fun() -> R = nonempty_list(V, fun conneg/2) end} || {V, R} <- Tests].

nonempty_language_range_list_test_() ->
	Tests = [
		{<<"da, en-gb;q=0.8, en;q=0.7">>, [
			{<<"da">>, 1000},
			{<<"en-gb">>, 800},
			{<<"en">>, 700}
		]},
		{<<"en, en-US, en-cockney, i-cherokee, x-pig-latin, es-419">>, [
			{<<"en">>, 1000},
			{<<"en-us">>, 1000},
			{<<"en-cockney">>, 1000},
			{<<"i-cherokee">>, 1000},
			{<<"x-pig-latin">>, 1000},
			{<<"es-419">>, 1000}
		]}
	],
	[{V, fun() -> R = nonempty_list(V, fun language_range/2) end}
		|| {V, R} <- Tests].

nonempty_token_list_test_() ->
	Tests = [
		{<<>>, {error, badarg}},
		{<<" ">>, {error, badarg}},
		{<<" , ">>, {error, badarg}},
		{<<",,,">>, {error, badarg}},
		{<<"a b">>, {error, badarg}},
		{<<"a , , , ">>, [<<"a">>]},
		{<<" , , , a">>, [<<"a">>]},
		{<<"a, , b">>, [<<"a">>, <<"b">>]},
		{<<"close">>, [<<"close">>]},
		{<<"keep-alive, upgrade">>, [<<"keep-alive">>, <<"upgrade">>]}
	],
	[{V, fun() -> R = nonempty_list(V, fun token/2) end} || {V, R} <- Tests].

media_range_list_test_() ->
	Tests = [
		{<<"audio/*; q=0.2, audio/basic">>, [
			{{<<"audio">>, <<"*">>, []}, 200, []},
			{{<<"audio">>, <<"basic">>, []}, 1000, []}
		]},
		{<<"text/plain; q=0.5, text/html, "
		   "text/x-dvi; q=0.8, text/x-c">>, [
		   {{<<"text">>, <<"plain">>, []}, 500, []},
		   {{<<"text">>, <<"html">>, []}, 1000, []},
		   {{<<"text">>, <<"x-dvi">>, []}, 800, []},
		   {{<<"text">>, <<"x-c">>, []}, 1000, []}
		]},
		{<<"text/*, text/html, text/html;level=1, */*">>, [
			{{<<"text">>, <<"*">>, []}, 1000, []},
			{{<<"text">>, <<"html">>, []}, 1000, []},
			{{<<"text">>, <<"html">>, [{<<"level">>, <<"1">>}]}, 1000, []},
			{{<<"*">>, <<"*">>, []}, 1000, []}
		]},
		{<<"text/*;q=0.3, text/html;q=0.7, text/html;level=1, "
		   "text/html;level=2;q=0.4, */*;q=0.5">>, [
		   {{<<"text">>, <<"*">>, []}, 300, []},
		   {{<<"text">>, <<"html">>, []}, 700, []},
		   {{<<"text">>, <<"html">>, [{<<"level">>, <<"1">>}]}, 1000, []},
		   {{<<"text">>, <<"html">>, [{<<"level">>, <<"2">>}]}, 400, []},
		   {{<<"*">>, <<"*">>, []}, 500, []}
		]},
		{<<"text/html;level=1;quoted=\"hi hi hi\";"
		   "q=0.123;standalone;complex=gits, text/plain">>, [
			{{<<"text">>, <<"html">>,
				[{<<"level">>, <<"1">>}, {<<"quoted">>, <<"hi hi hi">>}]}, 123,
				[<<"standalone">>, {<<"complex">>, <<"gits">>}]},
			{{<<"text">>, <<"plain">>, []}, 1000, []}
		]},
		{<<"text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2">>, [
			{{<<"text">>, <<"html">>, []}, 1000, []},
			{{<<"image">>, <<"gif">>, []}, 1000, []},
			{{<<"image">>, <<"jpeg">>, []}, 1000, []},
			{{<<"*">>, <<"*">>, []}, 200, []},
			{{<<"*">>, <<"*">>, []}, 200, []}
		]}
	],
	[{V, fun() -> R = list(V, fun media_range/2) end} || {V, R} <- Tests].

entity_tag_match_test_() ->
	Tests = [
		{<<"\"xyzzy\"">>, [{strong, <<"xyzzy">>}]},
		{<<"\"xyzzy\", W/\"r2d2xxxx\", \"c3piozzzz\"">>,
			[{strong, <<"xyzzy">>},
			 {weak, <<"r2d2xxxx">>},
			 {strong, <<"c3piozzzz">>}]},
		{<<"*">>, '*'}
	],
	[{V, fun() -> R = entity_tag_match(V) end} || {V, R} <- Tests].

http_date_test_() ->
	Tests = [
		{<<"Sun, 06 Nov 1994 08:49:37 GMT">>, {{1994, 11, 6}, {8, 49, 37}}},
		{<<"Sunday, 06-Nov-94 08:49:37 GMT">>, {{1994, 11, 6}, {8, 49, 37}}},
		{<<"Sun Nov  6 08:49:37 1994">>, {{1994, 11, 6}, {8, 49, 37}}}
	],
	[{V, fun() -> R = http_date(V) end} || {V, R} <- Tests].

rfc1123_date_test_() ->
	Tests = [
		{<<"Sun, 06 Nov 1994 08:49:37 GMT">>, {{1994, 11, 6}, {8, 49, 37}}}
	],
	[{V, fun() -> R = rfc1123_date(V) end} || {V, R} <- Tests].

rfc850_date_test_() ->
	Tests = [
		{<<"Sunday, 06-Nov-94 08:49:37 GMT">>, {{1994, 11, 6}, {8, 49, 37}}}
	],
	[{V, fun() -> R = rfc850_date(V) end} || {V, R} <- Tests].

asctime_date_test_() ->
	Tests = [
		{<<"Sun Nov  6 08:49:37 1994">>, {{1994, 11, 6}, {8, 49, 37}}}
	],
	[{V, fun() -> R = asctime_date(V) end} || {V, R} <- Tests].

content_type_test_() ->
	Tests = [
		{<<"text/plain; charset=iso-8859-4">>,
			{<<"text">>, <<"plain">>, [{<<"charset">>, <<"iso-8859-4">>}]}},
		{<<"multipart/form-data  \t;Boundary=\"MultipartIsUgly\"">>,
			{<<"multipart">>, <<"form-data">>, [
				{<<"boundary">>, <<"MultipartIsUgly">>}
			]}},
		{<<"foo/bar; one=FirstParam; two=SecondParam">>,
			{<<"foo">>, <<"bar">>, [
				{<<"one">>, <<"FirstParam">>},
				{<<"two">>, <<"SecondParam">>}
			]}}
	],
	[{V, fun () -> R = content_type(V) end} || {V, R} <- Tests].

parameterized_tokens_test_() ->
	Tests = [
		{<<"foo">>, [{<<"foo">>, []}]},
		{<<"bar; baz=2">>, [{<<"bar">>, [{<<"baz">>, <<"2">>}]}]},
		{<<"bar; baz=2;bat">>, [{<<"bar">>, [{<<"baz">>, <<"2">>}, <<"bat">>]}]},
		{<<"bar; baz=2;bat=\"z=1,2;3\"">>, [{<<"bar">>, [{<<"baz">>, <<"2">>}, {<<"bat">>, <<"z=1,2;3">>}]}]},
		{<<"foo, bar; baz=2">>, [{<<"foo">>, []}, {<<"bar">>, [{<<"baz">>, <<"2">>}]}]}
	],
	[{V, fun () -> R = parameterized_tokens(V) end} || {V, R} <- Tests].

digits_test_() ->
	Tests = [
		{<<"42    ">>, 42},
		{<<"69\t">>, 69},
		{<<"1337">>, 1337}
	],
	[{V, fun() -> R = digits(V) end} || {V, R} <- Tests].

http_authorization_test_() ->
	Tests = [
		{<<"basic">>, <<"QWxsYWRpbjpvcGVuIHNlc2FtZQ==">>,
			{<<"basic">>, {<<"Alladin">>, <<"open sesame">>}}},
		{<<"basic">>, <<"dXNlcm5hbWU6">>,
			{<<"basic">>, {<<"username">>, <<>>}}},
		{<<"basic">>, <<"dXNlcm5hbWUK">>,
			{error, badarg}},
		{<<"basic">>, <<"_[]@#$%^&*()-AA==">>,
			{error, badarg}},
		{<<"basic">>, <<"dXNlcjpwYXNzCA==">>,
			{error, badarg}},
		{<<"bearer">>, <<" some_secret_key">>,
			{<<"bearer">>,<<"some_secret_key">>}}
	],
	[{V, fun() -> R = authorization(V,T) end} || {T, V, R} <- Tests].

http_range_test_() ->
	Tests = [
		{<<"bytes=1-20">>,
			{<<"bytes">>, [{1, 20}]}},
		{<<"bytes=-100">>,
			{<<"bytes">>, [-100]}},
		{<<"bytes=1-">>,
			{<<"bytes">>, [{1, infinity}]}},
		{<<"bytes=1-20,30-40,50-">>,
			{<<"bytes">>, [{1, 20}, {30, 40}, {50, infinity}]}},
		{<<"bytes = 1 - 20 , 50 - , - 300 ">>,
			{<<"bytes">>, [{1, 20}, {50, infinity}, -300]}},
		{<<"bytes=1-20,-500,30-40">>,
			{<<"bytes">>, [{1, 20}, -500, {30, 40}]}},
		{<<"test=1-20,-500,30-40">>,
			{<<"test">>, [{1, 20}, -500, {30, 40}]}},
		{<<"bytes=-">>,
			{error, badarg}},
		{<<"bytes=-30,-">>,
			{error, badarg}}
	],
	[fun() -> R = range(V) end ||{V, R} <- Tests].
-endif.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted deps/cowboy/src/cowboy_http_handler.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
%% Copyright (c) 2011-2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(cowboy_http_handler).

-type opts() :: any().
-type state() :: any().
-type terminate_reason() :: {normal, shutdown}
	| {normal, timeout} %% Only occurs in loop handlers.
	| {error, closed} %% Only occurs in loop handlers.
	| {error, overflow} %% Only occurs in loop handlers.
	| {error, atom()}.

-callback init({atom(), http}, Req, opts())
	-> {ok, Req, state()}
	| {loop, Req, state()}
	| {loop, Req, state(), hibernate}
	| {loop, Req, state(), timeout()}
	| {loop, Req, state(), timeout(), hibernate}
	| {shutdown, Req, state()}
	| {upgrade, protocol, module()}
	| {upgrade, protocol, module(), Req, opts()}
	when Req::cowboy_req:req().
-callback handle(Req, State) -> {ok, Req, State}
	when Req::cowboy_req:req(), State::state().
-callback terminate(terminate_reason(), cowboy_req:req(), state()) -> ok.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<










































































Deleted deps/cowboy/src/cowboy_loop_handler.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
%% Copyright (c) 2011-2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(cowboy_loop_handler).

-type opts() :: any().
-type state() :: any().
-type terminate_reason() :: {normal, shutdown}
	| {normal, timeout}
	| {error, closed}
	| {error, overflow}
	| {error, atom()}.

-callback init({atom(), http}, Req, opts())
	-> {ok, Req, state()}
	| {loop, Req, state()}
	| {loop, Req, state(), hibernate}
	| {loop, Req, state(), timeout()}
	| {loop, Req, state(), timeout(), hibernate}
	| {shutdown, Req, state()}
	| {upgrade, protocol, module()}
	| {upgrade, protocol, module(), Req, opts()}
	when Req::cowboy_req:req().
-callback info(any(), Req, State)
	-> {ok, Req, State}
	| {loop, Req, State}
	| {loop, Req, State, hibernate}
	when Req::cowboy_req:req(), State::state().
-callback terminate(terminate_reason(), cowboy_req:req(), state()) -> ok.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
















































































Deleted deps/cowboy/src/cowboy_middleware.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
%% Copyright (c) 2013-2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(cowboy_middleware).

-type env() :: [{atom(), any()}].
-export_type([env/0]).

-callback execute(Req, Env)
	-> {ok, Req, Env}
	| {suspend, module(), atom(), [any()]}
	| {halt, Req}
	| {error, cowboy:http_status(), Req}
	when Req::cowboy_req:req(), Env::env().
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


















































Deleted deps/cowboy/src/cowboy_protocol.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
%% Copyright (c) 2011-2014, Loïc Hoguin <essen@ninenines.eu>
%% Copyright (c) 2011, Anthony Ramine <nox@dev-extend.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(cowboy_protocol).

%% API.
-export([start_link/4]).

%% Internal.
-export([init/4]).
-export([parse_request/3]).
-export([resume/6]).

-type opts() :: [{compress, boolean()}
	| {env, cowboy_middleware:env()}
	| {max_empty_lines, non_neg_integer()}
	| {max_header_name_length, non_neg_integer()}
	| {max_header_value_length, non_neg_integer()}
	| {max_headers, non_neg_integer()}
	| {max_keepalive, non_neg_integer()}
	| {max_request_line_length, non_neg_integer()}
	| {middlewares, [module()]}
	| {onrequest, cowboy:onrequest_fun()}
	| {onresponse, cowboy:onresponse_fun()}
	| {timeout, timeout()}].
-export_type([opts/0]).

-record(state, {
	socket :: inet:socket(),
	transport :: module(),
	middlewares :: [module()],
	compress :: boolean(),
	env :: cowboy_middleware:env(),
	onrequest :: undefined | cowboy:onrequest_fun(),
	onresponse = undefined :: undefined | cowboy:onresponse_fun(),
	max_empty_lines :: non_neg_integer(),
	req_keepalive = 1 :: non_neg_integer(),
	max_keepalive :: non_neg_integer(),
	max_request_line_length :: non_neg_integer(),
	max_header_name_length :: non_neg_integer(),
	max_header_value_length :: non_neg_integer(),
	max_headers :: non_neg_integer(),
	timeout :: timeout(),
	until :: non_neg_integer() | infinity
}).

-include_lib("cowlib/include/cow_inline.hrl").

%% API.

-spec start_link(ranch:ref(), inet:socket(), module(), opts()) -> {ok, pid()}.
start_link(Ref, Socket, Transport, Opts) ->
	Pid = spawn_link(?MODULE, init, [Ref, Socket, Transport, Opts]),
	{ok, Pid}.

%% Internal.

%% Faster alternative to proplists:get_value/3.
get_value(Key, Opts, Default) ->
	case lists:keyfind(Key, 1, Opts) of
		{_, Value} -> Value;
		_ -> Default
	end.

-spec init(ranch:ref(), inet:socket(), module(), opts()) -> ok.
init(Ref, Socket, Transport, Opts) ->
	Compress = get_value(compress, Opts, false),
	MaxEmptyLines = get_value(max_empty_lines, Opts, 5),
	MaxHeaderNameLength = get_value(max_header_name_length, Opts, 64),
	MaxHeaderValueLength = get_value(max_header_value_length, Opts, 4096),
	MaxHeaders = get_value(max_headers, Opts, 100),
	MaxKeepalive = get_value(max_keepalive, Opts, 100),
	MaxRequestLineLength = get_value(max_request_line_length, Opts, 4096),
	Middlewares = get_value(middlewares, Opts, [cowboy_router, cowboy_handler]),
	Env = [{listener, Ref}|get_value(env, Opts, [])],
	OnRequest = get_value(onrequest, Opts, undefined),
	OnResponse = get_value(onresponse, Opts, undefined),
	Timeout = get_value(timeout, Opts, 5000),
	ok = ranch:accept_ack(Ref),
	wait_request(<<>>, #state{socket=Socket, transport=Transport,
		middlewares=Middlewares, compress=Compress, env=Env,
		max_empty_lines=MaxEmptyLines, max_keepalive=MaxKeepalive,
		max_request_line_length=MaxRequestLineLength,
		max_header_name_length=MaxHeaderNameLength,
		max_header_value_length=MaxHeaderValueLength, max_headers=MaxHeaders,
		onrequest=OnRequest, onresponse=OnResponse,
		timeout=Timeout, until=until(Timeout)}, 0).

-spec until(timeout()) -> non_neg_integer() | infinity.
until(infinity) ->
	infinity;
until(Timeout) ->
	{Me, S, Mi} = os:timestamp(),
	Me * 1000000000 + S * 1000 + Mi div 1000 + Timeout.

%% Request parsing.
%%
%% The next set of functions is the request parsing code. All of it
%% runs using a single binary match context. This optimization ends
%% right after the header parsing is finished and the code becomes
%% more interesting past that point.

-spec recv(inet:socket(), module(), non_neg_integer() | infinity)
	-> {ok, binary()} | {error, closed | timeout | atom()}.
recv(Socket, Transport, infinity) ->
	Transport:recv(Socket, 0, infinity);
recv(Socket, Transport, Until) ->
	{Me, S, Mi} = os:timestamp(),
	Now = Me * 1000000000 + S * 1000 + Mi div 1000,
	Timeout = Until - Now,
	if	Timeout < 0 ->
			{error, timeout};
		true ->
			Transport:recv(Socket, 0, Timeout)
	end.

-spec wait_request(binary(), #state{}, non_neg_integer()) -> ok.
wait_request(Buffer, State=#state{socket=Socket, transport=Transport,
		until=Until}, ReqEmpty) ->
	case recv(Socket, Transport, Until) of
		{ok, Data} ->
			parse_request(<< Buffer/binary, Data/binary >>, State, ReqEmpty);
		{error, _} ->
			terminate(State)
	end.

-spec parse_request(binary(), #state{}, non_neg_integer()) -> ok.
%% Empty lines must be using \r\n.
parse_request(<< $\n, _/binary >>, State, _) ->
	error_terminate(400, State);
%% We limit the length of the Request-line to MaxLength to avoid endlessly
%% reading from the socket and eventually crashing.
parse_request(Buffer, State=#state{max_request_line_length=MaxLength,
		max_empty_lines=MaxEmpty}, ReqEmpty) ->
	case match_eol(Buffer, 0) of
		nomatch when byte_size(Buffer) > MaxLength ->
			error_terminate(414, State);
		nomatch ->
			wait_request(Buffer, State, ReqEmpty);
		1 when ReqEmpty =:= MaxEmpty ->
			error_terminate(400, State);
		1 ->
			<< _:16, Rest/binary >> = Buffer,
			parse_request(Rest, State, ReqEmpty + 1);
		_ ->
			parse_method(Buffer, State, <<>>)
	end.

match_eol(<< $\n, _/bits >>, N) ->
	N;
match_eol(<< _, Rest/bits >>, N) ->
	match_eol(Rest, N + 1);
match_eol(_, _) ->
	nomatch.

parse_method(<< C, Rest/bits >>, State, SoFar) ->
	case C of
		$\r -> error_terminate(400, State);
		$\s -> parse_uri(Rest, State, SoFar);
		_ -> parse_method(Rest, State, << SoFar/binary, C >>)
	end.

parse_uri(<< $\r, _/bits >>, State, _) ->
	error_terminate(400, State);
parse_uri(<< "* ", Rest/bits >>, State, Method) ->
	parse_version(Rest, State, Method, <<"*">>, <<>>);
parse_uri(<< "http://", Rest/bits >>, State, Method) ->
	parse_uri_skip_host(Rest, State, Method);
parse_uri(<< "https://", Rest/bits >>, State, Method) ->
	parse_uri_skip_host(Rest, State, Method);
parse_uri(<< "HTTP://", Rest/bits >>, State, Method) ->
	parse_uri_skip_host(Rest, State, Method);
parse_uri(<< "HTTPS://", Rest/bits >>, State, Method) ->
	parse_uri_skip_host(Rest, State, Method);
parse_uri(Buffer, State, Method) ->
	parse_uri_path(Buffer, State, Method, <<>>).

parse_uri_skip_host(<< C, Rest/bits >>, State, Method) ->
	case C of
		$\r -> error_terminate(400, State);
		$/ -> parse_uri_path(Rest, State, Method, <<"/">>);
		$\s -> parse_version(Rest, State, Method, <<"/">>, <<>>);
		$? -> parse_uri_query(Rest, State, Method, <<"/">>, <<>>);
		$# -> skip_uri_fragment(Rest, State, Method, <<"/">>, <<>>);
		_ -> parse_uri_skip_host(Rest, State, Method)
	end.

parse_uri_path(<< C, Rest/bits >>, State, Method, SoFar) ->
	case C of
		$\r -> error_terminate(400, State);
		$\s -> parse_version(Rest, State, Method, SoFar, <<>>);
		$? -> parse_uri_query(Rest, State, Method, SoFar, <<>>);
		$# -> skip_uri_fragment(Rest, State, Method, SoFar, <<>>);
		_ -> parse_uri_path(Rest, State, Method, << SoFar/binary, C >>)
	end.

parse_uri_query(<< C, Rest/bits >>, S, M, P, SoFar) ->
	case C of
		$\r -> error_terminate(400, S);
		$\s -> parse_version(Rest, S, M, P, SoFar);
		$# -> skip_uri_fragment(Rest, S, M, P, SoFar);
		_ -> parse_uri_query(Rest, S, M, P, << SoFar/binary, C >>)
	end.

skip_uri_fragment(<< C, Rest/bits >>, S, M, P, Q) ->
	case C of
		$\r -> error_terminate(400, S);
		$\s -> parse_version(Rest, S, M, P, Q);
		_ -> skip_uri_fragment(Rest, S, M, P, Q)
	end.

parse_version(<< "HTTP/1.1\r\n", Rest/bits >>, S, M, P, Q) ->
	parse_header(Rest, S, M, P, Q, 'HTTP/1.1', []);
parse_version(<< "HTTP/1.0\r\n", Rest/bits >>, S, M, P, Q) ->
	parse_header(Rest, S, M, P, Q, 'HTTP/1.0', []);
parse_version(_, State, _, _, _) ->
	error_terminate(505, State).

%% Stop receiving data if we have more than allowed number of headers.
wait_header(_, State=#state{max_headers=MaxHeaders}, _, _, _, _, Headers)
		when length(Headers) >= MaxHeaders ->
	error_terminate(400, State);
wait_header(Buffer, State=#state{socket=Socket, transport=Transport,
		until=Until}, M, P, Q, V, H) ->
	case recv(Socket, Transport, Until) of
		{ok, Data} ->
			parse_header(<< Buffer/binary, Data/binary >>,
				State, M, P, Q, V, H);
		{error, timeout} ->
			error_terminate(408, State);
		{error, _} ->
			terminate(State)
	end.

parse_header(<< $\r, $\n, Rest/bits >>, S, M, P, Q, V, Headers) ->
	request(Rest, S, M, P, Q, V, lists:reverse(Headers));
parse_header(Buffer, State=#state{max_header_name_length=MaxLength},
		M, P, Q, V, H) ->
	case match_colon(Buffer, 0) of
		nomatch when byte_size(Buffer) > MaxLength ->
			error_terminate(400, State);
		nomatch ->
			wait_header(Buffer, State, M, P, Q, V, H);
		_ ->
			parse_hd_name(Buffer, State, M, P, Q, V, H, <<>>)
	end.

match_colon(<< $:, _/bits >>, N) ->
	N;
match_colon(<< _, Rest/bits >>, N) ->
	match_colon(Rest, N + 1);
match_colon(_, _) ->
	nomatch.

parse_hd_name(<< C, Rest/bits >>, S, M, P, Q, V, H, SoFar) ->
	case C of
		$: -> parse_hd_before_value(Rest, S, M, P, Q, V, H, SoFar);
		$\s -> parse_hd_name_ws(Rest, S, M, P, Q, V, H, SoFar);
		$\t -> parse_hd_name_ws(Rest, S, M, P, Q, V, H, SoFar);
		?INLINE_LOWERCASE(parse_hd_name, Rest, S, M, P, Q, V, H, SoFar)
	end.

parse_hd_name_ws(<< C, Rest/bits >>, S, M, P, Q, V, H, Name) ->
	case C of
		$\s -> parse_hd_name_ws(Rest, S, M, P, Q, V, H, Name);
		$\t -> parse_hd_name_ws(Rest, S, M, P, Q, V, H, Name);
		$: -> parse_hd_before_value(Rest, S, M, P, Q, V, H, Name)
	end.

wait_hd_before_value(Buffer, State=#state{
		socket=Socket, transport=Transport, until=Until},
		M, P, Q, V, H, N) ->
	case recv(Socket, Transport, Until) of
		{ok, Data} ->
			parse_hd_before_value(<< Buffer/binary, Data/binary >>,
				State, M, P, Q, V, H, N);
		{error, timeout} ->
			error_terminate(408, State);
		{error, _} ->
			terminate(State)
	end.

parse_hd_before_value(<< $\s, Rest/bits >>, S, M, P, Q, V, H, N) ->
	parse_hd_before_value(Rest, S, M, P, Q, V, H, N);
parse_hd_before_value(<< $\t, Rest/bits >>, S, M, P, Q, V, H, N) ->
	parse_hd_before_value(Rest, S, M, P, Q, V, H, N);
parse_hd_before_value(Buffer, State=#state{
		max_header_value_length=MaxLength}, M, P, Q, V, H, N) ->
	case match_eol(Buffer, 0) of
		nomatch when byte_size(Buffer) > MaxLength ->
			error_terminate(400, State);
		nomatch ->
			wait_hd_before_value(Buffer, State, M, P, Q, V, H, N);
		_ ->
			parse_hd_value(Buffer, State, M, P, Q, V, H, N, <<>>)
	end.

%% We completely ignore the first argument which is always
%% the empty binary. We keep it there because we don't want
%% to change the other arguments' position and trigger costy
%% operations for no reasons.
wait_hd_value(_, State=#state{
		socket=Socket, transport=Transport, until=Until},
		M, P, Q, V, H, N, SoFar) ->
	case recv(Socket, Transport, Until) of
		{ok, Data} ->
			parse_hd_value(Data, State, M, P, Q, V, H, N, SoFar);
		{error, timeout} ->
			error_terminate(408, State);
		{error, _} ->
			terminate(State)
	end.

%% Pushing back as much as we could the retrieval of new data
%% to check for multilines allows us to avoid a few tests in
%% the critical path, but forces us to have a special function.
wait_hd_value_nl(_, State=#state{
		socket=Socket, transport=Transport, until=Until},
		M, P, Q, V, Headers, Name, SoFar) ->
	case recv(Socket, Transport, Until) of
		{ok, << C, Data/bits >>} when C =:= $\s; C =:= $\t  ->
			parse_hd_value(Data, State, M, P, Q, V, Headers, Name, SoFar);
		{ok, Data} ->
			parse_header(Data, State, M, P, Q, V, [{Name, SoFar}|Headers]);
		{error, timeout} ->
			error_terminate(408, State);
		{error, _} ->
			terminate(State)
	end.

parse_hd_value(<< $\r, Rest/bits >>, S, M, P, Q, V, Headers, Name, SoFar) ->
	case Rest of
		<< $\n >> ->
			wait_hd_value_nl(<<>>, S, M, P, Q, V, Headers, Name, SoFar);
		<< $\n, C, Rest2/bits >> when C =:= $\s; C =:= $\t ->
			parse_hd_value(Rest2, S, M, P, Q, V, Headers, Name,
				<< SoFar/binary, C >>);
		<< $\n, Rest2/bits >> ->
			parse_header(Rest2, S, M, P, Q, V, [{Name, SoFar}|Headers])
	end;
parse_hd_value(<< C, Rest/bits >>, S, M, P, Q, V, H, N, SoFar) ->
	parse_hd_value(Rest, S, M, P, Q, V, H, N, << SoFar/binary, C >>);
parse_hd_value(<<>>, State=#state{max_header_value_length=MaxLength},
		_, _, _, _, _, _, SoFar) when byte_size(SoFar) > MaxLength ->
	error_terminate(400, State);
parse_hd_value(<<>>, S, M, P, Q, V, H, N, SoFar) ->
	wait_hd_value(<<>>, S, M, P, Q, V, H, N, SoFar).

request(B, State=#state{transport=Transport}, M, P, Q, Version, Headers) ->
	case lists:keyfind(<<"host">>, 1, Headers) of
		false when Version =:= 'HTTP/1.1' ->
			error_terminate(400, State);
		false ->
			request(B, State, M, P, Q, Version, Headers,
				<<>>, default_port(Transport:name()));
		{_, RawHost} ->
			try parse_host(RawHost, false, <<>>) of
				{Host, undefined} ->
					request(B, State, M, P, Q, Version, Headers,
						Host, default_port(Transport:name()));
				{Host, Port} ->
					request(B, State, M, P, Q, Version, Headers,
						Host, Port)
			catch _:_ ->
				error_terminate(400, State)
			end
	end.

-spec default_port(atom()) -> 80 | 443.
default_port(ssl) -> 443;
default_port(_) -> 80.

%% Same code as cow_http:parse_fullhost/1, but inline because we
%% really want this to go fast.
parse_host(<< $[, Rest/bits >>, false, <<>>) ->
	parse_host(Rest, true, << $[ >>);
parse_host(<<>>, false, Acc) ->
	{Acc, undefined};
parse_host(<< $:, Rest/bits >>, false, Acc) ->
	{Acc, list_to_integer(binary_to_list(Rest))};
parse_host(<< $], Rest/bits >>, true, Acc) ->
	parse_host(Rest, false, << Acc/binary, $] >>);
parse_host(<< C, Rest/bits >>, E, Acc) ->
	case C of
		?INLINE_LOWERCASE(parse_host, Rest, E, Acc)
	end.

%% End of request parsing.
%%
%% We create the Req object and start handling the request.

request(Buffer, State=#state{socket=Socket, transport=Transport,
		req_keepalive=ReqKeepalive, max_keepalive=MaxKeepalive,
		compress=Compress, onresponse=OnResponse},
		Method, Path, Query, Version, Headers, Host, Port) ->
	case Transport:peername(Socket) of
		{ok, Peer} ->
			Req = cowboy_req:new(Socket, Transport, Peer, Method, Path,
				Query, Version, Headers, Host, Port, Buffer,
				ReqKeepalive < MaxKeepalive, Compress, OnResponse),
			onrequest(Req, State);
		{error, _} ->
			%% Couldn't read the peer address; connection is gone.
			terminate(State)
	end.

%% Call the global onrequest callback. The callback can send a reply,
%% in which case we consider the request handled and move on to the next
%% one. Note that since we haven't dispatched yet, we don't know the
%% handler, host_info, path_info or bindings yet.
-spec onrequest(cowboy_req:req(), #state{}) -> ok.
onrequest(Req, State=#state{onrequest=undefined}) ->
	execute(Req, State);
onrequest(Req, State=#state{onrequest=OnRequest}) ->
	Req2 = OnRequest(Req),
	case cowboy_req:get(resp_state, Req2) of
		waiting -> execute(Req2, State);
		_ -> next_request(Req2, State, ok)
	end.

-spec execute(cowboy_req:req(), #state{}) -> ok.
execute(Req, State=#state{middlewares=Middlewares, env=Env}) ->
	execute(Req, State, Env, Middlewares).

-spec execute(cowboy_req:req(), #state{}, cowboy_middleware:env(), [module()])
	-> ok.
execute(Req, State, Env, []) ->
	next_request(Req, State, get_value(result, Env, ok));
execute(Req, State, Env, [Middleware|Tail]) ->
	case Middleware:execute(Req, Env) of
		{ok, Req2, Env2} ->
			execute(Req2, State, Env2, Tail);
		{suspend, Module, Function, Args} ->
			erlang:hibernate(?MODULE, resume,
				[State, Env, Tail, Module, Function, Args]);
		{halt, Req2} ->
			next_request(Req2, State, ok);
		{error, Code, Req2} ->
			error_terminate(Code, Req2, State)
	end.

-spec resume(#state{}, cowboy_middleware:env(), [module()],
	module(), module(), [any()]) -> ok.
resume(State, Env, Tail, Module, Function, Args) ->
	case apply(Module, Function, Args) of
		{ok, Req2, Env2} ->
			execute(Req2, State, Env2, Tail);
		{suspend, Module2, Function2, Args2} ->
			erlang:hibernate(?MODULE, resume,
				[State, Env, Tail, Module2, Function2, Args2]);
		{halt, Req2} ->
			next_request(Req2, State, ok);
		{error, Code, Req2} ->
			error_terminate(Code, Req2, State)
	end.

-spec next_request(cowboy_req:req(), #state{}, any()) -> ok.
next_request(Req, State=#state{req_keepalive=Keepalive, timeout=Timeout},
		HandlerRes) ->
	cowboy_req:ensure_response(Req, 204),
	%% If we are going to close the connection,
	%% we do not want to attempt to skip the body.
	case cowboy_req:get(connection, Req) of
		close ->
			terminate(State);
		_ ->
			%% Skip the body if it is reasonably sized. Close otherwise.
			Buffer = case cowboy_req:body(Req) of
				{ok, _, Req2} -> cowboy_req:get(buffer, Req2);
				_ -> close
			end,
			%% Flush the resp_sent message before moving on.
			if HandlerRes =:= ok, Buffer =/= close ->
					receive {cowboy_req, resp_sent} -> ok after 0 -> ok end,
					?MODULE:parse_request(Buffer,
						State#state{req_keepalive=Keepalive + 1,
						until=until(Timeout)}, 0);
				true ->
					terminate(State)
			end
	end.

-spec error_terminate(cowboy:http_status(), #state{}) -> ok.
error_terminate(Status, State=#state{socket=Socket, transport=Transport,
		compress=Compress, onresponse=OnResponse}) ->
	error_terminate(Status, cowboy_req:new(Socket, Transport,
		undefined, <<"GET">>, <<>>, <<>>, 'HTTP/1.1', [], <<>>,
		undefined, <<>>, false, Compress, OnResponse), State).

-spec error_terminate(cowboy:http_status(), cowboy_req:req(), #state{}) -> ok.
error_terminate(Status, Req, State) ->
	_ = cowboy_req:reply(Status, Req),
	terminate(State).

-spec terminate(#state{}) -> ok.
terminate(#state{socket=Socket, transport=Transport}) ->
	Transport:close(Socket),
	ok.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted deps/cowboy/src/cowboy_req.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
%% Copyright (c) 2011-2014, Loïc Hoguin <essen@ninenines.eu>
%% Copyright (c) 2011, Anthony Ramine <nox@dev-extend.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(cowboy_req).

%% Request API.
-export([new/14]).
-export([method/1]).
-export([version/1]).
-export([peer/1]).
-export([host/1]).
-export([host_info/1]).
-export([port/1]).
-export([path/1]).
-export([path_info/1]).
-export([qs/1]).
-export([qs_val/2]).
-export([qs_val/3]).
-export([qs_vals/1]).
-export([host_url/1]).
-export([url/1]).
-export([binding/2]).
-export([binding/3]).
-export([bindings/1]).
-export([header/2]).
-export([header/3]).
-export([headers/1]).
-export([parse_header/2]).
-export([parse_header/3]).
-export([cookie/2]).
-export([cookie/3]).
-export([cookies/1]).
-export([meta/2]).
-export([meta/3]).
-export([set_meta/3]).

%% Request body API.
-export([has_body/1]).
-export([body_length/1]).
-export([body/1]).
-export([body/2]).
-export([body_qs/1]).
-export([body_qs/2]).

%% Multipart API.
-export([part/1]).
-export([part/2]).
-export([part_body/1]).
-export([part_body/2]).

%% Response API.
-export([set_resp_cookie/4]).
-export([set_resp_header/3]).
-export([set_resp_body/2]).
-export([set_resp_body_fun/2]).
-export([set_resp_body_fun/3]).
-export([has_resp_header/2]).
-export([has_resp_body/1]).
-export([delete_resp_header/2]).
-export([reply/2]).
-export([reply/3]).
-export([reply/4]).
-export([chunked_reply/2]).
-export([chunked_reply/3]).
-export([chunk/2]).
-export([upgrade_reply/3]).
-export([continue/1]).
-export([maybe_reply/2]).
-export([ensure_response/2]).

%% Private setter/getter API.
-export([append_buffer/2]).
-export([get/2]).
-export([set/2]).
-export([set_bindings/4]).

%% Misc API.
-export([compact/1]).
-export([lock/1]).
-export([to_list/1]).

-type cookie_opts() :: cow_cookie:cookie_opts().
-export_type([cookie_opts/0]).

-type content_decode_fun() :: fun((binary())
	-> {ok, binary()}
	| {error, atom()}).
-type transfer_decode_fun() :: fun((binary(), any())
	-> cow_http_te:decode_ret()).

-type body_opts() :: [{continue, boolean()}
	| {length, non_neg_integer()}
	| {read_length, non_neg_integer()}
	| {read_timeout, timeout()}
	| {transfer_decode, transfer_decode_fun(), any()}
	| {content_decode, content_decode_fun()}].
-export_type([body_opts/0]).

-type resp_body_fun() :: fun((any(), module()) -> ok).
-type send_chunk_fun() :: fun((iodata()) -> ok | {error, atom()}).
-type resp_chunked_fun() :: fun((send_chunk_fun()) -> ok).

-record(http_req, {
	%% Transport.
	socket = undefined :: any(),
	transport = undefined :: undefined | module(),
	connection = keepalive :: keepalive | close,

	%% Request.
	pid = undefined :: pid(),
	method = <<"GET">> :: binary(),
	version = 'HTTP/1.1' :: cowboy:http_version(),
	peer = undefined :: undefined | {inet:ip_address(), inet:port_number()},
	host = undefined :: undefined | binary(),
	host_info = undefined :: undefined | cowboy_router:tokens(),
	port = undefined :: undefined | inet:port_number(),
	path = undefined :: binary(),
	path_info = undefined :: undefined | cowboy_router:tokens(),
	qs = undefined :: binary(),
	qs_vals = undefined :: undefined | list({binary(), binary() | true}),
	bindings = undefined :: undefined | cowboy_router:bindings(),
	headers = [] :: cowboy:http_headers(),
	p_headers = [] :: [any()],
	cookies = undefined :: undefined | [{binary(), binary()}],
	meta = [] :: [{atom(), any()}],

	%% Request body.
	body_state = waiting :: waiting | done | {stream, non_neg_integer(),
		transfer_decode_fun(), any(), content_decode_fun()},
	buffer = <<>> :: binary(),
	multipart = undefined :: undefined | {binary(), binary()},

	%% Response.
	resp_compress = false :: boolean(),
	resp_state = waiting :: locked | waiting | waiting_stream
		| chunks | stream | done,
	resp_headers = [] :: cowboy:http_headers(),
	resp_body = <<>> :: iodata() | resp_body_fun()
		| {non_neg_integer(), resp_body_fun()}
		| {chunked, resp_chunked_fun()},

	%% Functions.
	onresponse = undefined :: undefined | already_called
		| cowboy:onresponse_fun()
}).

-opaque req() :: #http_req{}.
-export_type([req/0]).

%% Request API.

-spec new(any(), module(),
	undefined | {inet:ip_address(), inet:port_number()},
	binary(), binary(), binary(),
	cowboy:http_version(), cowboy:http_headers(), binary(),
	inet:port_number() | undefined, binary(), boolean(), boolean(),
	undefined | cowboy:onresponse_fun())
	-> req().
new(Socket, Transport, Peer, Method, Path, Query,
		Version, Headers, Host, Port, Buffer, CanKeepalive,
		Compress, OnResponse) ->
	Req = #http_req{socket=Socket, transport=Transport, pid=self(), peer=Peer,
		method=Method, path=Path, qs=Query, version=Version,
		headers=Headers, host=Host, port=Port, buffer=Buffer,
		resp_compress=Compress, onresponse=OnResponse},
	case CanKeepalive of
		false ->
			Req#http_req{connection=close};
		true ->
			case lists:keyfind(<<"connection">>, 1, Headers) of
				false ->
					case Version of
						'HTTP/1.1' -> Req; %% keepalive
						'HTTP/1.0' -> Req#http_req{connection=close}
					end;
				{_, ConnectionHeader} ->
					Tokens = cow_http_hd:parse_connection(ConnectionHeader),
					Connection = connection_to_atom(Tokens),
					Req#http_req{connection=Connection,
						p_headers=[{<<"connection">>, Tokens}]}
			end
	end.

-spec method(Req) -> {binary(), Req} when Req::req().
method(Req) ->
	{Req#http_req.method, Req}.

-spec version(Req) -> {cowboy:http_version(), Req} when Req::req().
version(Req) ->
	{Req#http_req.version, Req}.

-spec peer(Req)
	-> {{inet:ip_address(), inet:port_number()}, Req}
	when Req::req().
peer(Req) ->
	{Req#http_req.peer, Req}.

-spec host(Req) -> {binary(), Req} when Req::req().
host(Req) ->
	{Req#http_req.host, Req}.

-spec host_info(Req)
	-> {cowboy_router:tokens() | undefined, Req} when Req::req().
host_info(Req) ->
	{Req#http_req.host_info, Req}.

-spec port(Req) -> {inet:port_number(), Req} when Req::req().
port(Req) ->
	{Req#http_req.port, Req}.

-spec path(Req) -> {binary(), Req} when Req::req().
path(Req) ->
	{Req#http_req.path, Req}.

-spec path_info(Req)
	-> {cowboy_router:tokens() | undefined, Req} when Req::req().
path_info(Req) ->
	{Req#http_req.path_info, Req}.

-spec qs(Req) -> {binary(), Req} when Req::req().
qs(Req) ->
	{Req#http_req.qs, Req}.

-spec qs_val(binary(), Req)
	-> {binary() | true | undefined, Req} when Req::req().
qs_val(Name, Req) when is_binary(Name) ->
	qs_val(Name, Req, undefined).

-spec qs_val(binary(), Req, Default)
	-> {binary() | true | Default, Req} when Req::req(), Default::any().
qs_val(Name, Req=#http_req{qs=RawQs, qs_vals=undefined}, Default)
		when is_binary(Name) ->
	QsVals = cow_qs:parse_qs(RawQs),
	qs_val(Name, Req#http_req{qs_vals=QsVals}, Default);
qs_val(Name, Req, Default) ->
	case lists:keyfind(Name, 1, Req#http_req.qs_vals) of
		{Name, Value} -> {Value, Req};
		false -> {Default, Req}
	end.

-spec qs_vals(Req) -> {list({binary(), binary() | true}), Req} when Req::req().
qs_vals(Req=#http_req{qs=RawQs, qs_vals=undefined}) ->
	QsVals = cow_qs:parse_qs(RawQs),
	qs_vals(Req#http_req{qs_vals=QsVals});
qs_vals(Req=#http_req{qs_vals=QsVals}) ->
	{QsVals, Req}.

%% The URL includes the scheme, host and port only.
-spec host_url(Req) -> {undefined | binary(), Req} when Req::req().
host_url(Req=#http_req{port=undefined}) ->
	{undefined, Req};
host_url(Req=#http_req{transport=Transport, host=Host, port=Port}) ->
	TransportName = Transport:name(),
	Secure = case TransportName of
		ssl -> <<"s">>;
		_ -> <<>>
	end,
	PortBin = case {TransportName, Port} of
		{ssl, 443} -> <<>>;
		{tcp, 80} -> <<>>;
		_ -> << ":", (integer_to_binary(Port))/binary >>
	end,
	{<< "http", Secure/binary, "://", Host/binary, PortBin/binary >>, Req}.

%% The URL includes the scheme, host, port, path and query string.
-spec url(Req) -> {undefined | binary(), Req} when Req::req().
url(Req=#http_req{}) ->
	{HostURL, Req2} = host_url(Req),
	url(HostURL, Req2).

url(undefined, Req=#http_req{}) ->
	{undefined, Req};
url(HostURL, Req=#http_req{path=Path, qs=QS}) ->
	QS2 = case QS of
		<<>> -> <<>>;
		_ -> << "?", QS/binary >>
	end,
	{<< HostURL/binary, Path/binary, QS2/binary >>, Req}.

-spec binding(atom(), Req) -> {any() | undefined, Req} when Req::req().
binding(Name, Req) when is_atom(Name) ->
	binding(Name, Req, undefined).

-spec binding(atom(), Req, Default)
	-> {any() | Default, Req} when Req::req(), Default::any().
binding(Name, Req, Default) when is_atom(Name) ->
	case lists:keyfind(Name, 1, Req#http_req.bindings) of
		{Name, Value} -> {Value, Req};
		false -> {Default, Req}
	end.

-spec bindings(Req) -> {[{atom(), any()}], Req} when Req::req().
bindings(Req) ->
	{Req#http_req.bindings, Req}.

-spec header(binary(), Req)
	-> {binary() | undefined, Req} when Req::req().
header(Name, Req) ->
	header(Name, Req, undefined).

-spec header(binary(), Req, Default)
	-> {binary() | Default, Req} when Req::req(), Default::any().
header(Name, Req, Default) ->
	case lists:keyfind(Name, 1, Req#http_req.headers) of
		{Name, Value} -> {Value, Req};
		false -> {Default, Req}
	end.

-spec headers(Req) -> {cowboy:http_headers(), Req} when Req::req().
headers(Req) ->
	{Req#http_req.headers, Req}.

-spec parse_header(binary(), Req)
	-> {ok, any(), Req} | {undefined, binary(), Req}
	| {error, badarg} when Req::req().
parse_header(Name, Req=#http_req{p_headers=PHeaders}) ->
	case lists:keyfind(Name, 1, PHeaders) of
		false -> parse_header(Name, Req, parse_header_default(Name));
		{Name, Value} -> {ok, Value, Req}
	end.

-spec parse_header_default(binary()) -> any().
parse_header_default(<<"transfer-encoding">>) -> [<<"identity">>];
parse_header_default(_Name) -> undefined.

-spec parse_header(binary(), Req, any())
	-> {ok, any(), Req} | {undefined, binary(), Req}
	| {error, badarg} when Req::req().
parse_header(Name = <<"accept">>, Req, Default) ->
	parse_header(Name, Req, Default,
		fun (Value) ->
			cowboy_http:list(Value, fun cowboy_http:media_range/2)
		end);
parse_header(Name = <<"accept-charset">>, Req, Default) ->
	parse_header(Name, Req, Default,
		fun (Value) ->
			cowboy_http:nonempty_list(Value, fun cowboy_http:conneg/2)
		end);
parse_header(Name = <<"accept-encoding">>, Req, Default) ->
	parse_header(Name, Req, Default,
		fun (Value) ->
			cowboy_http:list(Value, fun cowboy_http:conneg/2)
		end);
parse_header(Name = <<"accept-language">>, Req, Default) ->
	parse_header(Name, Req, Default,
		fun (Value) ->
			cowboy_http:nonempty_list(Value, fun cowboy_http:language_range/2)
		end);
parse_header(Name = <<"authorization">>, Req, Default) ->
	parse_header(Name, Req, Default,
		fun (Value) ->
			cowboy_http:token_ci(Value, fun cowboy_http:authorization/2)
		end);
parse_header(Name = <<"content-length">>, Req, Default) ->
	parse_header(Name, Req, Default, fun cow_http_hd:parse_content_length/1);
parse_header(Name = <<"content-type">>, Req, Default) ->
	parse_header(Name, Req, Default, fun cowboy_http:content_type/1);
parse_header(Name = <<"cookie">>, Req, Default) ->
	parse_header(Name, Req, Default, fun cow_cookie:parse_cookie/1);
parse_header(Name = <<"expect">>, Req, Default) ->
	parse_header(Name, Req, Default,
		fun (Value) ->
			cowboy_http:nonempty_list(Value, fun cowboy_http:expectation/2)
		end);
parse_header(Name, Req, Default)
		when Name =:= <<"if-match">>;
			Name =:= <<"if-none-match">> ->
	parse_header(Name, Req, Default, fun cowboy_http:entity_tag_match/1);
parse_header(Name, Req, Default)
		when Name =:= <<"if-modified-since">>;
			Name =:= <<"if-unmodified-since">> ->
	parse_header(Name, Req, Default, fun cowboy_http:http_date/1);
parse_header(Name = <<"range">>, Req, Default) ->
	parse_header(Name, Req, Default, fun cowboy_http:range/1);
parse_header(Name, Req, Default)
		when Name =:= <<"sec-websocket-protocol">>;
			Name =:= <<"x-forwarded-for">> ->
	parse_header(Name, Req, Default,
		fun (Value) ->
			cowboy_http:nonempty_list(Value, fun cowboy_http:token/2)
		end);
parse_header(Name = <<"transfer-encoding">>, Req, Default) ->
	parse_header(Name, Req, Default, fun cow_http_hd:parse_transfer_encoding/1);
%% @todo Product version.
parse_header(Name = <<"upgrade">>, Req, Default) ->
	parse_header(Name, Req, Default,
		fun (Value) ->
			cowboy_http:nonempty_list(Value, fun cowboy_http:token_ci/2)
		end);
parse_header(Name = <<"sec-websocket-extensions">>, Req, Default) ->
	parse_header(Name, Req, Default, fun cowboy_http:parameterized_tokens/1);
parse_header(Name, Req, Default) ->
	{Value, Req2} = header(Name, Req, Default),
	{undefined, Value, Req2}.

parse_header(Name, Req=#http_req{p_headers=PHeaders}, Default, Fun) ->
	case header(Name, Req) of
		{undefined, Req2} ->
			{ok, Default, Req2#http_req{p_headers=[{Name, Default}|PHeaders]}};
		{Value, Req2} ->
			case Fun(Value) of
				{error, badarg} ->
					{error, badarg};
				P ->
					{ok, P, Req2#http_req{p_headers=[{Name, P}|PHeaders]}}
			end
	end.

-spec cookie(binary(), Req)
	-> {binary() | undefined, Req} when Req::req().
cookie(Name, Req) when is_binary(Name) ->
	cookie(Name, Req, undefined).

-spec cookie(binary(), Req, Default)
	-> {binary() | Default, Req} when Req::req(), Default::any().
cookie(Name, Req=#http_req{cookies=undefined}, Default) when is_binary(Name) ->
	case parse_header(<<"cookie">>, Req) of
		{ok, undefined, Req2} ->
			{Default, Req2#http_req{cookies=[]}};
		{ok, Cookies, Req2} ->
			cookie(Name, Req2#http_req{cookies=Cookies}, Default)
	end;
cookie(Name, Req, Default) ->
	case lists:keyfind(Name, 1, Req#http_req.cookies) of
		{Name, Value} -> {Value, Req};
		false -> {Default, Req}
	end.

-spec cookies(Req) -> {list({binary(), binary()}), Req} when Req::req().
cookies(Req=#http_req{cookies=undefined}) ->
	case parse_header(<<"cookie">>, Req) of
		{ok, undefined, Req2} ->
			{[], Req2#http_req{cookies=[]}};
		{ok, Cookies, Req2} ->
			cookies(Req2#http_req{cookies=Cookies});
		%% Flash player incorrectly sends an empty Cookie header.
		{error, badarg} ->
			{[], Req#http_req{cookies=[]}}
	end;
cookies(Req=#http_req{cookies=Cookies}) ->
	{Cookies, Req}.

-spec meta(atom(), Req) -> {any() | undefined, Req} when Req::req().
meta(Name, Req) ->
	meta(Name, Req, undefined).

-spec meta(atom(), Req, any()) -> {any(), Req} when Req::req().
meta(Name, Req, Default) ->
	case lists:keyfind(Name, 1, Req#http_req.meta) of
		{Name, Value} -> {Value, Req};
		false -> {Default, Req}
	end.

-spec set_meta(atom(), any(), Req) -> Req when Req::req().
set_meta(Name, Value, Req=#http_req{meta=Meta}) ->
	Req#http_req{meta=lists:keystore(Name, 1, Meta, {Name, Value})}.

%% Request Body API.

-spec has_body(req()) -> boolean().
has_body(Req) ->
	case lists:keyfind(<<"content-length">>, 1, Req#http_req.headers) of
		{_, <<"0">>} ->
			false;
		{_, _} ->
			true;
		_ ->
			lists:keymember(<<"transfer-encoding">>, 1, Req#http_req.headers)
	end.

%% The length may not be known if Transfer-Encoding is not identity,
%% and the body hasn't been read at the time of the call.
-spec body_length(Req) -> {undefined | non_neg_integer(), Req} when Req::req().
body_length(Req) ->
	case parse_header(<<"transfer-encoding">>, Req) of
		{ok, [<<"identity">>], Req2} ->
			{ok, Length, Req3} = parse_header(<<"content-length">>, Req2, 0),
			{Length, Req3};
		{ok, _, Req2} ->
			{undefined, Req2}
	end.

-spec body(Req)
	-> {ok, binary(), Req} | {more, binary(), Req}
	| {error, atom()} when Req::req().
body(Req) ->
	body(Req, []).

-spec body(Req, body_opts())
	-> {ok, binary(), Req} | {more, binary(), Req}
	| {error, atom()} when Req::req().
body(Req=#http_req{body_state=waiting}, Opts) ->
	%% Send a 100 continue if needed (enabled by default).
	Req1 = case lists:keyfind(continue, 1, Opts) of
		{_, false} ->
			Req;
		_ ->
			{ok, ExpectHeader, Req0} = parse_header(<<"expect">>, Req),
			ok = case ExpectHeader of
				[<<"100-continue">>] -> continue(Req0);
				_ -> ok
			end,
			Req0
	end,
	%% Initialize body streaming state.
	CFun = case lists:keyfind(content_decode, 1, Opts) of
		false ->
			fun cowboy_http:ce_identity/1;
		{_, CFun0} ->
			CFun0
	end,
	case lists:keyfind(transfer_decode, 1, Opts) of
		false ->
			case parse_header(<<"transfer-encoding">>, Req1) of
				{ok, [<<"chunked">>], Req2} ->
					body(Req2#http_req{body_state={stream, 0,
						fun cow_http_te:stream_chunked/2, {0, 0}, CFun}}, Opts);
				{ok, [<<"identity">>], Req2} ->
					{Len, Req3} = body_length(Req2),
					case Len of
						0 ->
							{ok, <<>>, Req3#http_req{body_state=done}};
						_ ->
							body(Req3#http_req{body_state={stream, Len,
								fun cow_http_te:stream_identity/2, {0, Len},
								CFun}}, Opts)
					end
			end;
		{_, TFun, TState} ->
			body(Req1#http_req{body_state={stream, 0,
				TFun, TState, CFun}}, Opts)
	end;
body(Req=#http_req{body_state=done}, _) ->
	{ok, <<>>, Req};
body(Req, Opts) ->
	ChunkLen = case lists:keyfind(length, 1, Opts) of
		false -> 8000000;
		{_, ChunkLen0} -> ChunkLen0
	end,
	ReadLen = case lists:keyfind(read_length, 1, Opts) of
		false -> 1000000;
		{_, ReadLen0} -> ReadLen0
	end,
	ReadTimeout = case lists:keyfind(read_timeout, 1, Opts) of
		false -> 15000;
		{_, ReadTimeout0} -> ReadTimeout0
	end,
	body_loop(Req, ReadTimeout, ReadLen, ChunkLen, <<>>).

body_loop(Req=#http_req{buffer=Buffer, body_state={stream, Length, _, _, _}},
		ReadTimeout, ReadLength, ChunkLength, Acc) ->
	{Tag, Res, Req2} = case Buffer of
		<<>> ->
			body_recv(Req, ReadTimeout, min(Length, ReadLength));
		_ ->
			body_decode(Req, ReadTimeout)
	end,
	case {Tag, Res} of
		{ok, {ok, Data}} ->
			{ok, << Acc/binary, Data/binary >>, Req2};
		{more, {ok, Data}} ->
			Acc2 = << Acc/binary, Data/binary >>,
			case byte_size(Acc2) >= ChunkLength of
				true -> {more, Acc2, Req2};
				false -> body_loop(Req2, ReadTimeout, ReadLength, ChunkLength, Acc2)
			end;
		_ -> %% Error.
			Res
	end.

body_recv(Req=#http_req{transport=Transport, socket=Socket, buffer=Buffer},
		ReadTimeout, ReadLength) ->
	case Transport:recv(Socket, ReadLength, ReadTimeout) of
		{ok, Data} ->
			body_decode(Req#http_req{buffer= << Buffer/binary, Data/binary >>},
				ReadTimeout);
		Error = {error, _} ->
			{error, Error, Req}
	end.

%% Two decodings happen. First a decoding function is applied to the
%% transferred data, and then another is applied to the actual content.
%%
%% Transfer encoding is generally used for chunked bodies. The decoding
%% function uses a state to keep track of how much it has read, which is
%% also initialized through this function.
%%
%% Content encoding is generally used for compression.
%%
%% @todo Handle chunked after-the-facts headers.
%% @todo Depending on the length returned we might want to 0 or +5 it.
body_decode(Req=#http_req{buffer=Data, body_state={stream, _,
		TDecode, TState, CDecode}}, ReadTimeout) ->
	case TDecode(Data, TState) of
		more ->
			body_recv(Req#http_req{body_state={stream, 0,
				TDecode, TState, CDecode}}, ReadTimeout, 0);
		{more, Data2, TState2} ->
			{more, CDecode(Data2), Req#http_req{body_state={stream, 0,
				TDecode, TState2, CDecode}, buffer= <<>>}};
		{more, Data2, Length, TState2} when is_integer(Length) ->
			{more, CDecode(Data2), Req#http_req{body_state={stream, Length,
				TDecode, TState2, CDecode}, buffer= <<>>}};
		{more, Data2, Rest, TState2} ->
			{more, CDecode(Data2), Req#http_req{body_state={stream, 0,
				TDecode, TState2, CDecode}, buffer=Rest}};
		{done, TotalLength, Rest} ->
			{ok, {ok, <<>>}, body_decode_end(Req, TotalLength, Rest)};
		{done, Data2, TotalLength, Rest} ->
			{ok, CDecode(Data2), body_decode_end(Req, TotalLength, Rest)}
	end.

body_decode_end(Req=#http_req{headers=Headers, p_headers=PHeaders},
		TotalLength, Rest) ->
	Headers2 = lists:keystore(<<"content-length">>, 1, Headers,
		{<<"content-length">>, integer_to_binary(TotalLength)}),
	%% At this point we just assume TEs were all decoded.
	Headers3 = lists:keydelete(<<"transfer-encoding">>, 1, Headers2),
	PHeaders2 = lists:keystore(<<"content-length">>, 1, PHeaders,
		{<<"content-length">>, TotalLength}),
	PHeaders3 = lists:keydelete(<<"transfer-encoding">>, 1, PHeaders2),
	Req#http_req{buffer=Rest, body_state=done,
		headers=Headers3, p_headers=PHeaders3}.

-spec body_qs(Req)
	-> {ok, [{binary(), binary() | true}], Req} | {error, atom()}
	when Req::req().
body_qs(Req) ->
	body_qs(Req, [
		{length, 64000},
		{read_length, 64000},
		{read_timeout, 5000}]).

-spec body_qs(Req, body_opts()) -> {ok, [{binary(), binary() | true}], Req}
	| {badlength, Req} | {error, atom()} when Req::req().
body_qs(Req, Opts) ->
	case body(Req, Opts) of
		{ok, Body, Req2} ->
			{ok, cow_qs:parse_qs(Body), Req2};
		{more, _, Req2} ->
			{badlength, Req2};
		{error, Reason} ->
			{error, Reason}
	end.

%% Multipart API.

-spec part(Req)
	-> {ok, cow_multipart:headers(), Req} | {done, Req}
	when Req::req().
part(Req) ->
	part(Req, [
		{length, 64000},
		{read_length, 64000},
		{read_timeout, 5000}]).

-spec part(Req, body_opts())
	-> {ok, cow_multipart:headers(), Req} | {done, Req}
	when Req::req().
part(Req=#http_req{multipart=undefined}, Opts) ->
	part(init_multipart(Req), Opts);
part(Req, Opts) ->
	{Data, Req2} = stream_multipart(Req, Opts),
	part(Data, Opts, Req2).

part(Buffer, Opts, Req=#http_req{multipart={Boundary, _}}) ->
	case cow_multipart:parse_headers(Buffer, Boundary) of
		more ->
			{Data, Req2} = stream_multipart(Req, Opts),
			part(<< Buffer/binary, Data/binary >>, Opts, Req2);
		{more, Buffer2} ->
			{Data, Req2} = stream_multipart(Req, Opts),
			part(<< Buffer2/binary, Data/binary >>, Opts, Req2);
		{ok, Headers, Rest} ->
			{ok, Headers, Req#http_req{multipart={Boundary, Rest}}};
		%% Ignore epilogue.
		{done, _} ->
			{done, Req#http_req{multipart=undefined}}
	end.

-spec part_body(Req)
	-> {ok, binary(), Req} | {more, binary(), Req}
	when Req::req().
part_body(Req) ->
	part_body(Req, []).

-spec part_body(Req, body_opts())
	-> {ok, binary(), Req} | {more, binary(), Req}
	when Req::req().
part_body(Req=#http_req{multipart=undefined}, Opts) ->
	part_body(init_multipart(Req), Opts);
part_body(Req, Opts) ->
	part_body(<<>>, Opts, Req, <<>>).

part_body(Buffer, Opts, Req=#http_req{multipart={Boundary, _}}, Acc) ->
	ChunkLen = case lists:keyfind(length, 1, Opts) of
		false -> 8000000;
		{_, ChunkLen0} -> ChunkLen0
	end,
	case byte_size(Acc) > ChunkLen of
		true ->
			{more, Acc, Req#http_req{multipart={Boundary, Buffer}}};
		false ->
			{Data, Req2} = stream_multipart(Req, Opts),
			case cow_multipart:parse_body(<< Buffer/binary, Data/binary >>, Boundary) of
				{ok, Body} ->
					part_body(<<>>, Opts, Req2, << Acc/binary, Body/binary >>);
				{ok, Body, Rest} ->
					part_body(Rest, Opts, Req2, << Acc/binary, Body/binary >>);
				done ->
					{ok, Acc, Req2};
				{done, Body} ->
					{ok, << Acc/binary, Body/binary >>, Req2};
				{done, Body, Rest} ->
					{ok, << Acc/binary, Body/binary >>,
						Req2#http_req{multipart={Boundary, Rest}}}
			end
	end.

init_multipart(Req) ->
	{ok, {<<"multipart">>, _, Params}, Req2}
		= parse_header(<<"content-type">>, Req),
	{_, Boundary} = lists:keyfind(<<"boundary">>, 1, Params),
	Req2#http_req{multipart={Boundary, <<>>}}.

stream_multipart(Req=#http_req{body_state=BodyState, multipart={_, <<>>}}, Opts) ->
	true = BodyState =/= done,
	{_, Data, Req2} = body(Req, Opts),
	{Data, Req2};
stream_multipart(Req=#http_req{multipart={Boundary, Buffer}}, _) ->
	{Buffer, Req#http_req{multipart={Boundary, <<>>}}}.

%% Response API.

%% The cookie name cannot contain any of the following characters:
%%   =,;\s\t\r\n\013\014
%%
%% The cookie value cannot contain any of the following characters:
%%   ,; \t\r\n\013\014
-spec set_resp_cookie(iodata(), iodata(), cookie_opts(), Req)
	-> Req when Req::req().
set_resp_cookie(Name, Value, Opts, Req) ->
	Cookie = cow_cookie:setcookie(Name, Value, Opts),
	set_resp_header(<<"set-cookie">>, Cookie, Req).

-spec set_resp_header(binary(), iodata(), Req)
	-> Req when Req::req().
set_resp_header(Name, Value, Req=#http_req{resp_headers=RespHeaders}) ->
	Req#http_req{resp_headers=[{Name, Value}|RespHeaders]}.

-spec set_resp_body(iodata(), Req) -> Req when Req::req().
set_resp_body(Body, Req) ->
	Req#http_req{resp_body=Body}.

-spec set_resp_body_fun(resp_body_fun(), Req) -> Req when Req::req().
set_resp_body_fun(StreamFun, Req) when is_function(StreamFun) ->
	Req#http_req{resp_body=StreamFun}.

%% If the body function crashes while writing the response body or writes
%% fewer bytes than declared the behaviour is undefined.
-spec set_resp_body_fun(non_neg_integer(), resp_body_fun(), Req)
	-> Req when Req::req();
	(chunked, resp_chunked_fun(), Req)
	-> Req when Req::req().
set_resp_body_fun(StreamLen, StreamFun, Req)
		when is_integer(StreamLen), is_function(StreamFun) ->
	Req#http_req{resp_body={StreamLen, StreamFun}};
set_resp_body_fun(chunked, StreamFun, Req)
		when is_function(StreamFun) ->
	Req#http_req{resp_body={chunked, StreamFun}}.

-spec has_resp_header(binary(), req()) -> boolean().
has_resp_header(Name, #http_req{resp_headers=RespHeaders}) ->
	lists:keymember(Name, 1, RespHeaders).

-spec has_resp_body(req()) -> boolean().
has_resp_body(#http_req{resp_body=RespBody}) when is_function(RespBody) ->
	true;
has_resp_body(#http_req{resp_body={chunked, _}}) ->
	true;
has_resp_body(#http_req{resp_body={Length, _}}) ->
	Length > 0;
has_resp_body(#http_req{resp_body=RespBody}) ->
	iolist_size(RespBody) > 0.

-spec delete_resp_header(binary(), Req)
	-> Req when Req::req().
delete_resp_header(Name, Req=#http_req{resp_headers=RespHeaders}) ->
	RespHeaders2 = lists:keydelete(Name, 1, RespHeaders),
	Req#http_req{resp_headers=RespHeaders2}.

-spec reply(cowboy:http_status(), Req) -> {ok, Req} when Req::req().
reply(Status, Req=#http_req{resp_body=Body}) ->
	reply(Status, [], Body, Req).

-spec reply(cowboy:http_status(), cowboy:http_headers(), Req)
	-> {ok, Req} when Req::req().
reply(Status, Headers, Req=#http_req{resp_body=Body}) ->
	reply(Status, Headers, Body, Req).

-spec reply(cowboy:http_status(), cowboy:http_headers(),
	iodata() | {non_neg_integer() | resp_body_fun()}, Req)
	-> {ok, Req} when Req::req().
reply(Status, Headers, Body, Req=#http_req{
		socket=Socket, transport=Transport,
		version=Version, connection=Connection,
		method=Method, resp_compress=Compress,
		resp_state=RespState, resp_headers=RespHeaders})
		when RespState =:= waiting; RespState =:= waiting_stream ->
	HTTP11Headers = if
		Transport =/= cowboy_spdy, Version =:= 'HTTP/1.0', Connection =:= keepalive ->
			[{<<"connection">>, atom_to_connection(Connection)}];
		Transport =/= cowboy_spdy, Version =:= 'HTTP/1.1', Connection =:= close ->
			[{<<"connection">>, atom_to_connection(Connection)}];
		true ->
			[]
	end,
	Req3 = case Body of
		BodyFun when is_function(BodyFun) ->
			%% We stream the response body until we close the connection.
			RespConn = close,
			{RespType, Req2} = if
				Transport =:= cowboy_spdy ->
					response(Status, Headers, RespHeaders, [
						{<<"date">>, cowboy_clock:rfc1123()},
						{<<"server">>, <<"Cowboy">>}
					], stream, Req);
				true ->
					response(Status, Headers, RespHeaders, [
						{<<"connection">>, <<"close">>},
						{<<"date">>, cowboy_clock:rfc1123()},
						{<<"server">>, <<"Cowboy">>},
						{<<"transfer-encoding">>, <<"identity">>}
					], <<>>, Req)
			end,
			if	RespType =/= hook, Method =/= <<"HEAD">> ->
					BodyFun(Socket, Transport);
				true -> ok
			end,
			Req2#http_req{connection=RespConn};
		{chunked, BodyFun} ->
			%% We stream the response body in chunks.
			{RespType, Req2} = chunked_response(Status, Headers, Req),
			if	RespType =/= hook, Method =/= <<"HEAD">> ->
					ChunkFun = fun(IoData) -> chunk(IoData, Req2) end,
					BodyFun(ChunkFun),
					%% Send the last chunk if chunked encoding was used.
					if
						Version =:= 'HTTP/1.0'; RespState =:= waiting_stream ->
							Req2;
						true ->
							last_chunk(Req2)
					end;
				true -> Req2
			end;
		{ContentLength, BodyFun} ->
			%% We stream the response body for ContentLength bytes.
			RespConn = response_connection(Headers, Connection),
			{RespType, Req2} = response(Status, Headers, RespHeaders, [
					{<<"content-length">>, integer_to_list(ContentLength)},
					{<<"date">>, cowboy_clock:rfc1123()},
					{<<"server">>, <<"Cowboy">>}
				|HTTP11Headers], stream, Req),
			if	RespType =/= hook, Method =/= <<"HEAD">> ->
					BodyFun(Socket, Transport);
				true -> ok
			end,
			Req2#http_req{connection=RespConn};
		_ when Compress ->
			RespConn = response_connection(Headers, Connection),
			Req2 = reply_may_compress(Status, Headers, Body, Req,
				RespHeaders, HTTP11Headers, Method),
			Req2#http_req{connection=RespConn};
		_ ->
			RespConn = response_connection(Headers, Connection),
			Req2 = reply_no_compress(Status, Headers, Body, Req,
				RespHeaders, HTTP11Headers, Method, iolist_size(Body)),
			Req2#http_req{connection=RespConn}
	end,
	{ok, Req3#http_req{resp_state=done, resp_headers=[], resp_body= <<>>}}.

reply_may_compress(Status, Headers, Body, Req,
		RespHeaders, HTTP11Headers, Method) ->
	BodySize = iolist_size(Body),
	case parse_header(<<"accept-encoding">>, Req) of
		{ok, Encodings, Req2} ->
			CanGzip = (BodySize > 300)
				andalso (false =:= lists:keyfind(<<"content-encoding">>,
					1, Headers))
				andalso (false =:= lists:keyfind(<<"content-encoding">>,
					1, RespHeaders))
				andalso (false =:= lists:keyfind(<<"transfer-encoding">>,
					1, Headers))
				andalso (false =:= lists:keyfind(<<"transfer-encoding">>,
					1, RespHeaders))
				andalso (Encodings =/= undefined)
				andalso (false =/= lists:keyfind(<<"gzip">>, 1, Encodings)),
			case CanGzip of
				true ->
					GzBody = zlib:gzip(Body),
					{_, Req3} = response(Status, Headers, RespHeaders, [
							{<<"content-length">>, integer_to_list(byte_size(GzBody))},
							{<<"content-encoding">>, <<"gzip">>},
							{<<"date">>, cowboy_clock:rfc1123()},
							{<<"server">>, <<"Cowboy">>}
						|HTTP11Headers],
						case Method of <<"HEAD">> -> <<>>; _ -> GzBody end,
						Req2),
					Req3;
				false ->
					reply_no_compress(Status, Headers, Body, Req,
						RespHeaders, HTTP11Headers, Method, BodySize)
			end;
		{error, badarg} ->
			reply_no_compress(Status, Headers, Body, Req,
				RespHeaders, HTTP11Headers, Method, BodySize)
	end.

reply_no_compress(Status, Headers, Body, Req,
		RespHeaders, HTTP11Headers, Method, BodySize) ->
	{_, Req2} = response(Status, Headers, RespHeaders, [
			{<<"content-length">>, integer_to_list(BodySize)},
			{<<"date">>, cowboy_clock:rfc1123()},
			{<<"server">>, <<"Cowboy">>}
		|HTTP11Headers],
		case Method of <<"HEAD">> -> <<>>; _ -> Body end,
		Req),
	Req2.

-spec chunked_reply(cowboy:http_status(), Req) -> {ok, Req} when Req::req().
chunked_reply(Status, Req) ->
	chunked_reply(Status, [], Req).

-spec chunked_reply(cowboy:http_status(), cowboy:http_headers(), Req)
	-> {ok, Req} when Req::req().
chunked_reply(Status, Headers, Req) ->
	{_, Req2} = chunked_response(Status, Headers, Req),
	{ok, Req2}.

-spec chunk(iodata(), req()) -> ok | {error, atom()}.
chunk(_Data, #http_req{method= <<"HEAD">>}) ->
	ok;
chunk(Data, #http_req{socket=Socket, transport=cowboy_spdy,
		resp_state=chunks}) ->
	cowboy_spdy:stream_data(Socket, Data);
chunk(Data, #http_req{socket=Socket, transport=Transport,
		resp_state=stream}) ->
	Transport:send(Socket, Data);
chunk(Data, #http_req{socket=Socket, transport=Transport,
		resp_state=chunks}) ->
	Transport:send(Socket, [integer_to_list(iolist_size(Data), 16),
		<<"\r\n">>, Data, <<"\r\n">>]).

%% If ever made public, need to send nothing if HEAD.
-spec last_chunk(Req) -> Req when Req::req().
last_chunk(Req=#http_req{socket=Socket, transport=cowboy_spdy}) ->
	_ = cowboy_spdy:stream_close(Socket),
	Req#http_req{resp_state=done};
last_chunk(Req=#http_req{socket=Socket, transport=Transport}) ->
	_ = Transport:send(Socket, <<"0\r\n\r\n">>),
	Req#http_req{resp_state=done}.

-spec upgrade_reply(cowboy:http_status(), cowboy:http_headers(), Req)
	-> {ok, Req} when Req::req().
upgrade_reply(Status, Headers, Req=#http_req{transport=Transport,
		resp_state=waiting, resp_headers=RespHeaders})
		when Transport =/= cowboy_spdy ->
	{_, Req2} = response(Status, Headers, RespHeaders, [
		{<<"connection">>, <<"Upgrade">>}
	], <<>>, Req),
	{ok, Req2#http_req{resp_state=done, resp_headers=[], resp_body= <<>>}}.

-spec continue(req()) -> ok | {error, atom()}.
continue(#http_req{socket=Socket, transport=Transport,
		version=Version}) ->
	HTTPVer = atom_to_binary(Version, latin1),
	Transport:send(Socket,
		<< HTTPVer/binary, " ", (status(100))/binary, "\r\n\r\n" >>).

%% Meant to be used internally for sending errors after crashes.
-spec maybe_reply([{module(), atom(), arity() | [term()], _}], req()) -> ok.
maybe_reply(Stacktrace, Req) ->
	receive
		{cowboy_req, resp_sent} -> ok
	after 0 ->
		_ = do_maybe_reply(Stacktrace, Req),
		ok
	end.

do_maybe_reply([
		{cow_http_hd, _, _, _},
		{cowboy_req, parse_header, _, _}|_], Req) ->
	cowboy_req:reply(400, Req);
do_maybe_reply(_, Req) ->
	cowboy_req:reply(500, Req).

-spec ensure_response(req(), cowboy:http_status()) -> ok.
%% The response has already been fully sent to the client.
ensure_response(#http_req{resp_state=done}, _) ->
	ok;
%% No response has been sent but everything apparently went fine.
%% Reply with the status code found in the second argument.
ensure_response(Req=#http_req{resp_state=RespState}, Status)
		when RespState =:= waiting; RespState =:= waiting_stream ->
	_ = reply(Status, [], [], Req),
	ok;
%% Terminate the chunked body for HTTP/1.1 only.
ensure_response(#http_req{method= <<"HEAD">>}, _) ->
	ok;
ensure_response(Req=#http_req{resp_state=chunks}, _) ->
	_ = last_chunk(Req),
	ok;
ensure_response(#http_req{}, _) ->
	ok.

%% Private setter/getter API.

-spec append_buffer(binary(), Req) -> Req when Req::req().
append_buffer(Suffix, Req=#http_req{buffer=Buffer}) ->
	Req#http_req{buffer= << Buffer/binary, Suffix/binary >>}.

-spec get(atom(), req()) -> any(); ([atom()], req()) -> any().
get(List, Req) when is_list(List) ->
	[g(Atom, Req) || Atom <- List];
get(Atom, Req) when is_atom(Atom) ->
	g(Atom, Req).

g(bindings, #http_req{bindings=Ret}) -> Ret;
g(body_state, #http_req{body_state=Ret}) -> Ret;
g(buffer, #http_req{buffer=Ret}) -> Ret;
g(connection, #http_req{connection=Ret}) -> Ret;
g(cookies, #http_req{cookies=Ret}) -> Ret;
g(headers, #http_req{headers=Ret}) -> Ret;
g(host, #http_req{host=Ret}) -> Ret;
g(host_info, #http_req{host_info=Ret}) -> Ret;
g(meta, #http_req{meta=Ret}) -> Ret;
g(method, #http_req{method=Ret}) -> Ret;
g(multipart, #http_req{multipart=Ret}) -> Ret;
g(onresponse, #http_req{onresponse=Ret}) -> Ret;
g(p_headers, #http_req{p_headers=Ret}) -> Ret;
g(path, #http_req{path=Ret}) -> Ret;
g(path_info, #http_req{path_info=Ret}) -> Ret;
g(peer, #http_req{peer=Ret}) -> Ret;
g(pid, #http_req{pid=Ret}) -> Ret;
g(port, #http_req{port=Ret}) -> Ret;
g(qs, #http_req{qs=Ret}) -> Ret;
g(qs_vals, #http_req{qs_vals=Ret}) -> Ret;
g(resp_body, #http_req{resp_body=Ret}) -> Ret;
g(resp_compress, #http_req{resp_compress=Ret}) -> Ret;
g(resp_headers, #http_req{resp_headers=Ret}) -> Ret;
g(resp_state, #http_req{resp_state=Ret}) -> Ret;
g(socket, #http_req{socket=Ret}) -> Ret;
g(transport, #http_req{transport=Ret}) -> Ret;
g(version, #http_req{version=Ret}) -> Ret.

-spec set([{atom(), any()}], Req) -> Req when Req::req().
set([], Req) -> Req;
set([{bindings, Val}|Tail], Req) -> set(Tail, Req#http_req{bindings=Val});
set([{body_state, Val}|Tail], Req) -> set(Tail, Req#http_req{body_state=Val});
set([{buffer, Val}|Tail], Req) -> set(Tail, Req#http_req{buffer=Val});
set([{connection, Val}|Tail], Req) -> set(Tail, Req#http_req{connection=Val});
set([{cookies, Val}|Tail], Req) -> set(Tail, Req#http_req{cookies=Val});
set([{headers, Val}|Tail], Req) -> set(Tail, Req#http_req{headers=Val});
set([{host, Val}|Tail], Req) -> set(Tail, Req#http_req{host=Val});
set([{host_info, Val}|Tail], Req) -> set(Tail, Req#http_req{host_info=Val});
set([{meta, Val}|Tail], Req) -> set(Tail, Req#http_req{meta=Val});
set([{method, Val}|Tail], Req) -> set(Tail, Req#http_req{method=Val});
set([{multipart, Val}|Tail], Req) -> set(Tail, Req#http_req{multipart=Val});
set([{onresponse, Val}|Tail], Req) -> set(Tail, Req#http_req{onresponse=Val});
set([{p_headers, Val}|Tail], Req) -> set(Tail, Req#http_req{p_headers=Val});
set([{path, Val}|Tail], Req) -> set(Tail, Req#http_req{path=Val});
set([{path_info, Val}|Tail], Req) -> set(Tail, Req#http_req{path_info=Val});
set([{peer, Val}|Tail], Req) -> set(Tail, Req#http_req{peer=Val});
set([{pid, Val}|Tail], Req) -> set(Tail, Req#http_req{pid=Val});
set([{port, Val}|Tail], Req) -> set(Tail, Req#http_req{port=Val});
set([{qs, Val}|Tail], Req) -> set(Tail, Req#http_req{qs=Val});
set([{qs_vals, Val}|Tail], Req) -> set(Tail, Req#http_req{qs_vals=Val});
set([{resp_body, Val}|Tail], Req) -> set(Tail, Req#http_req{resp_body=Val});
set([{resp_headers, Val}|Tail], Req) -> set(Tail, Req#http_req{resp_headers=Val});
set([{resp_state, Val}|Tail], Req) -> set(Tail, Req#http_req{resp_state=Val});
set([{socket, Val}|Tail], Req) -> set(Tail, Req#http_req{socket=Val});
set([{transport, Val}|Tail], Req) -> set(Tail, Req#http_req{transport=Val});
set([{version, Val}|Tail], Req) -> set(Tail, Req#http_req{version=Val}).

-spec set_bindings(cowboy_router:tokens(), cowboy_router:tokens(),
	cowboy_router:bindings(), Req) -> Req when Req::req().
set_bindings(HostInfo, PathInfo, Bindings, Req) ->
	Req#http_req{host_info=HostInfo, path_info=PathInfo,
		bindings=Bindings}.

%% Misc API.

-spec compact(Req) -> Req when Req::req().
compact(Req) ->
	Req#http_req{host_info=undefined,
		path_info=undefined, qs_vals=undefined,
		bindings=undefined, headers=[],
		p_headers=[], cookies=[]}.

-spec lock(Req) -> Req when Req::req().
lock(Req) ->
	Req#http_req{resp_state=locked}.

-spec to_list(req()) -> [{atom(), any()}].
to_list(Req) ->
	lists:zip(record_info(fields, http_req), tl(tuple_to_list(Req))).

%% Internal.

-spec chunked_response(cowboy:http_status(), cowboy:http_headers(), Req) ->
	{normal | hook, Req} when Req::req().
chunked_response(Status, Headers, Req=#http_req{
		transport=cowboy_spdy, resp_state=waiting,
		resp_headers=RespHeaders}) ->
	{RespType, Req2} = response(Status, Headers, RespHeaders, [
		{<<"date">>, cowboy_clock:rfc1123()},
		{<<"server">>, <<"Cowboy">>}
	], stream, Req),
	{RespType, Req2#http_req{resp_state=chunks,
		resp_headers=[], resp_body= <<>>}};
chunked_response(Status, Headers, Req=#http_req{
		version=Version, connection=Connection,
		resp_state=RespState, resp_headers=RespHeaders})
		when RespState =:= waiting; RespState =:= waiting_stream ->
	RespConn = response_connection(Headers, Connection),
	HTTP11Headers = if
		Version =:= 'HTTP/1.0', Connection =:= keepalive ->
			[{<<"connection">>, atom_to_connection(Connection)}];
		Version =:= 'HTTP/1.0' -> [];
		true ->
			MaybeTE = if
				RespState =:= waiting_stream -> [];
				true -> [{<<"transfer-encoding">>, <<"chunked">>}]
			end,
			if
				Connection =:= close ->
					[{<<"connection">>, atom_to_connection(Connection)}|MaybeTE];
				true ->
					MaybeTE
			end
	end,
	RespState2 = if
		Version =:= 'HTTP/1.1', RespState =:= 'waiting' -> chunks;
		true -> stream
	end,
	{RespType, Req2} = response(Status, Headers, RespHeaders, [
		{<<"date">>, cowboy_clock:rfc1123()},
		{<<"server">>, <<"Cowboy">>}
	|HTTP11Headers], <<>>, Req),
	{RespType, Req2#http_req{connection=RespConn, resp_state=RespState2,
			resp_headers=[], resp_body= <<>>}}.

-spec response(cowboy:http_status(), cowboy:http_headers(),
	cowboy:http_headers(), cowboy:http_headers(), stream | iodata(), Req)
	-> {normal | hook, Req} when Req::req().
response(Status, Headers, RespHeaders, DefaultHeaders, Body, Req=#http_req{
		socket=Socket, transport=Transport, version=Version,
		pid=ReqPid, onresponse=OnResponse}) ->
	FullHeaders = case OnResponse of
		already_called -> Headers;
		_ -> response_merge_headers(Headers, RespHeaders, DefaultHeaders)
	end,
	Body2 = case Body of stream -> <<>>; _ -> Body end,
	{Status2, FullHeaders2, Req2} = case OnResponse of
		already_called -> {Status, FullHeaders, Req};
		undefined -> {Status, FullHeaders, Req};
		OnResponse ->
			case OnResponse(Status, FullHeaders, Body2,
					%% Don't call 'onresponse' from the hook itself.
					Req#http_req{resp_headers=[], resp_body= <<>>,
						onresponse=already_called}) of
				StHdReq = {_, _, _} ->
					StHdReq;
				Req1 ->
					{Status, FullHeaders, Req1}
			end
	end,
	ReplyType = case Req2#http_req.resp_state of
		waiting when Transport =:= cowboy_spdy, Body =:= stream ->
			cowboy_spdy:stream_reply(Socket, status(Status2), FullHeaders2),
			ReqPid ! {?MODULE, resp_sent},
			normal;
		waiting when Transport =:= cowboy_spdy ->
			cowboy_spdy:reply(Socket, status(Status2), FullHeaders2, Body),
			ReqPid ! {?MODULE, resp_sent},
			normal;
		RespState when RespState =:= waiting; RespState =:= waiting_stream ->
			HTTPVer = atom_to_binary(Version, latin1),
			StatusLine = << HTTPVer/binary, " ",
				(status(Status2))/binary, "\r\n" >>,
			HeaderLines = [[Key, <<": ">>, Value, <<"\r\n">>]
				|| {Key, Value} <- FullHeaders2],
			Transport:send(Socket, [StatusLine, HeaderLines, <<"\r\n">>, Body2]),
			ReqPid ! {?MODULE, resp_sent},
			normal;
		_ ->
			hook
	end,
	{ReplyType, Req2}.

-spec response_connection(cowboy:http_headers(), keepalive | close)
	-> keepalive | close.
response_connection([], Connection) ->
	Connection;
response_connection([{Name, Value}|Tail], Connection) ->
	case Name of
		<<"connection">> ->
			Tokens = cow_http_hd:parse_connection(Value),
			connection_to_atom(Tokens);
		_ ->
			response_connection(Tail, Connection)
	end.

-spec response_merge_headers(cowboy:http_headers(), cowboy:http_headers(),
	cowboy:http_headers()) -> cowboy:http_headers().
response_merge_headers(Headers, RespHeaders, DefaultHeaders) ->
	Headers2 = [{Key, Value} || {Key, Value} <- Headers],
	merge_headers(
		merge_headers(Headers2, RespHeaders),
		DefaultHeaders).

-spec merge_headers(cowboy:http_headers(), cowboy:http_headers())
	-> cowboy:http_headers().

%% Merge headers by prepending the tuples in the second list to the
%% first list. It also handles Set-Cookie properly, which supports
%% duplicated entries. Notice that, while the RFC2109 does allow more
%% than one cookie to be set per Set-Cookie header, we are following
%% the implementation of common web servers and applications which
%% return many distinct headers per each Set-Cookie entry to avoid
%% issues with clients/browser which may not support it.
merge_headers(Headers, []) ->
	Headers;
merge_headers(Headers, [{<<"set-cookie">>, Value}|Tail]) ->
	merge_headers([{<<"set-cookie">>, Value}|Headers], Tail);
merge_headers(Headers, [{Name, Value}|Tail]) ->
	Headers2 = case lists:keymember(Name, 1, Headers) of
		true -> Headers;
		false -> [{Name, Value}|Headers]
	end,
	merge_headers(Headers2, Tail).

-spec atom_to_connection(keepalive) -> <<_:80>>;
						(close) -> <<_:40>>.
atom_to_connection(keepalive) ->
	<<"keep-alive">>;
atom_to_connection(close) ->
	<<"close">>.

%% We don't match on "keep-alive" since it is the default value.
-spec connection_to_atom([binary()]) -> keepalive | close.
connection_to_atom([]) ->
	keepalive;
connection_to_atom([<<"close">>|_]) ->
	close;
connection_to_atom([_|Tail]) ->
	connection_to_atom(Tail).

-spec status(cowboy:http_status()) -> binary().
status(100) -> <<"100 Continue">>;
status(101) -> <<"101 Switching Protocols">>;
status(102) -> <<"102 Processing">>;
status(200) -> <<"200 OK">>;
status(201) -> <<"201 Created">>;
status(202) -> <<"202 Accepted">>;
status(203) -> <<"203 Non-Authoritative Information">>;
status(204) -> <<"204 No Content">>;
status(205) -> <<"205 Reset Content">>;
status(206) -> <<"206 Partial Content">>;
status(207) -> <<"207 Multi-Status">>;
status(226) -> <<"226 IM Used">>;
status(300) -> <<"300 Multiple Choices">>;
status(301) -> <<"301 Moved Permanently">>;
status(302) -> <<"302 Found">>;
status(303) -> <<"303 See Other">>;
status(304) -> <<"304 Not Modified">>;
status(305) -> <<"305 Use Proxy">>;
status(306) -> <<"306 Switch Proxy">>;
status(307) -> <<"307 Temporary Redirect">>;
status(400) -> <<"400 Bad Request">>;
status(401) -> <<"401 Unauthorized">>;
status(402) -> <<"402 Payment Required">>;
status(403) -> <<"403 Forbidden">>;
status(404) -> <<"404 Not Found">>;
status(405) -> <<"405 Method Not Allowed">>;
status(406) -> <<"406 Not Acceptable">>;
status(407) -> <<"407 Proxy Authentication Required">>;
status(408) -> <<"408 Request Timeout">>;
status(409) -> <<"409 Conflict">>;
status(410) -> <<"410 Gone">>;
status(411) -> <<"411 Length Required">>;
status(412) -> <<"412 Precondition Failed">>;
status(413) -> <<"413 Request Entity Too Large">>;
status(414) -> <<"414 Request-URI Too Long">>;
status(415) -> <<"415 Unsupported Media Type">>;
status(416) -> <<"416 Requested Range Not Satisfiable">>;
status(417) -> <<"417 Expectation Failed">>;
status(418) -> <<"418 I'm a teapot">>;
status(422) -> <<"422 Unprocessable Entity">>;
status(423) -> <<"423 Locked">>;
status(424) -> <<"424 Failed Dependency">>;
status(425) -> <<"425 Unordered Collection">>;
status(426) -> <<"426 Upgrade Required">>;
status(428) -> <<"428 Precondition Required">>;
status(429) -> <<"429 Too Many Requests">>;
status(431) -> <<"431 Request Header Fields Too Large">>;
status(500) -> <<"500 Internal Server Error">>;
status(501) -> <<"501 Not Implemented">>;
status(502) -> <<"502 Bad Gateway">>;
status(503) -> <<"503 Service Unavailable">>;
status(504) -> <<"504 Gateway Timeout">>;
status(505) -> <<"505 HTTP Version Not Supported">>;
status(506) -> <<"506 Variant Also Negotiates">>;
status(507) -> <<"507 Insufficient Storage">>;
status(510) -> <<"510 Not Extended">>;
status(511) -> <<"511 Network Authentication Required">>;
status(B) when is_binary(B) -> B.

%% Tests.

-ifdef(TEST).
url_test() ->
	{undefined, _} =
		url(#http_req{transport=ranch_tcp, host= <<>>, port= undefined,
			path= <<>>, qs= <<>>, pid=self()}),
	{<<"http://localhost/path">>, _ } =
		url(#http_req{transport=ranch_tcp, host= <<"localhost">>, port=80,
			path= <<"/path">>, qs= <<>>, pid=self()}),
	{<<"http://localhost:443/path">>, _} =
		url(#http_req{transport=ranch_tcp, host= <<"localhost">>, port=443,
			path= <<"/path">>, qs= <<>>, pid=self()}),
	{<<"http://localhost:8080/path">>, _} =
		url(#http_req{transport=ranch_tcp, host= <<"localhost">>, port=8080,
			path= <<"/path">>, qs= <<>>, pid=self()}),
	{<<"http://localhost:8080/path?dummy=2785">>, _} =
		url(#http_req{transport=ranch_tcp, host= <<"localhost">>, port=8080,
			path= <<"/path">>, qs= <<"dummy=2785">>, pid=self()}),
	{<<"https://localhost/path">>, _} =
		url(#http_req{transport=ranch_ssl, host= <<"localhost">>, port=443,
			path= <<"/path">>, qs= <<>>, pid=self()}),
	{<<"https://localhost:8443/path">>, _} =
		url(#http_req{transport=ranch_ssl, host= <<"localhost">>, port=8443,
			path= <<"/path">>, qs= <<>>, pid=self()}),
	{<<"https://localhost:8443/path?dummy=2785">>, _} =
		url(#http_req{transport=ranch_ssl, host= <<"localhost">>, port=8443,
			path= <<"/path">>, qs= <<"dummy=2785">>, pid=self()}),
	ok.

connection_to_atom_test_() ->
	Tests = [
		{[<<"close">>], close},
		{[<<"keep-alive">>], keepalive},
		{[<<"keep-alive">>, <<"upgrade">>], keepalive}
	],
	[{lists:flatten(io_lib:format("~p", [T])),
		fun() -> R = connection_to_atom(T) end} || {T, R} <- Tests].

merge_headers_test_() ->
	Tests = [
		{[{<<"content-length">>,<<"13">>},{<<"server">>,<<"Cowboy">>}],
		 [{<<"set-cookie">>,<<"foo=bar">>},{<<"content-length">>,<<"11">>}],
		 [{<<"set-cookie">>,<<"foo=bar">>},
		  {<<"content-length">>,<<"13">>},
		  {<<"server">>,<<"Cowboy">>}]},
		{[{<<"content-length">>,<<"13">>},{<<"server">>,<<"Cowboy">>}],
		 [{<<"set-cookie">>,<<"foo=bar">>},{<<"set-cookie">>,<<"bar=baz">>}],
		 [{<<"set-cookie">>,<<"bar=baz">>},
		  {<<"set-cookie">>,<<"foo=bar">>},
		  {<<"content-length">>,<<"13">>},
		  {<<"server">>,<<"Cowboy">>}]}
	],
	[fun() -> Res = merge_headers(L,R) end || {L, R, Res} <- Tests].
-endif.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted deps/cowboy/src/cowboy_rest.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
%% Copyright (c) 2011-2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

%% Originally based on the Webmachine Diagram from Alan Dean and
%% Justin Sheehy.
-module(cowboy_rest).
-behaviour(cowboy_sub_protocol).

-export([upgrade/4]).

-record(state, {
	env :: cowboy_middleware:env(),
	method = undefined :: binary(),

	%% Handler.
	handler :: atom(),
	handler_state :: any(),

	%% Allowed methods. Only used for OPTIONS requests.
	allowed_methods :: [binary()],

	%% Media type.
	content_types_p = [] ::
		[{binary() | {binary(), binary(), [{binary(), binary()}] | '*'},
			atom()}],
	content_type_a :: undefined
		| {binary() | {binary(), binary(), [{binary(), binary()}] | '*'},
			atom()},

	%% Language.
	languages_p = [] :: [binary()],
	language_a :: undefined | binary(),

	%% Charset.
	charsets_p = [] :: [binary()],
	charset_a :: undefined | binary(),

	%% Whether the resource exists.
	exists = false :: boolean(),

	%% Cached resource calls.
	etag :: undefined | no_call | {strong | weak, binary()},
	last_modified :: undefined | no_call | calendar:datetime(),
	expires :: undefined | no_call | calendar:datetime() | binary()
}).

-spec upgrade(Req, Env, module(), any())
	-> {ok, Req, Env} when Req::cowboy_req:req(), Env::cowboy_middleware:env().
upgrade(Req, Env, Handler, HandlerOpts) ->
	Method = cowboy_req:get(method, Req),
	case erlang:function_exported(Handler, rest_init, 2) of
		true ->
			try Handler:rest_init(Req, HandlerOpts) of
				{ok, Req2, HandlerState} ->
					service_available(Req2, #state{env=Env, method=Method,
						handler=Handler, handler_state=HandlerState})
			catch Class:Reason ->
				Stacktrace = erlang:get_stacktrace(),
				cowboy_req:maybe_reply(Stacktrace, Req),
				erlang:Class([
					{reason, Reason},
					{mfa, {Handler, rest_init, 2}},
					{stacktrace, Stacktrace},
					{req, cowboy_req:to_list(Req)},
					{opts, HandlerOpts}
				])
			end;
		false ->
			service_available(Req, #state{env=Env, method=Method,
				handler=Handler})
	end.

service_available(Req, State) ->
	expect(Req, State, service_available, true, fun known_methods/2, 503).

%% known_methods/2 should return a list of binary methods.
known_methods(Req, State=#state{method=Method}) ->
	case call(Req, State, known_methods) of
		no_call when Method =:= <<"HEAD">>; Method =:= <<"GET">>;
				Method =:= <<"POST">>; Method =:= <<"PUT">>;
				Method =:= <<"PATCH">>; Method =:= <<"DELETE">>;
				Method =:= <<"OPTIONS">> ->
			next(Req, State, fun uri_too_long/2);
		no_call ->
			next(Req, State, 501);
		{halt, Req2, HandlerState} ->
			terminate(Req2, State#state{handler_state=HandlerState});
		{List, Req2, HandlerState} ->
			State2 = State#state{handler_state=HandlerState},
			case lists:member(Method, List) of
				true -> next(Req2, State2, fun uri_too_long/2);
				false -> next(Req2, State2, 501)
			end
	end.

uri_too_long(Req, State) ->
	expect(Req, State, uri_too_long, false, fun allowed_methods/2, 414).

%% allowed_methods/2 should return a list of binary methods.
allowed_methods(Req, State=#state{method=Method}) ->
	case call(Req, State, allowed_methods) of
		no_call when Method =:= <<"HEAD">>; Method =:= <<"GET">> ->
			next(Req, State, fun malformed_request/2);
		no_call when Method =:= <<"OPTIONS">> ->
			next(Req, State#state{allowed_methods=
				[<<"HEAD">>, <<"GET">>, <<"OPTIONS">>]},
				fun malformed_request/2);
		no_call ->
			method_not_allowed(Req, State,
				[<<"HEAD">>, <<"GET">>, <<"OPTIONS">>]);
		{halt, Req2, HandlerState} ->
			terminate(Req2, State#state{handler_state=HandlerState});
		{List, Req2, HandlerState} ->
			State2 = State#state{handler_state=HandlerState},
			case lists:member(Method, List) of
				true when Method =:= <<"OPTIONS">> ->
					next(Req2, State2#state{allowed_methods=List},
						fun malformed_request/2);
				true ->
					next(Req2, State2, fun malformed_request/2);
				false ->
					method_not_allowed(Req2, State2, List)
			end
	end.

method_not_allowed(Req, State, []) ->
	Req2 = cowboy_req:set_resp_header(<<"allow">>, <<>>, Req),
	respond(Req2, State, 405);
method_not_allowed(Req, State, Methods) ->
	<< ", ", Allow/binary >> = << << ", ", M/binary >> || M <- Methods >>,
	Req2 = cowboy_req:set_resp_header(<<"allow">>, Allow, Req),
	respond(Req2, State, 405).

malformed_request(Req, State) ->
	expect(Req, State, malformed_request, false, fun is_authorized/2, 400).

%% is_authorized/2 should return true or {false, WwwAuthenticateHeader}.
is_authorized(Req, State) ->
	case call(Req, State, is_authorized) of
		no_call ->
			forbidden(Req, State);
		{halt, Req2, HandlerState} ->
			terminate(Req2, State#state{handler_state=HandlerState});
		{true, Req2, HandlerState} ->
			forbidden(Req2, State#state{handler_state=HandlerState});
		{{false, AuthHead}, Req2, HandlerState} ->
			Req3 = cowboy_req:set_resp_header(
				<<"www-authenticate">>, AuthHead, Req2),
			respond(Req3, State#state{handler_state=HandlerState}, 401)
	end.

forbidden(Req, State) ->
	expect(Req, State, forbidden, false, fun valid_content_headers/2, 403).

valid_content_headers(Req, State) ->
	expect(Req, State, valid_content_headers, true,
		fun known_content_type/2, 501).

known_content_type(Req, State) ->
	expect(Req, State, known_content_type, true,
		fun valid_entity_length/2, 415).

valid_entity_length(Req, State) ->
	expect(Req, State, valid_entity_length, true, fun options/2, 413).

%% If you need to add additional headers to the response at this point,
%% you should do it directly in the options/2 call using set_resp_headers.
options(Req, State=#state{allowed_methods=Methods, method= <<"OPTIONS">>}) ->
	case call(Req, State, options) of
		no_call when Methods =:= [] ->
			Req2 = cowboy_req:set_resp_header(<<"allow">>, <<>>, Req),
			respond(Req2, State, 200);
		no_call ->
			<< ", ", Allow/binary >>
				= << << ", ", M/binary >> || M <- Methods >>,
			Req2 = cowboy_req:set_resp_header(<<"allow">>, Allow, Req),
			respond(Req2, State, 200);
		{halt, Req2, HandlerState} ->
			terminate(Req2, State#state{handler_state=HandlerState});
		{ok, Req2, HandlerState} ->
			respond(Req2, State#state{handler_state=HandlerState}, 200)
	end;
options(Req, State) ->
	content_types_provided(Req, State).

%% content_types_provided/2 should return a list of content types and their
%% associated callback function as a tuple: {{Type, SubType, Params}, Fun}.
%% Type and SubType are the media type as binary. Params is a list of
%% Key/Value tuple, with Key and Value a binary. Fun is the name of the
%% callback that will be used to return the content of the response. It is
%% given as an atom.
%%
%% An example of such return value would be:
%%    {{<<"text">>, <<"html">>, []}, to_html}
%%
%% Note that it is also possible to return a binary content type that will
%% then be parsed by Cowboy. However note that while this may make your
%% resources a little more readable, this is a lot less efficient.
%%
%% An example of such return value would be:
%%    {<<"text/html">>, to_html}
content_types_provided(Req, State) ->
	case call(Req, State, content_types_provided) of
		no_call ->
			State2 = State#state{
				content_types_p=[{{<<"text">>, <<"html">>, '*'}, to_html}]},
			case cowboy_req:parse_header(<<"accept">>, Req) of
				{error, badarg} ->
					respond(Req, State2, 400);
				{ok, undefined, Req2} ->
					languages_provided(
						cowboy_req:set_meta(media_type, {<<"text">>, <<"html">>, []}, Req2),
						State2#state{content_type_a={{<<"text">>, <<"html">>, []}, to_html}});
				{ok, Accept, Req2} ->
					Accept2 = prioritize_accept(Accept),
					choose_media_type(Req2, State2, Accept2)
			end;
		{halt, Req2, HandlerState} ->
			terminate(Req2, State#state{handler_state=HandlerState});
		{[], Req2, HandlerState} ->
			not_acceptable(Req2, State#state{handler_state=HandlerState});
		{CTP, Req2, HandlerState} ->
			CTP2 = [normalize_content_types(P) || P <- CTP],
			State2 = State#state{
				handler_state=HandlerState, content_types_p=CTP2},
			case cowboy_req:parse_header(<<"accept">>, Req2) of
				{error, badarg} ->
					respond(Req2, State2, 400);
				{ok, undefined, Req3} ->
					{PMT, _Fun} = HeadCTP = hd(CTP2),
					languages_provided(
						cowboy_req:set_meta(media_type, PMT, Req3),
						State2#state{content_type_a=HeadCTP});
				{ok, Accept, Req3} ->
					Accept2 = prioritize_accept(Accept),
					choose_media_type(Req3, State2, Accept2)
			end
	end.

normalize_content_types({ContentType, Callback})
		when is_binary(ContentType) ->
	{cowboy_http:content_type(ContentType), Callback};
normalize_content_types(Normalized) ->
	Normalized.

prioritize_accept(Accept) ->
	lists:sort(
		fun ({MediaTypeA, Quality, _AcceptParamsA},
			 {MediaTypeB, Quality, _AcceptParamsB}) ->
				%% Same quality, check precedence in more details.
				prioritize_mediatype(MediaTypeA, MediaTypeB);
			({_MediaTypeA, QualityA, _AcceptParamsA},
			 {_MediaTypeB, QualityB, _AcceptParamsB}) ->
				%% Just compare the quality.
				QualityA > QualityB
		end, Accept).

%% Media ranges can be overridden by more specific media ranges or
%% specific media types. If more than one media range applies to a given
%% type, the most specific reference has precedence.
%%
%% We always choose B over A when we can't decide between the two.
prioritize_mediatype({TypeA, SubTypeA, ParamsA}, {TypeB, SubTypeB, ParamsB}) ->
	case TypeB of
		TypeA ->
			case SubTypeB of
				SubTypeA -> length(ParamsA) > length(ParamsB);
				<<"*">> -> true;
				_Any -> false
			end;
		<<"*">> -> true;
		_Any -> false
	end.

%% Ignoring the rare AcceptParams. Not sure what should be done about them.
choose_media_type(Req, State, []) ->
	not_acceptable(Req, State);
choose_media_type(Req, State=#state{content_types_p=CTP},
		[MediaType|Tail]) ->
	match_media_type(Req, State, Tail, CTP, MediaType).

match_media_type(Req, State, Accept, [], _MediaType) ->
	choose_media_type(Req, State, Accept);
match_media_type(Req, State, Accept, CTP,
		MediaType = {{<<"*">>, <<"*">>, _Params_A}, _QA, _APA}) ->
	match_media_type_params(Req, State, Accept, CTP, MediaType);
match_media_type(Req, State, Accept,
			CTP = [{{Type, SubType_P, _PP}, _Fun}|_Tail],
			MediaType = {{Type, SubType_A, _PA}, _QA, _APA})
		when SubType_P =:= SubType_A; SubType_A =:= <<"*">> ->
	match_media_type_params(Req, State, Accept, CTP, MediaType);
match_media_type(Req, State, Accept, [_Any|Tail], MediaType) ->
	match_media_type(Req, State, Accept, Tail, MediaType).

match_media_type_params(Req, State, _Accept,
		[Provided = {{TP, STP, '*'}, _Fun}|_Tail],
		{{_TA, _STA, Params_A}, _QA, _APA}) ->
	PMT = {TP, STP, Params_A},
	languages_provided(cowboy_req:set_meta(media_type, PMT, Req),
		State#state{content_type_a=Provided});
match_media_type_params(Req, State, Accept,
		[Provided = {PMT = {_TP, _STP, Params_P}, _Fun}|Tail],
		MediaType = {{_TA, _STA, Params_A}, _QA, _APA}) ->
	case lists:sort(Params_P) =:= lists:sort(Params_A) of
		true ->
			languages_provided(cowboy_req:set_meta(media_type, PMT, Req),
				State#state{content_type_a=Provided});
		false ->
			match_media_type(Req, State, Accept, Tail, MediaType)
	end.

%% languages_provided should return a list of binary values indicating
%% which languages are accepted by the resource.
%%
%% @todo I suppose we should also ask the resource if it wants to
%% set a language itself or if it wants it to be automatically chosen.
languages_provided(Req, State) ->
	case call(Req, State, languages_provided) of
		no_call ->
			charsets_provided(Req, State);
		{halt, Req2, HandlerState} ->
			terminate(Req2, State#state{handler_state=HandlerState});
		{[], Req2, HandlerState} ->
			not_acceptable(Req2, State#state{handler_state=HandlerState});
		{LP, Req2, HandlerState} ->
			State2 = State#state{handler_state=HandlerState, languages_p=LP},
			{ok, AcceptLanguage, Req3} =
				cowboy_req:parse_header(<<"accept-language">>, Req2),
			case AcceptLanguage of
				undefined ->
					set_language(Req3, State2#state{language_a=hd(LP)});
				AcceptLanguage ->
					AcceptLanguage2 = prioritize_languages(AcceptLanguage),
					choose_language(Req3, State2, AcceptLanguage2)
			end
	end.

%% A language-range matches a language-tag if it exactly equals the tag,
%% or if it exactly equals a prefix of the tag such that the first tag
%% character following the prefix is "-". The special range "*", if
%% present in the Accept-Language field, matches every tag not matched
%% by any other range present in the Accept-Language field.
%%
%% @todo The last sentence probably means we should always put '*'
%% at the end of the list.
prioritize_languages(AcceptLanguages) ->
	lists:sort(
		fun ({_TagA, QualityA}, {_TagB, QualityB}) ->
			QualityA > QualityB
		end, AcceptLanguages).

choose_language(Req, State, []) ->
	not_acceptable(Req, State);
choose_language(Req, State=#state{languages_p=LP}, [Language|Tail]) ->
	match_language(Req, State, Tail, LP, Language).

match_language(Req, State, Accept, [], _Language) ->
	choose_language(Req, State, Accept);
match_language(Req, State, _Accept, [Provided|_Tail], {'*', _Quality}) ->
	set_language(Req, State#state{language_a=Provided});
match_language(Req, State, _Accept, [Provided|_Tail], {Provided, _Quality}) ->
	set_language(Req, State#state{language_a=Provided});
match_language(Req, State, Accept, [Provided|Tail],
		Language = {Tag, _Quality}) ->
	Length = byte_size(Tag),
	case Provided of
		<< Tag:Length/binary, $-, _Any/bits >> ->
			set_language(Req, State#state{language_a=Provided});
		_Any ->
			match_language(Req, State, Accept, Tail, Language)
	end.

set_language(Req, State=#state{language_a=Language}) ->
	Req2 = cowboy_req:set_resp_header(<<"content-language">>, Language, Req),
	charsets_provided(cowboy_req:set_meta(language, Language, Req2), State).

%% charsets_provided should return a list of binary values indicating
%% which charsets are accepted by the resource.
charsets_provided(Req, State) ->
	case call(Req, State, charsets_provided) of
		no_call ->
			set_content_type(Req, State);
		{halt, Req2, HandlerState} ->
			terminate(Req2, State#state{handler_state=HandlerState});
		{[], Req2, HandlerState} ->
			not_acceptable(Req2, State#state{handler_state=HandlerState});
		{CP, Req2, HandlerState} ->
			State2 = State#state{handler_state=HandlerState, charsets_p=CP},
			{ok, AcceptCharset, Req3} =
				cowboy_req:parse_header(<<"accept-charset">>, Req2),
			case AcceptCharset of
				undefined ->
					set_content_type(Req3, State2#state{charset_a=hd(CP)});
				AcceptCharset ->
					AcceptCharset2 = prioritize_charsets(AcceptCharset),
					choose_charset(Req3, State2, AcceptCharset2)
			end
	end.

%% The special value "*", if present in the Accept-Charset field,
%% matches every character set (including ISO-8859-1) which is not
%% mentioned elsewhere in the Accept-Charset field. If no "*" is present
%% in an Accept-Charset field, then all character sets not explicitly
%% mentioned get a quality value of 0, except for ISO-8859-1, which gets
%% a quality value of 1 if not explicitly mentioned.
prioritize_charsets(AcceptCharsets) ->
	AcceptCharsets2 = lists:sort(
		fun ({_CharsetA, QualityA}, {_CharsetB, QualityB}) ->
			QualityA > QualityB
		end, AcceptCharsets),
	case lists:keymember(<<"*">>, 1, AcceptCharsets2) of
		true -> AcceptCharsets2;
		false ->
			case lists:keymember(<<"iso-8859-1">>, 1, AcceptCharsets2) of
				true -> AcceptCharsets2;
				false -> [{<<"iso-8859-1">>, 1000}|AcceptCharsets2]
			end
	end.

choose_charset(Req, State, []) ->
	not_acceptable(Req, State);
choose_charset(Req, State=#state{charsets_p=CP}, [Charset|Tail]) ->
	match_charset(Req, State, Tail, CP, Charset).

match_charset(Req, State, Accept, [], _Charset) ->
	choose_charset(Req, State, Accept);
match_charset(Req, State, _Accept, [Provided|_], {Provided, _}) ->
	set_content_type(Req, State#state{charset_a=Provided});
match_charset(Req, State, Accept, [_|Tail], Charset) ->
	match_charset(Req, State, Accept, Tail, Charset).

set_content_type(Req, State=#state{
		content_type_a={{Type, SubType, Params}, _Fun},
		charset_a=Charset}) ->
	ParamsBin = set_content_type_build_params(Params, []),
	ContentType = [Type, <<"/">>, SubType, ParamsBin],
	ContentType2 = case Charset of
		undefined -> ContentType;
		Charset -> [ContentType, <<"; charset=">>, Charset]
	end,
	Req2 = cowboy_req:set_resp_header(<<"content-type">>, ContentType2, Req),
	encodings_provided(cowboy_req:set_meta(charset, Charset, Req2), State).

set_content_type_build_params('*', []) ->
	<<>>;
set_content_type_build_params([], []) ->
	<<>>;
set_content_type_build_params([], Acc) ->
	lists:reverse(Acc);
set_content_type_build_params([{Attr, Value}|Tail], Acc) ->
	set_content_type_build_params(Tail, [[Attr, <<"=">>, Value], <<";">>|Acc]).

%% @todo Match for identity as we provide nothing else for now.
%% @todo Don't forget to set the Content-Encoding header when we reply a body
%% and the found encoding is something other than identity.
encodings_provided(Req, State) ->
	variances(Req, State).

not_acceptable(Req, State) ->
	respond(Req, State, 406).

%% variances/2 should return a list of headers that will be added
%% to the Vary response header. The Accept, Accept-Language,
%% Accept-Charset and Accept-Encoding headers do not need to be
%% specified.
%%
%% @todo Do Accept-Encoding too when we handle it.
%% @todo Does the order matter?
variances(Req, State=#state{content_types_p=CTP,
		languages_p=LP, charsets_p=CP}) ->
	Variances = case CTP of
		[] -> [];
		[_] -> [];
		[_|_] -> [<<"accept">>]
	end,
	Variances2 = case LP of
		[] -> Variances;
		[_] -> Variances;
		[_|_] -> [<<"accept-language">>|Variances]
	end,
	Variances3 = case CP of
		[] -> Variances2;
		[_] -> Variances2;
		[_|_] -> [<<"accept-charset">>|Variances2]
	end,
	try variances(Req, State, Variances3) of
		{Variances4, Req2, State2} ->
			case [[<<", ">>, V] || V <- Variances4] of
				[] ->
					resource_exists(Req2, State2);
				[[<<", ">>, H]|Variances5] ->
					Req3 = cowboy_req:set_resp_header(
						<<"vary">>, [H|Variances5], Req2),
					resource_exists(Req3, State2)
			end
	catch Class:Reason ->
		error_terminate(Req, State, Class, Reason, variances)
	end.

variances(Req, State, Variances) ->
	case unsafe_call(Req, State, variances) of
		no_call ->
			{Variances, Req, State};
		{HandlerVariances, Req2, HandlerState} ->
			{Variances ++ HandlerVariances, Req2,
				State#state{handler_state=HandlerState}}
	end.

resource_exists(Req, State) ->
	expect(Req, State, resource_exists, true,
		fun if_match_exists/2, fun if_match_must_not_exist/2).

if_match_exists(Req, State) ->
	State2 = State#state{exists=true},
	case cowboy_req:parse_header(<<"if-match">>, Req) of
		{ok, undefined, Req2} ->
			if_unmodified_since_exists(Req2, State2);
		{ok, '*', Req2} ->
			if_unmodified_since_exists(Req2, State2);
		{ok, ETagsList, Req2} ->
			if_match(Req2, State2, ETagsList);
		{error, badarg} ->
			respond(Req, State2, 400)
	end.

if_match(Req, State, EtagsList) ->
	try generate_etag(Req, State) of
		{Etag, Req2, State2} ->
			case lists:member(Etag, EtagsList) of
				true -> if_unmodified_since_exists(Req2, State2);
				%% Etag may be `undefined' which cannot be a member.
				false -> precondition_failed(Req2, State2)
			end
	catch Class:Reason ->
		error_terminate(Req, State, Class, Reason, generate_etag)
	end.

if_match_must_not_exist(Req, State) ->
	case cowboy_req:header(<<"if-match">>, Req) of
		{undefined, Req2} -> is_put_to_missing_resource(Req2, State);
		{_Any, Req2} -> precondition_failed(Req2, State)
	end.

if_unmodified_since_exists(Req, State) ->
	case cowboy_req:parse_header(<<"if-unmodified-since">>, Req) of
		{ok, undefined, Req2} ->
			if_none_match_exists(Req2, State);
		{ok, IfUnmodifiedSince, Req2} ->
			if_unmodified_since(Req2, State, IfUnmodifiedSince);
		{error, badarg} ->
			if_none_match_exists(Req, State)
	end.

%% If LastModified is the atom 'no_call', we continue.
if_unmodified_since(Req, State, IfUnmodifiedSince) ->
	try last_modified(Req, State) of
		{LastModified, Req2, State2} ->
			case LastModified > IfUnmodifiedSince of
				true -> precondition_failed(Req2, State2);
				false -> if_none_match_exists(Req2, State2)
			end
	catch Class:Reason ->
		error_terminate(Req, State, Class, Reason, last_modified)
	end.

if_none_match_exists(Req, State) ->
	case cowboy_req:parse_header(<<"if-none-match">>, Req) of
		{ok, undefined, Req2} ->
			if_modified_since_exists(Req2, State);
		{ok, '*', Req2} ->
			precondition_is_head_get(Req2, State);
		{ok, EtagsList, Req2} ->
			if_none_match(Req2, State, EtagsList);
		{error, badarg} ->
			respond(Req, State, 400)
	end.

if_none_match(Req, State, EtagsList) ->
	try generate_etag(Req, State) of
		{Etag, Req2, State2} ->
			case Etag of
				undefined ->
					precondition_failed(Req2, State2);
				Etag ->
					case lists:member(Etag, EtagsList) of
						true -> precondition_is_head_get(Req2, State2);
						false -> if_modified_since_exists(Req2, State2)
					end
			end
	catch Class:Reason ->
		error_terminate(Req, State, Class, Reason, generate_etag)
	end.

precondition_is_head_get(Req, State=#state{method=Method})
		when Method =:= <<"HEAD">>; Method =:= <<"GET">> ->
	not_modified(Req, State);
precondition_is_head_get(Req, State) ->
	precondition_failed(Req, State).

if_modified_since_exists(Req, State) ->
	case cowboy_req:parse_header(<<"if-modified-since">>, Req) of
		{ok, undefined, Req2} ->
			method(Req2, State);
		{ok, IfModifiedSince, Req2} ->
			if_modified_since_now(Req2, State, IfModifiedSince);
		{error, badarg} ->
			method(Req, State)
	end.

if_modified_since_now(Req, State, IfModifiedSince) ->
	case IfModifiedSince > erlang:universaltime() of
		true -> method(Req, State);
		false -> if_modified_since(Req, State, IfModifiedSince)
	end.

if_modified_since(Req, State, IfModifiedSince) ->
	try last_modified(Req, State) of
		{no_call, Req2, State2} ->
			method(Req2, State2);
		{LastModified, Req2, State2} ->
			case LastModified > IfModifiedSince of
				true -> method(Req2, State2);
				false -> not_modified(Req2, State2)
			end
	catch Class:Reason ->
		error_terminate(Req, State, Class, Reason, last_modified)
	end.

not_modified(Req, State) ->
	Req2 = cowboy_req:delete_resp_header(<<"content-type">>, Req),
	try set_resp_etag(Req2, State) of
		{Req3, State2} ->
			try set_resp_expires(Req3, State2) of
				{Req4, State3} ->
					respond(Req4, State3, 304)
			catch Class:Reason ->
				error_terminate(Req, State, Class, Reason, expires)
			end
	catch Class:Reason ->
		error_terminate(Req, State, Class, Reason, generate_etag)
	end.

precondition_failed(Req, State) ->
	respond(Req, State, 412).

is_put_to_missing_resource(Req, State=#state{method= <<"PUT">>}) ->
	moved_permanently(Req, State, fun is_conflict/2);
is_put_to_missing_resource(Req, State) ->
	previously_existed(Req, State).

%% moved_permanently/2 should return either false or {true, Location}
%% with Location the full new URI of the resource.
moved_permanently(Req, State, OnFalse) ->
	case call(Req, State, moved_permanently) of
		{{true, Location}, Req2, HandlerState} ->
			Req3 = cowboy_req:set_resp_header(
				<<"location">>, Location, Req2),
			respond(Req3, State#state{handler_state=HandlerState}, 301);
		{false, Req2, HandlerState} ->
			OnFalse(Req2, State#state{handler_state=HandlerState});
		{halt, Req2, HandlerState} ->
			terminate(Req2, State#state{handler_state=HandlerState});
		no_call ->
			OnFalse(Req, State)
	end.

previously_existed(Req, State) ->
	expect(Req, State, previously_existed, false,
		fun (R, S) -> is_post_to_missing_resource(R, S, 404) end,
		fun (R, S) -> moved_permanently(R, S, fun moved_temporarily/2) end).

%% moved_temporarily/2 should return either false or {true, Location}
%% with Location the full new URI of the resource.
moved_temporarily(Req, State) ->
	case call(Req, State, moved_temporarily) of
		{{true, Location}, Req2, HandlerState} ->
			Req3 = cowboy_req:set_resp_header(
				<<"location">>, Location, Req2),
			respond(Req3, State#state{handler_state=HandlerState}, 307);
		{false, Req2, HandlerState} ->
			is_post_to_missing_resource(Req2, State#state{handler_state=HandlerState}, 410);
		{halt, Req2, HandlerState} ->
			terminate(Req2, State#state{handler_state=HandlerState});
		no_call ->
			is_post_to_missing_resource(Req, State, 410)
	end.

is_post_to_missing_resource(Req, State=#state{method= <<"POST">>}, OnFalse) ->
	allow_missing_post(Req, State, OnFalse);
is_post_to_missing_resource(Req, State, OnFalse) ->
	respond(Req, State, OnFalse).

allow_missing_post(Req, State, OnFalse) ->
	expect(Req, State, allow_missing_post, true, fun accept_resource/2, OnFalse).

method(Req, State=#state{method= <<"DELETE">>}) ->
	delete_resource(Req, State);
method(Req, State=#state{method= <<"PUT">>}) ->
	is_conflict(Req, State);
method(Req, State=#state{method=Method})
		when Method =:= <<"POST">>; Method =:= <<"PATCH">> ->
	accept_resource(Req, State);
method(Req, State=#state{method=Method})
		when Method =:= <<"GET">>; Method =:= <<"HEAD">> ->
	set_resp_body_etag(Req, State);
method(Req, State) ->
	multiple_choices(Req, State).

%% delete_resource/2 should start deleting the resource and return.
delete_resource(Req, State) ->
	expect(Req, State, delete_resource, false, 500, fun delete_completed/2).

%% delete_completed/2 indicates whether the resource has been deleted yet.
delete_completed(Req, State) ->
	expect(Req, State, delete_completed, true, fun has_resp_body/2, 202).

is_conflict(Req, State) ->
	expect(Req, State, is_conflict, false, fun accept_resource/2, 409).

%% content_types_accepted should return a list of media types and their
%% associated callback functions in the same format as content_types_provided.
%%
%% The callback will then be called and is expected to process the content
%% pushed to the resource in the request body.
%%
%% content_types_accepted SHOULD return a different list
%% for each HTTP method.
accept_resource(Req, State) ->
	case call(Req, State, content_types_accepted) of
		no_call ->
			respond(Req, State, 415);
		{halt, Req2, HandlerState} ->
			terminate(Req2, State#state{handler_state=HandlerState});
		{CTA, Req2, HandlerState} ->
			CTA2 = [normalize_content_types(P) || P <- CTA],
			State2 = State#state{handler_state=HandlerState},
			case cowboy_req:parse_header(<<"content-type">>, Req2) of
				{ok, ContentType, Req3} ->
					choose_content_type(Req3, State2, ContentType, CTA2);
				{error, badarg} ->
					respond(Req2, State2, 415)
			end
	end.

%% The special content type '*' will always match. It can be used as a
%% catch-all content type for accepting any kind of request content.
%% Note that because it will always match, it should be the last of the
%% list of content types, otherwise it'll shadow the ones following.
choose_content_type(Req, State, _ContentType, []) ->
	respond(Req, State, 415);
choose_content_type(Req, State, ContentType, [{Accepted, Fun}|_Tail])
		when Accepted =:= '*'; Accepted =:= ContentType ->
	process_content_type(Req, State, Fun);
%% The special parameter '*' will always match any kind of content type
%% parameters.
%% Note that because it will always match, it should be the last of the
%% list for specific content type, otherwise it'll shadow the ones following.
choose_content_type(Req, State, {Type, SubType, Param},
		[{{Type, SubType, AcceptedParam}, Fun}|_Tail])
		when AcceptedParam =:= '*'; AcceptedParam =:= Param ->
	process_content_type(Req, State, Fun);
choose_content_type(Req, State, ContentType, [_Any|Tail]) ->
	choose_content_type(Req, State, ContentType, Tail).

process_content_type(Req, State=#state{method=Method, exists=Exists}, Fun) ->
	try case call(Req, State, Fun) of
		{halt, Req2, HandlerState2} ->
			terminate(Req2, State#state{handler_state=HandlerState2});
		{true, Req2, HandlerState2} when Exists ->
			State2 = State#state{handler_state=HandlerState2},
			next(Req2, State2, fun has_resp_body/2);
		{true, Req2, HandlerState2} ->
			State2 = State#state{handler_state=HandlerState2},
			next(Req2, State2, fun maybe_created/2);
		{false, Req2, HandlerState2} ->
			State2 = State#state{handler_state=HandlerState2},
			respond(Req2, State2, 400);
		{{true, ResURL}, Req2, HandlerState2} when Method =:= <<"POST">> ->
			State2 = State#state{handler_state=HandlerState2},
			Req3 = cowboy_req:set_resp_header(
				<<"location">>, ResURL, Req2),
			if
				Exists -> respond(Req3, State2, 303);
				true -> respond(Req3, State2, 201)
			end
	end catch Class:Reason = {case_clause, no_call} ->
		error_terminate(Req, State, Class, Reason, Fun)
	end.

%% If PUT was used then the resource has been created at the current URL.
%% Otherwise, if a location header has been set then the resource has been
%% created at a new URL. If not, send a 200 or 204 as expected from a
%% POST or PATCH request.
maybe_created(Req, State=#state{method= <<"PUT">>}) ->
	respond(Req, State, 201);
maybe_created(Req, State) ->
	case cowboy_req:has_resp_header(<<"location">>, Req) of
		true -> respond(Req, State, 201);
		false -> has_resp_body(Req, State)
	end.

has_resp_body(Req, State) ->
	case cowboy_req:has_resp_body(Req) of
		true -> multiple_choices(Req, State);
		false -> respond(Req, State, 204)
	end.

%% Set the Etag header if any for the response provided.
set_resp_body_etag(Req, State) ->
	try set_resp_etag(Req, State) of
		{Req2, State2} ->
			set_resp_body_last_modified(Req2, State2)
	catch Class:Reason ->
		error_terminate(Req, State, Class, Reason, generate_etag)
	end.

%% Set the Last-Modified header if any for the response provided.
set_resp_body_last_modified(Req, State) ->
	try last_modified(Req, State) of
		{LastModified, Req2, State2} ->
			case LastModified of
				LastModified when is_atom(LastModified) ->
					set_resp_body_expires(Req2, State2);
				LastModified ->
					LastModifiedBin = cowboy_clock:rfc1123(LastModified),
					Req3 = cowboy_req:set_resp_header(
						<<"last-modified">>, LastModifiedBin, Req2),
					set_resp_body_expires(Req3, State2)
			end
	catch Class:Reason ->
		error_terminate(Req, State, Class, Reason, last_modified)
	end.

%% Set the Expires header if any for the response provided.
set_resp_body_expires(Req, State) ->
	try set_resp_expires(Req, State) of
		{Req2, State2} ->
			set_resp_body(Req2, State2)
	catch Class:Reason ->
		error_terminate(Req, State, Class, Reason, expires)
	end.

%% Set the response headers and call the callback found using
%% content_types_provided/2 to obtain the request body and add
%% it to the response.
set_resp_body(Req, State=#state{content_type_a={_, Callback}}) ->
	try case call(Req, State, Callback) of
		{halt, Req2, HandlerState2} ->
			terminate(Req2, State#state{handler_state=HandlerState2});
		{Body, Req2, HandlerState2} ->
			State2 = State#state{handler_state=HandlerState2},
			Req3 = case Body of
				{stream, StreamFun} ->
					cowboy_req:set_resp_body_fun(StreamFun, Req2);
				{stream, Len, StreamFun} ->
					cowboy_req:set_resp_body_fun(Len, StreamFun, Req2);
				{chunked, StreamFun} ->
					cowboy_req:set_resp_body_fun(chunked, StreamFun, Req2);
				_Contents ->
					cowboy_req:set_resp_body(Body, Req2)
			end,
			multiple_choices(Req3, State2)
	end catch Class:Reason = {case_clause, no_call} ->
		error_terminate(Req, State, Class, Reason, Callback)
	end.

multiple_choices(Req, State) ->
	expect(Req, State, multiple_choices, false, 200, 300).

%% Response utility functions.

set_resp_etag(Req, State) ->
	{Etag, Req2, State2} = generate_etag(Req, State),
	case Etag of
		undefined ->
			{Req2, State2};
		Etag ->
			Req3 = cowboy_req:set_resp_header(
				<<"etag">>, encode_etag(Etag), Req2),
			{Req3, State2}
	end.

-spec encode_etag({strong | weak, binary()}) -> iolist().
encode_etag({strong, Etag}) -> [$",Etag,$"];
encode_etag({weak, Etag}) -> ["W/\"",Etag,$"].

set_resp_expires(Req, State) ->
	{Expires, Req2, State2} = expires(Req, State),
	case Expires of
		Expires when is_atom(Expires) ->
			{Req2, State2};
		Expires when is_binary(Expires) ->
			Req3 = cowboy_req:set_resp_header(
				<<"expires">>, Expires, Req2),
			{Req3, State2};
		Expires ->
			ExpiresBin = cowboy_clock:rfc1123(Expires),
			Req3 = cowboy_req:set_resp_header(
				<<"expires">>, ExpiresBin, Req2),
			{Req3, State2}
	end.

%% Info retrieval. No logic.

generate_etag(Req, State=#state{etag=no_call}) ->
	{undefined, Req, State};
generate_etag(Req, State=#state{etag=undefined}) ->
	case unsafe_call(Req, State, generate_etag) of
		no_call ->
			{undefined, Req, State#state{etag=no_call}};
		{Etag, Req2, HandlerState} when is_binary(Etag) ->
			[Etag2] = cowboy_http:entity_tag_match(Etag),
			{Etag2, Req2, State#state{handler_state=HandlerState, etag=Etag2}};
		{Etag, Req2, HandlerState} ->
			{Etag, Req2, State#state{handler_state=HandlerState, etag=Etag}}
	end;
generate_etag(Req, State=#state{etag=Etag}) ->
	{Etag, Req, State}.

last_modified(Req, State=#state{last_modified=no_call}) ->
	{undefined, Req, State};
last_modified(Req, State=#state{last_modified=undefined}) ->
	case unsafe_call(Req, State, last_modified) of
		no_call ->
			{undefined, Req, State#state{last_modified=no_call}};
		{LastModified, Req2, HandlerState} ->
			{LastModified, Req2, State#state{handler_state=HandlerState,
				last_modified=LastModified}}
	end;
last_modified(Req, State=#state{last_modified=LastModified}) ->
	{LastModified, Req, State}.

expires(Req, State=#state{expires=no_call}) ->
	{undefined, Req, State};
expires(Req, State=#state{expires=undefined}) ->
	case unsafe_call(Req, State, expires) of
		no_call ->
			{undefined, Req, State#state{expires=no_call}};
		{Expires, Req2, HandlerState} ->
			{Expires, Req2, State#state{handler_state=HandlerState,
				expires=Expires}}
	end;
expires(Req, State=#state{expires=Expires}) ->
	{Expires, Req, State}.

%% REST primitives.

expect(Req, State, Callback, Expected, OnTrue, OnFalse) ->
	case call(Req, State, Callback) of
		no_call ->
			next(Req, State, OnTrue);
		{halt, Req2, HandlerState} ->
			terminate(Req2, State#state{handler_state=HandlerState});
		{Expected, Req2, HandlerState} ->
			next(Req2, State#state{handler_state=HandlerState}, OnTrue);
		{_Unexpected, Req2, HandlerState} ->
			next(Req2, State#state{handler_state=HandlerState}, OnFalse)
	end.

call(Req, State=#state{handler=Handler, handler_state=HandlerState},
		Callback) ->
	case erlang:function_exported(Handler, Callback, 2) of
		true ->
			try
				Handler:Callback(Req, HandlerState)
			catch Class:Reason ->
				error_terminate(Req, State, Class, Reason, Callback)
			end;
		false ->
			no_call
	end.

unsafe_call(Req, #state{handler=Handler, handler_state=HandlerState},
		Callback) ->
	case erlang:function_exported(Handler, Callback, 2) of
		true -> Handler:Callback(Req, HandlerState);
		false -> no_call
	end.

next(Req, State, Next) when is_function(Next) ->
	Next(Req, State);
next(Req, State, StatusCode) when is_integer(StatusCode) ->
	respond(Req, State, StatusCode).

respond(Req, State, StatusCode) ->
	{ok, Req2} = cowboy_req:reply(StatusCode, Req),
	terminate(Req2, State).

terminate(Req, State=#state{env=Env}) ->
	rest_terminate(Req, State),
	{ok, Req, [{result, ok}|Env]}.

error_terminate(Req, State=#state{handler=Handler, handler_state=HandlerState},
		Class, Reason, Callback) ->
	Stacktrace = erlang:get_stacktrace(),
	rest_terminate(Req, State),
	cowboy_req:maybe_reply(Stacktrace, Req),
	erlang:Class([
		{reason, Reason},
		{mfa, {Handler, Callback, 2}},
		{stacktrace, Stacktrace},
		{req, cowboy_req:to_list(Req)},
		{state, HandlerState}
	]).

rest_terminate(Req, #state{handler=Handler, handler_state=HandlerState}) ->
	case erlang:function_exported(Handler, rest_terminate, 2) of
		true -> ok = Handler:rest_terminate(
			cowboy_req:lock(Req), HandlerState);
		false -> ok
	end.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted deps/cowboy/src/cowboy_router.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
%% Copyright (c) 2011-2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

%% Routing middleware.
%%
%% Resolve the handler to be used for the request based on the
%% routing information found in the <em>dispatch</em> environment value.
%% When found, the handler module and associated data are added to
%% the environment as the <em>handler</em> and <em>handler_opts</em> values
%% respectively.
%%
%% If the route cannot be found, processing stops with either
%% a 400 or a 404 reply.
-module(cowboy_router).
-behaviour(cowboy_middleware).

-export([compile/1]).
-export([execute/2]).

-type bindings() :: [{atom(), binary()}].
-type tokens() :: [binary()].
-export_type([bindings/0]).
-export_type([tokens/0]).

-type constraints() :: [{atom(), int}
	| {atom(), function, fun ((binary()) -> true | {true, any()} | false)}].
-export_type([constraints/0]).

-type route_match() :: '_' | iodata().
-type route_path() :: {Path::route_match(), Handler::module(), Opts::any()}
	| {Path::route_match(), constraints(), Handler::module(), Opts::any()}.
-type route_rule() :: {Host::route_match(), Paths::[route_path()]}
	| {Host::route_match(), constraints(), Paths::[route_path()]}.
-type routes() :: [route_rule()].
-export_type([routes/0]).

-type dispatch_match() :: '_' | <<_:8>> | [binary() | '_' | '...' | atom()].
-type dispatch_path() :: {dispatch_match(), module(), any()}.
-type dispatch_rule() :: {Host::dispatch_match(), Paths::[dispatch_path()]}.
-opaque dispatch_rules() :: [dispatch_rule()].
-export_type([dispatch_rules/0]).

-spec compile(routes()) -> dispatch_rules().
compile(Routes) ->
	compile(Routes, []).

compile([], Acc) ->
	lists:reverse(Acc);
compile([{Host, Paths}|Tail], Acc) ->
	compile([{Host, [], Paths}|Tail], Acc);
compile([{HostMatch, Constraints, Paths}|Tail], Acc) ->
	HostRules = case HostMatch of
		'_' -> '_';
		_ -> compile_host(HostMatch)
	end,
	PathRules = compile_paths(Paths, []),
	Hosts = case HostRules of
		'_' -> [{'_', Constraints, PathRules}];
		_ -> [{R, Constraints, PathRules} || R <- HostRules]
	end,
	compile(Tail, Hosts ++ Acc).

compile_host(HostMatch) when is_list(HostMatch) ->
	compile_host(list_to_binary(HostMatch));
compile_host(HostMatch) when is_binary(HostMatch) ->
	compile_rules(HostMatch, $., [], [], <<>>).

compile_paths([], Acc) ->
	lists:reverse(Acc);
compile_paths([{PathMatch, Handler, Opts}|Tail], Acc) ->
	compile_paths([{PathMatch, [], Handler, Opts}|Tail], Acc);
compile_paths([{PathMatch, Constraints, Handler, Opts}|Tail], Acc)
		when is_list(PathMatch) ->
	compile_paths([{iolist_to_binary(PathMatch),
		Constraints, Handler, Opts}|Tail], Acc);
compile_paths([{'_', Constraints, Handler, Opts}|Tail], Acc) ->
	compile_paths(Tail, [{'_', Constraints, Handler, Opts}] ++ Acc);
compile_paths([{<< $/, PathMatch/binary >>, Constraints, Handler, Opts}|Tail],
		Acc) ->
	PathRules = compile_rules(PathMatch, $/, [], [], <<>>),
	Paths = [{lists:reverse(R), Constraints, Handler, Opts} || R <- PathRules],
	compile_paths(Tail, Paths ++ Acc);
compile_paths([{PathMatch, _, _, _}|_], _) ->
	error({badarg, "The following route MUST begin with a slash: "
		++ binary_to_list(PathMatch)}).

compile_rules(<<>>, _, Segments, Rules, <<>>) ->
	[Segments|Rules];
compile_rules(<<>>, _, Segments, Rules, Acc) ->
	[[Acc|Segments]|Rules];
compile_rules(<< S, Rest/binary >>, S, Segments, Rules, <<>>) ->
	compile_rules(Rest, S, Segments, Rules, <<>>);
compile_rules(<< S, Rest/binary >>, S, Segments, Rules, Acc) ->
	compile_rules(Rest, S, [Acc|Segments], Rules, <<>>);
compile_rules(<< $:, Rest/binary >>, S, Segments, Rules, <<>>) ->
	{NameBin, Rest2} = compile_binding(Rest, S, <<>>),
	Name = binary_to_atom(NameBin, utf8),
	compile_rules(Rest2, S, Segments, Rules, Name);
compile_rules(<< $:, _/binary >>, _, _, _, _) ->
	erlang:error(badarg);
compile_rules(<< $[, $., $., $., $], Rest/binary >>, S, Segments, Rules, Acc)
		when Acc =:= <<>> ->
	compile_rules(Rest, S, ['...'|Segments], Rules, Acc);
compile_rules(<< $[, $., $., $., $], Rest/binary >>, S, Segments, Rules, Acc) ->
	compile_rules(Rest, S, ['...', Acc|Segments], Rules, Acc);
compile_rules(<< $[, S, Rest/binary >>, S, Segments, Rules, Acc) ->
	compile_brackets(Rest, S, [Acc|Segments], Rules);
compile_rules(<< $[, Rest/binary >>, S, Segments, Rules, <<>>) ->
	compile_brackets(Rest, S, Segments, Rules);
%% Open bracket in the middle of a segment.
compile_rules(<< $[, _/binary >>, _, _, _, _) ->
	erlang:error(badarg);
%% Missing an open bracket.
compile_rules(<< $], _/binary >>, _, _, _, _) ->
	erlang:error(badarg);
compile_rules(<< C, Rest/binary >>, S, Segments, Rules, Acc) ->
	compile_rules(Rest, S, Segments, Rules, << Acc/binary, C >>).

%% Everything past $: until the segment separator ($. for hosts,
%% $/ for paths) or $[ or $] or end of binary is the binding name.
compile_binding(<<>>, _, <<>>) ->
	erlang:error(badarg);
compile_binding(Rest = <<>>, _, Acc) ->
	{Acc, Rest};
compile_binding(Rest = << C, _/binary >>, S, Acc)
		when C =:= S; C =:= $[; C =:= $] ->
	{Acc, Rest};
compile_binding(<< C, Rest/binary >>, S, Acc) ->
	compile_binding(Rest, S, << Acc/binary, C >>).

compile_brackets(Rest, S, Segments, Rules) ->
	{Bracket, Rest2} = compile_brackets_split(Rest, <<>>, 0),
	Rules1 = compile_rules(Rest2, S, Segments, [], <<>>),
	Rules2 = compile_rules(<< Bracket/binary, Rest2/binary >>,
		S, Segments, [], <<>>),
	Rules ++ Rules2 ++ Rules1.

%% Missing a close bracket.
compile_brackets_split(<<>>, _, _) ->
	erlang:error(badarg);
%% Make sure we don't confuse the closing bracket we're looking for.
compile_brackets_split(<< C, Rest/binary >>, Acc, N) when C =:= $[ ->
	compile_brackets_split(Rest, << Acc/binary, C >>, N + 1);
compile_brackets_split(<< C, Rest/binary >>, Acc, N) when C =:= $], N > 0 ->
	compile_brackets_split(Rest, << Acc/binary, C >>, N - 1);
%% That's the right one.
compile_brackets_split(<< $], Rest/binary >>, Acc, 0) ->
	{Acc, Rest};
compile_brackets_split(<< C, Rest/binary >>, Acc, N) ->
	compile_brackets_split(Rest, << Acc/binary, C >>, N).

-spec execute(Req, Env)
	-> {ok, Req, Env} | {error, 400 | 404, Req}
	when Req::cowboy_req:req(), Env::cowboy_middleware:env().
execute(Req, Env) ->
	{_, Dispatch} = lists:keyfind(dispatch, 1, Env),
	[Host, Path] = cowboy_req:get([host, path], Req),
	case match(Dispatch, Host, Path) of
		{ok, Handler, HandlerOpts, Bindings, HostInfo, PathInfo} ->
			Req2 = cowboy_req:set_bindings(HostInfo, PathInfo, Bindings, Req),
			{ok, Req2, [{handler, Handler}, {handler_opts, HandlerOpts}|Env]};
		{error, notfound, host} ->
			{error, 400, Req};
		{error, badrequest, path} ->
			{error, 400, Req};
		{error, notfound, path} ->
			{error, 404, Req}
	end.

%% Internal.

%% Match hostname tokens and path tokens against dispatch rules.
%%
%% It is typically used for matching tokens for the hostname and path of
%% the request against a global dispatch rule for your listener.
%%
%% Dispatch rules are a list of <em>{Hostname, PathRules}</em> tuples, with
%% <em>PathRules</em> being a list of <em>{Path, HandlerMod, HandlerOpts}</em>.
%%
%% <em>Hostname</em> and <em>Path</em> are match rules and can be either the
%% atom <em>'_'</em>, which matches everything, `<<"*">>', which match the
%% wildcard path, or a list of tokens.
%%
%% Each token can be either a binary, the atom <em>'_'</em>,
%% the atom '...' or a named atom. A binary token must match exactly,
%% <em>'_'</em> matches everything for a single token, <em>'...'</em> matches
%% everything for the rest of the tokens and a named atom will bind the
%% corresponding token value and return it.
%%
%% The list of hostname tokens is reversed before matching. For example, if
%% we were to match "www.ninenines.eu", we would first match "eu", then
%% "ninenines", then "www". This means that in the context of hostnames,
%% the <em>'...'</em> atom matches properly the lower levels of the domain
%% as would be expected.
%%
%% When a result is found, this function will return the handler module and
%% options found in the dispatch list, a key-value list of bindings and
%% the tokens that were matched by the <em>'...'</em> atom for both the
%% hostname and path.
-spec match(dispatch_rules(), Host::binary() | tokens(), Path::binary())
	-> {ok, module(), any(), bindings(),
		HostInfo::undefined | tokens(),
		PathInfo::undefined | tokens()}
	| {error, notfound, host} | {error, notfound, path}
	| {error, badrequest, path}.
match([], _, _) ->
	{error, notfound, host};
%% If the host is '_' then there can be no constraints.
match([{'_', [], PathMatchs}|_Tail], _, Path) ->
	match_path(PathMatchs, undefined, Path, []);
match([{HostMatch, Constraints, PathMatchs}|Tail], Tokens, Path)
		when is_list(Tokens) ->
	case list_match(Tokens, HostMatch, []) of
		false ->
			match(Tail, Tokens, Path);
		{true, Bindings, HostInfo} ->
			HostInfo2 = case HostInfo of
				undefined -> undefined;
				_ -> lists:reverse(HostInfo)
			end,
			case check_constraints(Constraints, Bindings) of
				{ok, Bindings2} ->
					match_path(PathMatchs, HostInfo2, Path, Bindings2);
				nomatch ->
					match(Tail, Tokens, Path)
			end
	end;
match(Dispatch, Host, Path) ->
	match(Dispatch, split_host(Host), Path).

-spec match_path([dispatch_path()],
	HostInfo::undefined | tokens(), binary() | tokens(), bindings())
	-> {ok, module(), any(), bindings(),
		HostInfo::undefined | tokens(),
		PathInfo::undefined | tokens()}
	| {error, notfound, path} | {error, badrequest, path}.
match_path([], _, _, _) ->
	{error, notfound, path};
%% If the path is '_' then there can be no constraints.
match_path([{'_', [], Handler, Opts}|_Tail], HostInfo, _, Bindings) ->
	{ok, Handler, Opts, Bindings, HostInfo, undefined};
match_path([{<<"*">>, _Constraints, Handler, Opts}|_Tail], HostInfo, <<"*">>, Bindings) ->
	{ok, Handler, Opts, Bindings, HostInfo, undefined};
match_path([{PathMatch, Constraints, Handler, Opts}|Tail], HostInfo, Tokens,
		Bindings) when is_list(Tokens) ->
	case list_match(Tokens, PathMatch, Bindings) of
		false ->
			match_path(Tail, HostInfo, Tokens, Bindings);
		{true, PathBinds, PathInfo} ->
			case check_constraints(Constraints, PathBinds) of
				{ok, PathBinds2} ->
					{ok, Handler, Opts, PathBinds2, HostInfo, PathInfo};
				nomatch ->
					match_path(Tail, HostInfo, Tokens, Bindings)
			end
	end;
match_path(_Dispatch, _HostInfo, badrequest, _Bindings) ->
	{error, badrequest, path};
match_path(Dispatch, HostInfo, Path, Bindings) ->
	match_path(Dispatch, HostInfo, split_path(Path), Bindings).

check_constraints([], Bindings) ->
	{ok, Bindings};
check_constraints([Constraint|Tail], Bindings) ->
	Name = element(1, Constraint),
	case lists:keyfind(Name, 1, Bindings) of
		false ->
			check_constraints(Tail, Bindings);
		{_, Value} ->
			case check_constraint(Constraint, Value) of
				true ->
					check_constraints(Tail, Bindings);
				{true, Value2} ->
					Bindings2 = lists:keyreplace(Name, 1, Bindings,
						{Name, Value2}),
					check_constraints(Tail, Bindings2);
				false ->
					nomatch
			end
	end.

check_constraint({_, int}, Value) ->
	try {true, list_to_integer(binary_to_list(Value))}
	catch _:_ -> false
	end;
check_constraint({_, function, Fun}, Value) ->
	Fun(Value).

-spec split_host(binary()) -> tokens().
split_host(Host) ->
	split_host(Host, []).

split_host(Host, Acc) ->
	case binary:match(Host, <<".">>) of
		nomatch when Host =:= <<>> ->
			Acc;
		nomatch ->
			[Host|Acc];
		{Pos, _} ->
			<< Segment:Pos/binary, _:8, Rest/bits >> = Host,
			false = byte_size(Segment) == 0,
			split_host(Rest, [Segment|Acc])
	end.

%% Following RFC2396, this function may return path segments containing any
%% character, including <em>/</em> if, and only if, a <em>/</em> was escaped
%% and part of a path segment.
-spec split_path(binary()) -> tokens().
split_path(<< $/, Path/bits >>) ->
	split_path(Path, []);
split_path(_) ->
	badrequest.

split_path(Path, Acc) ->
	try
		case binary:match(Path, <<"/">>) of
			nomatch when Path =:= <<>> ->
				lists:reverse([cow_qs:urldecode(S) || S <- Acc]);
			nomatch ->
				lists:reverse([cow_qs:urldecode(S) || S <- [Path|Acc]]);
			{Pos, _} ->
				<< Segment:Pos/binary, _:8, Rest/bits >> = Path,
				split_path(Rest, [Segment|Acc])
		end
	catch
		error:badarg ->
			badrequest
	end.

-spec list_match(tokens(), dispatch_match(), bindings())
	-> {true, bindings(), undefined | tokens()} | false.
%% Atom '...' matches any trailing path, stop right now.
list_match(List, ['...'], Binds) ->
	{true, Binds, List};
%% Atom '_' matches anything, continue.
list_match([_E|Tail], ['_'|TailMatch], Binds) ->
	list_match(Tail, TailMatch, Binds);
%% Both values match, continue.
list_match([E|Tail], [E|TailMatch], Binds) ->
	list_match(Tail, TailMatch, Binds);
%% Bind E to the variable name V and continue,
%% unless V was already defined and E isn't identical to the previous value.
list_match([E|Tail], [V|TailMatch], Binds) when is_atom(V) ->
	case lists:keyfind(V, 1, Binds) of
		{_, E} ->
			list_match(Tail, TailMatch, Binds);
		{_, _} ->
			false;
		false ->
			list_match(Tail, TailMatch, [{V, E}|Binds])
	end;
%% Match complete.
list_match([], [], Binds) ->
	{true, Binds, undefined};
%% Values don't match, stop.
list_match(_List, _Match, _Binds) ->
	false.

%% Tests.

-ifdef(TEST).
compile_test_() ->
	Tests = [
		%% Match any host and path.
		{[{'_', [{'_', h, o}]}],
			[{'_', [], [{'_', [], h, o}]}]},
		{[{"cowboy.example.org",
				[{"/", ha, oa}, {"/path/to/resource", hb, ob}]}],
			[{[<<"org">>, <<"example">>, <<"cowboy">>], [], [
				{[], [], ha, oa},
				{[<<"path">>, <<"to">>, <<"resource">>], [], hb, ob}]}]},
		{[{'_', [{"/path/to/resource/", h, o}]}],
			[{'_', [], [{[<<"path">>, <<"to">>, <<"resource">>], [], h, o}]}]},
		% Cyrillic from a latin1 encoded file.
		{[{'_', [{[47,208,191,209,131,209,130,209,140,47,208,186,47,209,128,
				208,181,209,129,209,131,209,128,209,129,209,131,47], h, o}]}],
			[{'_', [], [{[<<208,191,209,131,209,130,209,140>>, <<208,186>>,
				<<209,128,208,181,209,129,209,131,209,128,209,129,209,131>>],
				[], h, o}]}]},
		{[{"cowboy.example.org.", [{'_', h, o}]}],
			[{[<<"org">>, <<"example">>, <<"cowboy">>], [], [{'_', [], h, o}]}]},
		{[{".cowboy.example.org", [{'_', h, o}]}],
			[{[<<"org">>, <<"example">>, <<"cowboy">>], [], [{'_', [], h, o}]}]},
		% Cyrillic from a latin1 encoded file.
		{[{[208,189,208,181,208,186,208,184,208,185,46,209,129,208,176,
				208,185,209,130,46,209,128,209,132,46], [{'_', h, o}]}],
			[{[<<209,128,209,132>>, <<209,129,208,176,208,185,209,130>>,
				<<208,189,208,181,208,186,208,184,208,185>>],
				[], [{'_', [], h, o}]}]},
		{[{":subdomain.example.org", [{"/hats/:name/prices", h, o}]}],
			[{[<<"org">>, <<"example">>, subdomain], [], [
				{[<<"hats">>, name, <<"prices">>], [], h, o}]}]},
		{[{"ninenines.:_", [{"/hats/:_", h, o}]}],
			[{['_', <<"ninenines">>], [], [{[<<"hats">>, '_'], [], h, o}]}]},
		{[{"[www.]ninenines.eu",
			[{"/horses", h, o}, {"/hats/[page/:number]", h, o}]}], [
				{[<<"eu">>, <<"ninenines">>], [], [
					{[<<"horses">>], [], h, o},
					{[<<"hats">>], [], h, o},
					{[<<"hats">>, <<"page">>, number], [], h, o}]},
				{[<<"eu">>, <<"ninenines">>, <<"www">>], [], [
					{[<<"horses">>], [], h, o},
					{[<<"hats">>], [], h, o},
					{[<<"hats">>, <<"page">>, number], [], h, o}]}]},
		{[{'_', [{"/hats/[page/[:number]]", h, o}]}], [{'_', [], [
			{[<<"hats">>], [], h, o},
			{[<<"hats">>, <<"page">>], [], h, o},
			{[<<"hats">>, <<"page">>, number], [], h, o}]}]},
		{[{"[...]ninenines.eu", [{"/hats/[...]", h, o}]}],
			[{[<<"eu">>, <<"ninenines">>, '...'], [], [
				{[<<"hats">>, '...'], [], h, o}]}]}
	],
	[{lists:flatten(io_lib:format("~p", [Rt])),
		fun() -> Rs = compile(Rt) end} || {Rt, Rs} <- Tests].

split_host_test_() ->
	Tests = [
		{<<"">>, []},
		{<<"*">>, [<<"*">>]},
		{<<"cowboy.ninenines.eu">>,
			[<<"eu">>, <<"ninenines">>, <<"cowboy">>]},
		{<<"ninenines.eu">>,
			[<<"eu">>, <<"ninenines">>]},
		{<<"a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z">>,
			[<<"z">>, <<"y">>, <<"x">>, <<"w">>, <<"v">>, <<"u">>, <<"t">>,
			<<"s">>, <<"r">>, <<"q">>, <<"p">>, <<"o">>, <<"n">>, <<"m">>,
			<<"l">>, <<"k">>, <<"j">>, <<"i">>, <<"h">>, <<"g">>, <<"f">>,
			<<"e">>, <<"d">>, <<"c">>, <<"b">>, <<"a">>]}
	],
	[{H, fun() -> R = split_host(H) end} || {H, R} <- Tests].

split_path_test_() ->
	Tests = [
		{<<"/">>, []},
		{<<"/extend//cowboy">>, [<<"extend">>, <<>>, <<"cowboy">>]},
		{<<"/users">>, [<<"users">>]},
		{<<"/users/42/friends">>, [<<"users">>, <<"42">>, <<"friends">>]},
		{<<"/users/a+b/c%21d">>, [<<"users">>, <<"a b">>, <<"c!d">>]}
	],
	[{P, fun() -> R = split_path(P) end} || {P, R} <- Tests].

match_test_() ->
	Dispatch = [
		{[<<"eu">>, <<"ninenines">>, '_', <<"www">>], [], [
			{[<<"users">>, '_', <<"mails">>], [], match_any_subdomain_users, []}
		]},
		{[<<"eu">>, <<"ninenines">>], [], [
			{[<<"users">>, id, <<"friends">>], [], match_extend_users_friends, []},
			{'_', [], match_extend, []}
		]},
		{[var, <<"ninenines">>], [], [
			{[<<"threads">>, var], [], match_duplicate_vars,
				[we, {expect, two}, var, here]}
		]},
		{[ext, <<"erlang">>], [], [
			{'_', [], match_erlang_ext, []}
		]},
		{'_', [], [
			{[<<"users">>, id, <<"friends">>], [], match_users_friends, []},
			{'_', [], match_any, []}
		]}
	],
	Tests = [
		{<<"any">>, <<"/">>, {ok, match_any, [], []}},
		{<<"www.any.ninenines.eu">>, <<"/users/42/mails">>,
			{ok, match_any_subdomain_users, [], []}},
		{<<"www.ninenines.eu">>, <<"/users/42/mails">>,
			{ok, match_any, [], []}},
		{<<"www.ninenines.eu">>, <<"/">>,
			{ok, match_any, [], []}},
		{<<"www.any.ninenines.eu">>, <<"/not_users/42/mails">>,
			{error, notfound, path}},
		{<<"ninenines.eu">>, <<"/">>,
			{ok, match_extend, [], []}},
		{<<"ninenines.eu">>, <<"/users/42/friends">>,
			{ok, match_extend_users_friends, [], [{id, <<"42">>}]}},
		{<<"erlang.fr">>, '_',
			{ok, match_erlang_ext, [], [{ext, <<"fr">>}]}},
		{<<"any">>, <<"/users/444/friends">>,
			{ok, match_users_friends, [], [{id, <<"444">>}]}}
	],
	[{lists:flatten(io_lib:format("~p, ~p", [H, P])), fun() ->
		{ok, Handler, Opts, Binds, undefined, undefined}
			= match(Dispatch, H, P)
	end} || {H, P, {ok, Handler, Opts, Binds}} <- Tests].

match_info_test_() ->
	Dispatch = [
		{[<<"eu">>, <<"ninenines">>, <<"www">>], [], [
			{[<<"pathinfo">>, <<"is">>, <<"next">>, '...'], [], match_path, []}
		]},
		{[<<"eu">>, <<"ninenines">>, '...'], [], [
			{'_', [], match_any, []}
		]},
		% Cyrillic from a latin1 encoded file.
		{[<<209,128,209,132>>, <<209,129,208,176,208,185,209,130>>], [], [
			{[<<208,191,209,131,209,130,209,140>>, '...'], [], match_path, []}
		]}
	],
	Tests = [
		{<<"ninenines.eu">>, <<"/">>,
			{ok, match_any, [], [], [], undefined}},
		{<<"bugs.ninenines.eu">>, <<"/">>,
			{ok, match_any, [], [], [<<"bugs">>], undefined}},
		{<<"cowboy.bugs.ninenines.eu">>, <<"/">>,
			{ok, match_any, [], [], [<<"cowboy">>, <<"bugs">>], undefined}},
		{<<"www.ninenines.eu">>, <<"/pathinfo/is/next">>,
			{ok, match_path, [], [], undefined, []}},
		{<<"www.ninenines.eu">>, <<"/pathinfo/is/next/path_info">>,
			{ok, match_path, [], [], undefined, [<<"path_info">>]}},
		{<<"www.ninenines.eu">>, <<"/pathinfo/is/next/foo/bar">>,
			{ok, match_path, [], [], undefined, [<<"foo">>, <<"bar">>]}},
		% Cyrillic from a latin1 encoded file.
		{<<209,129,208,176,208,185,209,130,46,209,128,209,132>>,
			<<47,208,191,209,131,209,130,209,140,47,208,180,208,190,208,188,208,190,208,185>>,
			{ok, match_path, [], [], undefined, [<<208,180,208,190,208,188,208,190,208,185>>]}}
	],
	[{lists:flatten(io_lib:format("~p, ~p", [H, P])), fun() ->
		R = match(Dispatch, H, P)
	end} || {H, P, R} <- Tests].

match_constraints_test() ->
	Dispatch = [{'_', [],
		[{[<<"path">>, value], [{value, int}], match, []}]}],
	{ok, _, [], [{value, 123}], _, _} = match(Dispatch,
		<<"ninenines.eu">>, <<"/path/123">>),
	{ok, _, [], [{value, 123}], _, _} = match(Dispatch,
		<<"ninenines.eu">>, <<"/path/123/">>),
	{error, notfound, path} = match(Dispatch,
		<<"ninenines.eu">>, <<"/path/NaN/">>),
	Dispatch2 = [{'_', [],
		[{[<<"path">>, username], [{username, function,
		fun(Value) -> Value =:= cowboy_bstr:to_lower(Value) end}],
		match, []}]}],
	{ok, _, [], [{username, <<"essen">>}], _, _} = match(Dispatch2,
		<<"ninenines.eu">>, <<"/path/essen">>),
	{error, notfound, path} = match(Dispatch2,
		<<"ninenines.eu">>, <<"/path/ESSEN">>),
	ok.

match_same_bindings_test() ->
	Dispatch = [{[same, same], [], [{'_', [], match, []}]}],
	{ok, _, [], [{same, <<"eu">>}], _, _} = match(Dispatch,
		<<"eu.eu">>, <<"/">>),
	{error, notfound, host} = match(Dispatch,
		<<"ninenines.eu">>, <<"/">>),
	Dispatch2 = [{[<<"eu">>, <<"ninenines">>, user], [],
		[{[<<"path">>, user], [], match, []}]}],
	{ok, _, [], [{user, <<"essen">>}], _, _} = match(Dispatch2,
		<<"essen.ninenines.eu">>, <<"/path/essen">>),
	{ok, _, [], [{user, <<"essen">>}], _, _} = match(Dispatch2,
		<<"essen.ninenines.eu">>, <<"/path/essen/">>),
	{error, notfound, path} = match(Dispatch2,
		<<"essen.ninenines.eu">>, <<"/path/notessen">>),
	Dispatch3 = [{'_', [], [{[same, same], [], match, []}]}],
	{ok, _, [], [{same, <<"path">>}], _, _} = match(Dispatch3,
		<<"ninenines.eu">>, <<"/path/path">>),
	{error, notfound, path} = match(Dispatch3,
		<<"ninenines.eu">>, <<"/path/to">>),
	ok.
-endif.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted deps/cowboy/src/cowboy_spdy.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
%% Copyright (c) 2013-2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(cowboy_spdy).

%% API.
-export([start_link/4]).

%% Internal.
-export([init/5]).
-export([system_continue/3]).
-export([system_terminate/4]).
-export([system_code_change/4]).

%% Internal request process.
-export([request_init/11]).
-export([resume/5]).
-export([reply/4]).
-export([stream_reply/3]).
-export([stream_data/2]).
-export([stream_close/1]).

%% Internal transport functions.
-export([name/0]).
-export([messages/0]).
-export([recv/3]).
-export([send/2]).
-export([sendfile/2]).
-export([setopts/2]).

-type streamid() :: non_neg_integer().
-type socket() :: {pid(), streamid()}.

-record(child, {
	streamid :: streamid(),
	pid :: pid(),
	input = nofin :: fin | nofin,
	in_buffer = <<>> :: binary(),
	is_recv = false :: false | {active, socket(), pid()}
		| {passive, socket(), pid(), non_neg_integer(), reference()},
	output = nofin :: fin | nofin
}).

-record(state, {
	parent = undefined :: pid(),
	socket,
	transport,
	buffer = <<>> :: binary(),
	middlewares,
	env,
	onrequest,
	onresponse,
	peer,
	zdef,
	zinf,
	last_streamid = 0 :: streamid(),
	children = [] :: [#child{}]
}).

-type opts() :: [{env, cowboy_middleware:env()}
	| {middlewares, [module()]}
	| {onrequest, cowboy:onrequest_fun()}
	| {onresponse, cowboy:onresponse_fun()}].
-export_type([opts/0]).

%% API.

-spec start_link(any(), inet:socket(), module(), any()) -> {ok, pid()}.
start_link(Ref, Socket, Transport, Opts) ->
	proc_lib:start_link(?MODULE, init,
		[self(), Ref, Socket, Transport, Opts]).

%% Internal.

%% Faster alternative to proplists:get_value/3.
get_value(Key, Opts, Default) ->
	case lists:keyfind(Key, 1, Opts) of
		{_, Value} -> Value;
		_ -> Default
	end.

-spec init(pid(), ranch:ref(), inet:socket(), module(), opts()) -> ok.
init(Parent, Ref, Socket, Transport, Opts) ->
	process_flag(trap_exit, true),
	ok = proc_lib:init_ack(Parent, {ok, self()}),
	{ok, Peer} = Transport:peername(Socket),
	Middlewares = get_value(middlewares, Opts, [cowboy_router, cowboy_handler]),
	Env = [{listener, Ref}|get_value(env, Opts, [])],
	OnRequest = get_value(onrequest, Opts, undefined),
	OnResponse = get_value(onresponse, Opts, undefined),
	Zdef = cow_spdy:deflate_init(),
	Zinf = cow_spdy:inflate_init(),
	ok = ranch:accept_ack(Ref),
	loop(#state{parent=Parent, socket=Socket, transport=Transport,
		middlewares=Middlewares, env=Env, onrequest=OnRequest,
		onresponse=OnResponse, peer=Peer, zdef=Zdef, zinf=Zinf}).

loop(State=#state{parent=Parent, socket=Socket, transport=Transport,
		buffer=Buffer, children=Children}) ->
	{OK, Closed, Error} = Transport:messages(),
	Transport:setopts(Socket, [{active, once}]),
	receive
		{OK, Socket, Data} ->
			parse_frame(State, << Buffer/binary, Data/binary >>);
		{Closed, Socket} ->
			terminate(State);
		{Error, Socket, _Reason} ->
			terminate(State);
		{recv, FromSocket = {Pid, StreamID}, FromPid, Length, Timeout}
				when Pid =:= self() ->
			Child = #child{in_buffer=InBuffer, is_recv=false}
				= get_child(StreamID, State),
			if
				Length =:= 0, InBuffer =/= <<>> ->
					FromPid ! {recv, FromSocket, {ok, InBuffer}},
					loop(replace_child(Child#child{in_buffer= <<>>}, State));
				byte_size(InBuffer) >= Length ->
					<< Data:Length/binary, Rest/binary >> = InBuffer,
					FromPid ! {recv, FromSocket, {ok, Data}},
					loop(replace_child(Child#child{in_buffer=Rest}, State));
				true ->
					TRef = erlang:send_after(Timeout, self(),
						{recv_timeout, FromSocket}),
					loop(replace_child(Child#child{
						is_recv={passive, FromSocket, FromPid, Length, TRef}},
						State))
			end;
		{recv_timeout, {Pid, StreamID}}
				when Pid =:= self() ->
			Child = #child{is_recv={passive, FromSocket, FromPid, _, _}}
				= get_child(StreamID, State),
			FromPid ! {recv, FromSocket, {error, timeout}},
			loop(replace_child(Child, State));
		{reply, {Pid, StreamID}, Status, Headers}
				when Pid =:= self() ->
			Child = #child{output=nofin} = get_child(StreamID, State),
			syn_reply(State, StreamID, true, Status, Headers),
			loop(replace_child(Child#child{output=fin}, State));
		{reply, {Pid, StreamID}, Status, Headers, Body}
				when Pid =:= self() ->
			Child = #child{output=nofin} = get_child(StreamID, State),
			syn_reply(State, StreamID, false, Status, Headers),
			data(State, StreamID, true, Body),
			loop(replace_child(Child#child{output=fin}, State));
		{stream_reply, {Pid, StreamID}, Status, Headers}
				when Pid =:= self() ->
			#child{output=nofin} = get_child(StreamID, State),
			syn_reply(State, StreamID, false, Status, Headers),
			loop(State);
		{stream_data, {Pid, StreamID}, Data}
				when Pid =:= self() ->
			#child{output=nofin} = get_child(StreamID, State),
			data(State, StreamID, false, Data),
			loop(State);
		{stream_close, {Pid, StreamID}}
				when Pid =:= self() ->
			Child = #child{output=nofin} = get_child(StreamID, State),
			data(State, StreamID, true, <<>>),
			loop(replace_child(Child#child{output=fin}, State));
		{sendfile, {Pid, StreamID}, Filepath}
				when Pid =:= self() ->
			Child = #child{output=nofin} = get_child(StreamID, State),
			data_from_file(State, StreamID, Filepath),
			loop(replace_child(Child#child{output=fin}, State));
		{active, FromSocket = {Pid, StreamID}, FromPid} when Pid =:= self() ->
			Child = #child{in_buffer=InBuffer, is_recv=false}
				= get_child(StreamID, State),
			case InBuffer of
				<<>> ->
					loop(replace_child(Child#child{
						is_recv={active, FromSocket, FromPid}}, State));
				_ ->
					FromPid ! {spdy, FromSocket, InBuffer},
					loop(replace_child(Child#child{in_buffer= <<>>}, State))
			end;
		{passive, FromSocket = {Pid, StreamID}, FromPid} when Pid =:= self() ->
			Child = #child{is_recv=IsRecv} = get_child(StreamID, State),
			%% Make sure we aren't in the middle of a recv call.
			case IsRecv of false -> ok; {active, FromSocket, FromPid} -> ok end,
			loop(replace_child(Child#child{is_recv=false}, State));
		{'EXIT', Parent, Reason} ->
			exit(Reason);
		{'EXIT', Pid, _} ->
			%% @todo Report the error if any.
			loop(delete_child(Pid, State));
		{system, From, Request} ->
			sys:handle_system_msg(Request, From, Parent, ?MODULE, [], State);
		%% Calls from the supervisor module.
		{'$gen_call', {To, Tag}, which_children} ->
			Workers = [{?MODULE, Pid, worker, [?MODULE]}
				|| #child{pid=Pid} <- Children],
			To ! {Tag, Workers},
			loop(State);
		{'$gen_call', {To, Tag}, count_children} ->
			NbChildren = length(Children),
			Counts = [{specs, 1}, {active, NbChildren},
				{supervisors, 0}, {workers, NbChildren}],
			To ! {Tag, Counts},
			loop(State);
		{'$gen_call', {To, Tag}, _} ->
			To ! {Tag, {error, ?MODULE}},
			loop(State)
	after 60000 ->
		goaway(State, ok),
		terminate(State)
	end.

-spec system_continue(_, _, #state{}) -> ok.
system_continue(_, _, State) ->
	loop(State).

-spec system_terminate(any(), _, _, _) -> no_return().
system_terminate(Reason, _, _, _) ->
	exit(Reason).

-spec system_code_change(Misc, _, _, _) -> {ok, Misc} when Misc::#state{}.
system_code_change(Misc, _, _, _) ->
	{ok, Misc}.

parse_frame(State=#state{zinf=Zinf}, Data) ->
	case cow_spdy:split(Data) of
		{true, Frame, Rest} ->
			P = cow_spdy:parse(Frame, Zinf),
			case handle_frame(State#state{buffer = Rest}, P) of
				error ->
					terminate(State);
				State2 ->
					parse_frame(State2, Rest)
			end;
		false ->
			loop(State#state{buffer=Data})
	end.

%% FLAG_UNIDIRECTIONAL can only be set by the server.
handle_frame(State, {syn_stream, StreamID, _, _, true,
		_, _, _, _, _, _, _}) ->
	rst_stream(State, StreamID, protocol_error),
	State;
%% We do not support Associated-To-Stream-ID.
handle_frame(State, {syn_stream, StreamID, AssocToStreamID,
		_, _, _, _, _, _, _, _, _}) when AssocToStreamID =/= 0 ->
	rst_stream(State, StreamID, internal_error),
	State;
%% SYN_STREAM.
%%
%% Erlang does not allow us to control the priority of processes
%% so we ignore that value entirely.
handle_frame(State=#state{middlewares=Middlewares, env=Env,
		onrequest=OnRequest, onresponse=OnResponse, peer=Peer},
		{syn_stream, StreamID, _, IsFin, _, _,
		Method, _, Host, Path, Version, Headers}) ->
	Pid = spawn_link(?MODULE, request_init, [
		{self(), StreamID}, Peer, OnRequest, OnResponse,
		Env, Middlewares, Method, Host, Path, Version, Headers
	]),
	new_child(State, StreamID, Pid, IsFin);
%% RST_STREAM.
handle_frame(State, {rst_stream, StreamID, Status}) ->
	error_logger:error_msg("Received RST_STREAM frame ~p ~p",
		[StreamID, Status]),
	%% @todo Stop StreamID.
	State;
%% PING initiated by the server; ignore, we don't send any.
handle_frame(State, {ping, PingID}) when PingID rem 2 =:= 0 ->
	error_logger:error_msg("Ignored PING control frame: ~p~n", [PingID]),
	State;
%% PING initiated by the client; send it back.
handle_frame(State=#state{socket=Socket, transport=Transport},
		{ping, PingID}) ->
	Transport:send(Socket, cow_spdy:ping(PingID)),
	State;
%% Data received for a stream.
handle_frame(State, {data, StreamID, IsFin, Data}) ->
	Child = #child{input=nofin, in_buffer=Buffer, is_recv=IsRecv}
		= get_child(StreamID, State),
	Data2 = << Buffer/binary, Data/binary >>,
	IsFin2 = if IsFin -> fin; true -> nofin end,
	Child2 = case IsRecv of
		{active, FromSocket, FromPid} ->
			FromPid ! {spdy, FromSocket, Data},
			Child#child{input=IsFin2, is_recv=false};
		{passive, FromSocket, FromPid, 0, TRef} ->
			FromPid ! {recv, FromSocket, {ok, Data2}},
			cancel_recv_timeout(StreamID, TRef),
			Child#child{input=IsFin2, in_buffer= <<>>, is_recv=false};
		{passive, FromSocket, FromPid, Length, TRef}
				when byte_size(Data2) >= Length ->
			<< Data3:Length/binary, Rest/binary >> = Data2,
			FromPid ! {recv, FromSocket, {ok, Data3}},
			cancel_recv_timeout(StreamID, TRef),
			Child#child{input=IsFin2, in_buffer=Rest, is_recv=false};
		_ ->
			Child#child{input=IsFin2, in_buffer=Data2}
	end,
	replace_child(Child2, State);
%% General error, can't recover.
handle_frame(State, {error, badprotocol}) ->
	goaway(State, protocol_error),
	error;
%% Ignore all other frames for now.
handle_frame(State, Frame) ->
	error_logger:error_msg("Ignored frame ~p", [Frame]),
	State.

cancel_recv_timeout(StreamID, TRef) ->
	_ = erlang:cancel_timer(TRef),
	receive
		{recv_timeout, {Pid, StreamID}}
				when Pid =:= self() ->
			ok
	after 0 ->
		ok
	end.

%% @todo We must wait for the children to finish here,
%% but only up to N milliseconds. Then we shutdown.
terminate(_State) ->
	ok.

syn_reply(#state{socket=Socket, transport=Transport, zdef=Zdef},
		StreamID, IsFin, Status, Headers) ->
	Transport:send(Socket, cow_spdy:syn_reply(Zdef, StreamID, IsFin,
		Status, <<"HTTP/1.1">>, Headers)).

rst_stream(#state{socket=Socket, transport=Transport}, StreamID, Status) ->
	Transport:send(Socket, cow_spdy:rst_stream(StreamID, Status)).

goaway(#state{socket=Socket, transport=Transport, last_streamid=LastStreamID},
		Status) ->
	Transport:send(Socket, cow_spdy:goaway(LastStreamID, Status)).

data(#state{socket=Socket, transport=Transport}, StreamID, IsFin, Data) ->
	Transport:send(Socket, cow_spdy:data(StreamID, IsFin, Data)).

data_from_file(#state{socket=Socket, transport=Transport},
		StreamID, Filepath) ->
	{ok, IoDevice} = file:open(Filepath, [read, binary, raw]),
	data_from_file(Socket, Transport, StreamID, IoDevice).

data_from_file(Socket, Transport, StreamID, IoDevice) ->
	case file:read(IoDevice, 16#1fff) of
		eof ->
			_ = Transport:send(Socket, cow_spdy:data(StreamID, true, <<>>)),
			ok;
		{ok, Data} ->
			case Transport:send(Socket, cow_spdy:data(StreamID, false, Data)) of
				ok ->
					data_from_file(Socket, Transport, StreamID, IoDevice);
				{error, _} ->
					ok
			end
	end.

%% Children.

new_child(State=#state{children=Children}, StreamID, Pid, IsFin) ->
	IsFin2 = if IsFin -> fin; true -> nofin end,
	State#state{last_streamid=StreamID,
		children=[#child{streamid=StreamID,
		pid=Pid, input=IsFin2}|Children]}.

get_child(StreamID, #state{children=Children}) ->
	lists:keyfind(StreamID, #child.streamid, Children).

replace_child(Child=#child{streamid=StreamID},
		State=#state{children=Children}) ->
	Children2 = lists:keyreplace(StreamID, #child.streamid, Children, Child),
	State#state{children=Children2}.

delete_child(Pid, State=#state{children=Children}) ->
	Children2 = lists:keydelete(Pid, #child.pid, Children),
	State#state{children=Children2}.

%% Request process.

-spec request_init(socket(), {inet:ip_address(), inet:port_number()},
		cowboy:onrequest_fun(), cowboy:onresponse_fun(),
		cowboy_middleware:env(), [module()],
		binary(), binary(), binary(), binary(), [{binary(), binary()}])
	-> ok.
request_init(FakeSocket, Peer, OnRequest, OnResponse,
		Env, Middlewares, Method, Host, Path, Version, Headers) ->
	{Host2, Port} = cow_http:parse_fullhost(Host),
	{Path2, Qs} = cow_http:parse_fullpath(Path),
	Version2 = cow_http:parse_version(Version),
	Req = cowboy_req:new(FakeSocket, ?MODULE, Peer,
		Method, Path2, Qs, Version2, Headers,
		Host2, Port, <<>>, true, false, OnResponse),
	case OnRequest of
		undefined ->
			execute(Req, Env, Middlewares);
		_ ->
			Req2 = OnRequest(Req),
			case cowboy_req:get(resp_state, Req2) of
				waiting -> execute(Req2, Env, Middlewares);
				_ -> ok
			end
	end.

-spec execute(cowboy_req:req(), cowboy_middleware:env(), [module()])
	-> ok.
execute(Req, _, []) ->
	cowboy_req:ensure_response(Req, 204);
execute(Req, Env, [Middleware|Tail]) ->
	case Middleware:execute(Req, Env) of
		{ok, Req2, Env2} ->
			execute(Req2, Env2, Tail);
		{suspend, Module, Function, Args} ->
			erlang:hibernate(?MODULE, resume,
				[Env, Tail, Module, Function, Args]);
		{halt, Req2} ->
			cowboy_req:ensure_response(Req2, 204);
		{error, Status, Req2} ->
			cowboy_req:reply(Status, Req2)
	end.

-spec resume(cowboy_middleware:env(), [module()],
	module(), module(), [any()]) -> ok.
resume(Env, Tail, Module, Function, Args) ->
	case apply(Module, Function, Args) of
		{ok, Req2, Env2} ->
			execute(Req2, Env2, Tail);
		{suspend, Module2, Function2, Args2} ->
			erlang:hibernate(?MODULE, resume,
				[Env, Tail, Module2, Function2, Args2]);
		{halt, Req2} ->
			cowboy_req:ensure_response(Req2, 204);
		{error, Status, Req2} ->
			cowboy_req:reply(Status, Req2)
	end.

%% Reply functions used by cowboy_req.

-spec reply(socket(), binary(), cowboy:http_headers(), iodata()) -> ok.
reply(Socket = {Pid, _}, Status, Headers, Body) ->
	_ = case iolist_size(Body) of
		0 -> Pid ! {reply, Socket, Status, Headers};
		_ -> Pid ! {reply, Socket, Status, Headers, Body}
	end,
	ok.

-spec stream_reply(socket(), binary(), cowboy:http_headers()) -> ok.
stream_reply(Socket = {Pid, _}, Status, Headers) ->
	_ = Pid ! {stream_reply, Socket, Status, Headers},
	ok.

-spec stream_data(socket(), iodata()) -> ok.
stream_data(Socket = {Pid, _}, Data) ->
	_ = Pid ! {stream_data, Socket, Data},
	ok.

-spec stream_close(socket()) -> ok.
stream_close(Socket = {Pid, _}) ->
	_ = Pid ! {stream_close, Socket},
	ok.

%% Internal transport functions.

-spec name() -> spdy.
name() ->
	spdy.

-spec messages() -> {spdy, spdy_closed, spdy_error}.
messages() ->
	{spdy, spdy_closed, spdy_error}.

-spec recv(socket(), non_neg_integer(), timeout())
	-> {ok, binary()} | {error, timeout}.
recv(Socket = {Pid, _}, Length, Timeout) ->
	_ = Pid ! {recv, Socket, self(), Length, Timeout},
	receive
		{recv, Socket, Ret} ->
			Ret
	end.

-spec send(socket(), iodata()) -> ok.
send(Socket, Data) ->
	stream_data(Socket, Data).

%% We don't wait for the result of the actual sendfile call,
%% therefore we can't know how much was actually sent.
%% This isn't a problem as we don't use this value in Cowboy.
-spec sendfile(socket(), file:name_all()) -> {ok, undefined}.
sendfile(Socket = {Pid, _}, Filepath) ->
	_ = Pid ! {sendfile, Socket, Filepath},
	{ok, undefined}.

-spec setopts({pid(), _}, list()) -> ok.
setopts(Socket = {Pid, _}, [{active, once}]) ->
	_ = Pid ! {active, Socket, self()},
	ok;
setopts(Socket = {Pid, _}, [{active, false}]) ->
	_ = Pid ! {passive, Socket, self()},
	ok.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted deps/cowboy/src/cowboy_static.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
%% Copyright (c) 2011, Magnus Klaar <magnus.klaar@gmail.com>
%% Copyright (c) 2013-2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(cowboy_static).

-export([init/3]).
-export([rest_init/2]).
-export([malformed_request/2]).
-export([forbidden/2]).
-export([content_types_provided/2]).
-export([resource_exists/2]).
-export([last_modified/2]).
-export([generate_etag/2]).
-export([get_file/2]).

-type extra_etag() :: {etag, module(), function()} | {etag, false}.
-type extra_mimetypes() :: {mimetypes, module(), function()}
	| {mimetypes, binary() | {binary(), binary(), [{binary(), binary()}]}}.
-type extra() :: [extra_etag() | extra_mimetypes()].
-type opts() :: {file | dir, string() | binary()}
	| {file | dir, string() | binary(), extra()}
	| {priv_file | priv_dir, atom(), string() | binary()}
	| {priv_file | priv_dir, atom(), string() | binary(), extra()}.
-export_type([opts/0]).

-include_lib("kernel/include/file.hrl").

-type state() :: {binary(), {ok, #file_info{}} | {error, atom()}, extra()}.

-spec init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
init(_, _, _) ->
	{upgrade, protocol, cowboy_rest}.

%% Resolve the file that will be sent and get its file information.
%% If the handler is configured to manage a directory, check that the
%% requested file is inside the configured directory.

-spec rest_init(Req, opts())
	-> {ok, Req, error | state()}
	when Req::cowboy_req:req().
rest_init(Req, {Name, Path}) ->
	rest_init_opts(Req, {Name, Path, []});
rest_init(Req, {Name, App, Path})
		when Name =:= priv_file; Name =:= priv_dir ->
	rest_init_opts(Req, {Name, App, Path, []});
rest_init(Req, Opts) ->
	rest_init_opts(Req, Opts).

rest_init_opts(Req, {priv_file, App, Path, Extra}) ->
	rest_init_info(Req, absname(priv_path(App, Path)), Extra);
rest_init_opts(Req, {file, Path, Extra}) ->
	rest_init_info(Req, absname(Path), Extra);
rest_init_opts(Req, {priv_dir, App, Path, Extra}) ->
	rest_init_dir(Req, priv_path(App, Path), Extra);
rest_init_opts(Req, {dir, Path, Extra}) ->
	rest_init_dir(Req, Path, Extra).

priv_path(App, Path) ->
	case code:priv_dir(App) of
		{error, bad_name} ->
			error({badarg, "Can't resolve the priv_dir of application "
				++ atom_to_list(App)});
		PrivDir when is_list(Path) ->
			PrivDir ++ "/" ++ Path;
		PrivDir when is_binary(Path) ->
			<< (list_to_binary(PrivDir))/binary, $/, Path/binary >>
	end.

absname(Path) when is_list(Path) ->
	filename:absname(list_to_binary(Path));
absname(Path) when is_binary(Path) ->
	filename:absname(Path).

rest_init_dir(Req, Path, Extra) when is_list(Path) ->
	rest_init_dir(Req, list_to_binary(Path), Extra);
rest_init_dir(Req, Path, Extra) ->
	Dir = fullpath(filename:absname(Path)),
	{PathInfo, Req2} = cowboy_req:path_info(Req),
	Filepath = filename:join([Dir|PathInfo]),
	Len = byte_size(Dir),
	case fullpath(Filepath) of
		<< Dir:Len/binary, $/, _/binary >> ->
			rest_init_info(Req2, Filepath, Extra);
		_ ->
			{ok, Req2, error}
	end.

fullpath(Path) ->
	fullpath(filename:split(Path), []).
fullpath([], Acc) ->
	filename:join(lists:reverse(Acc));
fullpath([<<".">>|Tail], Acc) ->
	fullpath(Tail, Acc);
fullpath([<<"..">>|Tail], Acc=[_]) ->
	fullpath(Tail, Acc);
fullpath([<<"..">>|Tail], [_|Acc]) ->
	fullpath(Tail, Acc);
fullpath([Segment|Tail], Acc) ->
	fullpath(Tail, [Segment|Acc]).

rest_init_info(Req, Path, Extra) ->
	Info = file:read_file_info(Path, [{time, universal}]),
	{ok, Req, {Path, Info, Extra}}.

-ifdef(TEST).
fullpath_test_() ->
	Tests = [
		{<<"/home/cowboy">>, <<"/home/cowboy">>},
		{<<"/home/cowboy">>, <<"/home/cowboy/">>},
		{<<"/home/cowboy">>, <<"/home/cowboy/./">>},
		{<<"/home/cowboy">>, <<"/home/cowboy/./././././.">>},
		{<<"/home/cowboy">>, <<"/home/cowboy/abc/..">>},
		{<<"/home/cowboy">>, <<"/home/cowboy/abc/../">>},
		{<<"/home/cowboy">>, <<"/home/cowboy/abc/./../.">>},
		{<<"/">>, <<"/home/cowboy/../../../../../..">>},
		{<<"/etc/passwd">>, <<"/home/cowboy/../../etc/passwd">>}
	],
	[{P, fun() -> R = fullpath(P) end} || {R, P} <- Tests].

good_path_check_test_() ->
	Tests = [
		<<"/home/cowboy/file">>,
		<<"/home/cowboy/file/">>,
		<<"/home/cowboy/./file">>,
		<<"/home/cowboy/././././././file">>,
		<<"/home/cowboy/abc/../file">>,
		<<"/home/cowboy/abc/../file">>,
		<<"/home/cowboy/abc/./.././file">>
	],
	[{P, fun() ->
		case fullpath(P) of
			<< "/home/cowboy/", _/binary >> -> ok
		end
	end} || P <- Tests].

bad_path_check_test_() ->
	Tests = [
		<<"/home/cowboy/../../../../../../file">>,
		<<"/home/cowboy/../../etc/passwd">>
	],
	[{P, fun() ->
		error = case fullpath(P) of
			<< "/home/cowboy/", _/binary >> -> ok;
			_ -> error
		end
	end} || P <- Tests].

good_path_win32_check_test_() ->
	Tests = case os:type() of
		{unix, _} ->
			[];
		{win32, _} ->
			[
				<<"c:/home/cowboy/file">>,
				<<"c:/home/cowboy/file/">>,
				<<"c:/home/cowboy/./file">>,
				<<"c:/home/cowboy/././././././file">>,
				<<"c:/home/cowboy/abc/../file">>,
				<<"c:/home/cowboy/abc/../file">>,
				<<"c:/home/cowboy/abc/./.././file">>
			]
	end,
	[{P, fun() ->
		case fullpath(P) of
			<< "c:/home/cowboy/", _/binary >> -> ok
		end
	end} || P <- Tests].

bad_path_win32_check_test_() ->
	Tests = case os:type() of
		{unix, _} ->
			[];
		{win32, _} ->
			[
				<<"c:/home/cowboy/../../secretfile.bat">>,
				<<"c:/home/cowboy/c:/secretfile.bat">>,
				<<"c:/home/cowboy/..\\..\\secretfile.bat">>,
				<<"c:/home/cowboy/c:\\secretfile.bat">>
			]
	end,
	[{P, fun() ->
		error = case fullpath(P) of
			<< "c:/home/cowboy/", _/binary >> -> ok;
			_ -> error
		end
	end} || P <- Tests].
-endif.

%% Reject requests that tried to access a file outside
%% the target directory.

-spec malformed_request(Req, State)
	-> {boolean(), Req, State}.
malformed_request(Req, State) ->
	{State =:= error, Req, State}.

%% Directories, files that can't be accessed at all and
%% files with no read flag are forbidden.

-spec forbidden(Req, State)
	-> {boolean(), Req, State}
	when State::state().
forbidden(Req, State={_, {ok, #file_info{type=directory}}, _}) ->
	{true, Req, State};
forbidden(Req, State={_, {error, eacces}, _}) ->
	{true, Req, State};
forbidden(Req, State={_, {ok, #file_info{access=Access}}, _})
		when Access =:= write; Access =:= none ->
	{true, Req, State};
forbidden(Req, State) ->
	{false, Req, State}.

%% Detect the mimetype of the file.

-spec content_types_provided(Req, State)
	-> {[{binary(), get_file}], Req, State}
	when State::state().
content_types_provided(Req, State={Path, _, Extra}) ->
	case lists:keyfind(mimetypes, 1, Extra) of
		false ->
			{[{cow_mimetypes:web(Path), get_file}], Req, State};
		{mimetypes, Module, Function} ->
			{[{Module:Function(Path), get_file}], Req, State};
		{mimetypes, Type} ->
			{[{Type, get_file}], Req, State}
	end.

%% Assume the resource doesn't exist if it's not a regular file.

-spec resource_exists(Req, State)
	-> {boolean(), Req, State}
	when State::state().
resource_exists(Req, State={_, {ok, #file_info{type=regular}}, _}) ->
	{true, Req, State};
resource_exists(Req, State) ->
	{false, Req, State}.

%% Generate an etag for the file.

-spec generate_etag(Req, State)
	-> {{strong | weak, binary()}, Req, State}
	when State::state().
generate_etag(Req, State={Path, {ok, #file_info{size=Size, mtime=Mtime}},
		Extra}) ->
	case lists:keyfind(etag, 1, Extra) of
		false ->
			{generate_default_etag(Size, Mtime), Req, State};
		{etag, Module, Function} ->
			{Module:Function(Path, Size, Mtime), Req, State};
		{etag, false} ->
			{undefined, Req, State}
	end.

generate_default_etag(Size, Mtime) ->
	{strong, integer_to_binary(erlang:phash2({Size, Mtime}, 16#ffffffff))}.

%% Return the time of last modification of the file.

-spec last_modified(Req, State)
	-> {calendar:datetime(), Req, State}
	when State::state().
last_modified(Req, State={_, {ok, #file_info{mtime=Modified}}, _}) ->
	{Modified, Req, State}.

%% Stream the file.
%% @todo Export cowboy_req:resp_body_fun()?

-spec get_file(Req, State)
	-> {{stream, non_neg_integer(), fun()}, Req, State}
	when State::state().
get_file(Req, State={Path, {ok, #file_info{size=Size}}, _}) ->
	Sendfile = fun (Socket, Transport) ->
		case Transport:sendfile(Socket, Path) of
			{ok, _} -> ok;
			{error, closed} -> ok;
			{error, etimedout} -> ok
		end
	end,
	{{stream, Size, Sendfile}, Req, State}.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






































































































































































































































































































































































































































































































































































































Deleted deps/cowboy/src/cowboy_sub_protocol.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
%% Copyright (c) 2013, James Fish <james@fishcakez.com>
%% Copyright (c) 2013-2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(cowboy_sub_protocol).

-callback upgrade(Req, Env, module(), any())
	-> {ok, Req, Env}
	| {suspend, module(), atom(), [any()]}
	| {halt, Req}
	| {error, cowboy:http_status(), Req}
	when Req::cowboy_req:req(), Env::cowboy_middleware:env().
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<














































Deleted deps/cowboy/src/cowboy_sup.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
%% Copyright (c) 2011-2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(cowboy_sup).
-behaviour(supervisor).

-export([start_link/0]).
-export([init/1]).

-spec start_link() -> {ok, pid()}.
start_link() ->
	supervisor:start_link({local, ?MODULE}, ?MODULE, []).

-spec init([])
	-> {ok, {{supervisor:strategy(), 10, 10}, [supervisor:child_spec()]}}.
init([]) ->
	Procs = [{cowboy_clock, {cowboy_clock, start_link, []},
		permanent, 5000, worker, [cowboy_clock]}],
	{ok, {{one_for_one, 10, 10}, Procs}}.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




























































Deleted deps/cowboy/src/cowboy_websocket.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
%% Copyright (c) 2011-2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

%% Cowboy supports versions 7 through 17 of the Websocket drafts.
%% It also supports RFC6455, the proposed standard for Websocket.
-module(cowboy_websocket).
-behaviour(cowboy_sub_protocol).

-export([upgrade/4]).
-export([handler_loop/4]).

-type close_code() :: 1000..4999.
-export_type([close_code/0]).

-type frame() :: close | ping | pong
	| {text | binary | close | ping | pong, iodata()}
	| {close, close_code(), iodata()}.
-export_type([frame/0]).

-type opcode() :: 0 | 1 | 2 | 8 | 9 | 10.
-type mask_key() :: 0..16#ffffffff.
-type frag_state() :: undefined
	| {nofin, opcode(), binary()} | {fin, opcode(), binary()}.
-type rsv() :: << _:3 >>.
-type terminate_reason() :: {normal | error | remote, atom()}
	| {remote, close_code(), binary()}.

-record(state, {
	env :: cowboy_middleware:env(),
	socket = undefined :: inet:socket(),
	transport = undefined :: module(),
	handler :: module(),
	key = undefined :: undefined | binary(),
	timeout = infinity :: timeout(),
	timeout_ref = undefined :: undefined | reference(),
	messages = undefined :: undefined | {atom(), atom(), atom()},
	hibernate = false :: boolean(),
	frag_state = undefined :: frag_state(),
	utf8_state = <<>> :: binary(),
	deflate_frame = false :: boolean(),
	inflate_state :: undefined | port(),
	deflate_state :: undefined | port()
}).

-spec upgrade(Req, Env, module(), any())
	-> {ok, Req, Env}
	| {suspend, module(), atom(), [any()]}
	when Req::cowboy_req:req(), Env::cowboy_middleware:env().
upgrade(Req, Env, Handler, HandlerOpts) ->
	{_, Ref} = lists:keyfind(listener, 1, Env),
	ranch:remove_connection(Ref),
	[Socket, Transport] = cowboy_req:get([socket, transport], Req),
	State = #state{env=Env, socket=Socket, transport=Transport,
		handler=Handler},
	try websocket_upgrade(State, Req) of
		{ok, State2, Req2} ->
			handler_init(State2, Req2, HandlerOpts)
	catch _:_ ->
		receive
			{cowboy_req, resp_sent} -> ok
		after 0 ->
			_ = cowboy_req:reply(400, Req),
			exit(normal)
		end
	end.

-spec websocket_upgrade(#state{}, Req)
	-> {ok, #state{}, Req} when Req::cowboy_req:req().
websocket_upgrade(State, Req) ->
	{ok, ConnTokens, Req2}
		= cowboy_req:parse_header(<<"connection">>, Req),
	true = lists:member(<<"upgrade">>, ConnTokens),
	%% @todo Should probably send a 426 if the Upgrade header is missing.
	{ok, [<<"websocket">>], Req3}
		= cowboy_req:parse_header(<<"upgrade">>, Req2),
	{Version, Req4} = cowboy_req:header(<<"sec-websocket-version">>, Req3),
	IntVersion = list_to_integer(binary_to_list(Version)),
	true = (IntVersion =:= 7) orelse (IntVersion =:= 8)
		orelse (IntVersion =:= 13),
	{Key, Req5} = cowboy_req:header(<<"sec-websocket-key">>, Req4),
	false = Key =:= undefined,
	websocket_extensions(State#state{key=Key},
		cowboy_req:set_meta(websocket_version, IntVersion, Req5)).

-spec websocket_extensions(#state{}, Req)
	-> {ok, #state{}, Req} when Req::cowboy_req:req().
websocket_extensions(State, Req) ->
	case cowboy_req:parse_header(<<"sec-websocket-extensions">>, Req) of
		{ok, Extensions, Req2} when Extensions =/= undefined ->
			[Compress] = cowboy_req:get([resp_compress], Req),
			case lists:keyfind(<<"x-webkit-deflate-frame">>, 1, Extensions) of
				{<<"x-webkit-deflate-frame">>, []} when Compress =:= true ->
					Inflate = zlib:open(),
					Deflate = zlib:open(),
					% Since we are negotiating an unconstrained deflate-frame
					% then we must be willing to accept frames using the
					% maximum window size which is 2^15. The negative value
					% indicates that zlib headers are not used.
					ok = zlib:inflateInit(Inflate, -15),
					% Initialize the deflater with a window size of 2^15 bits and disable
					% the zlib headers.
					ok = zlib:deflateInit(Deflate, best_compression, deflated, -15, 8, default),
					{ok, State#state{
						deflate_frame = true,
						inflate_state = Inflate,
						deflate_state = Deflate
					}, cowboy_req:set_meta(websocket_compress, true, Req2)};
				_ ->
					{ok, State, cowboy_req:set_meta(websocket_compress, false, Req2)}
			end;
		_ ->
			{ok, State, cowboy_req:set_meta(websocket_compress, false, Req)}
	end.

-spec handler_init(#state{}, Req, any())
	-> {ok, Req, cowboy_middleware:env()} | {suspend, module(), atom(), [any()]}
	when Req::cowboy_req:req().
handler_init(State=#state{env=Env, transport=Transport,
		handler=Handler}, Req, HandlerOpts) ->
	try Handler:websocket_init(Transport:name(), Req, HandlerOpts) of
		{ok, Req2, HandlerState} ->
			websocket_handshake(State, Req2, HandlerState);
		{ok, Req2, HandlerState, hibernate} ->
			websocket_handshake(State#state{hibernate=true},
				Req2, HandlerState);
		{ok, Req2, HandlerState, Timeout} ->
			websocket_handshake(State#state{timeout=Timeout},
				Req2, HandlerState);
		{ok, Req2, HandlerState, Timeout, hibernate} ->
			websocket_handshake(State#state{timeout=Timeout,
				hibernate=true}, Req2, HandlerState);
		{shutdown, Req2} ->
			cowboy_req:ensure_response(Req2, 400),
			{ok, Req2, [{result, closed}|Env]}
	catch Class:Reason ->
		Stacktrace = erlang:get_stacktrace(),
		cowboy_req:maybe_reply(Stacktrace, Req),
		erlang:Class([
			{reason, Reason},
			{mfa, {Handler, websocket_init, 3}},
			{stacktrace, Stacktrace},
			{req, cowboy_req:to_list(Req)},
			{opts, HandlerOpts}
		])
	end.

-spec websocket_handshake(#state{}, Req, any())
	-> {ok, Req, cowboy_middleware:env()}
	| {suspend, module(), atom(), [any()]}
	when Req::cowboy_req:req().
websocket_handshake(State=#state{
			transport=Transport, key=Key, deflate_frame=DeflateFrame},
		Req, HandlerState) ->
	Challenge = base64:encode(crypto:hash(sha,
		<< Key/binary, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" >>)),
	Extensions = case DeflateFrame of
		false -> [];
		true -> [{<<"sec-websocket-extensions">>, <<"x-webkit-deflate-frame">>}]
	end,
	{ok, Req2} = cowboy_req:upgrade_reply(
		101,
		[{<<"upgrade">>, <<"websocket">>},
		 {<<"sec-websocket-accept">>, Challenge}|
		 Extensions],
		Req),
	%% Flush the resp_sent message before moving on.
	receive {cowboy_req, resp_sent} -> ok after 0 -> ok end,
	State2 = handler_loop_timeout(State),
	handler_before_loop(State2#state{key=undefined,
		messages=Transport:messages()}, Req2, HandlerState, <<>>).

-spec handler_before_loop(#state{}, Req, any(), binary())
	-> {ok, Req, cowboy_middleware:env()}
	| {suspend, module(), atom(), [any()]}
	when Req::cowboy_req:req().
handler_before_loop(State=#state{
			socket=Socket, transport=Transport, hibernate=true},
		Req, HandlerState, SoFar) ->
	Transport:setopts(Socket, [{active, once}]),
	{suspend, ?MODULE, handler_loop,
		[State#state{hibernate=false}, Req, HandlerState, SoFar]};
handler_before_loop(State=#state{socket=Socket, transport=Transport},
		Req, HandlerState, SoFar) ->
	Transport:setopts(Socket, [{active, once}]),
	handler_loop(State, Req, HandlerState, SoFar).

-spec handler_loop_timeout(#state{}) -> #state{}.
handler_loop_timeout(State=#state{timeout=infinity}) ->
	State#state{timeout_ref=undefined};
handler_loop_timeout(State=#state{timeout=Timeout, timeout_ref=PrevRef}) ->
	_ = case PrevRef of undefined -> ignore; PrevRef ->
		erlang:cancel_timer(PrevRef) end,
	TRef = erlang:start_timer(Timeout, self(), ?MODULE),
	State#state{timeout_ref=TRef}.

-spec handler_loop(#state{}, Req, any(), binary())
	-> {ok, Req, cowboy_middleware:env()}
	| {suspend, module(), atom(), [any()]}
	when Req::cowboy_req:req().
handler_loop(State=#state{socket=Socket, messages={OK, Closed, Error},
		timeout_ref=TRef}, Req, HandlerState, SoFar) ->
	receive
		{OK, Socket, Data} ->
			State2 = handler_loop_timeout(State),
			websocket_data(State2, Req, HandlerState,
				<< SoFar/binary, Data/binary >>);
		{Closed, Socket} ->
			handler_terminate(State, Req, HandlerState, {error, closed});
		{Error, Socket, Reason} ->
			handler_terminate(State, Req, HandlerState, {error, Reason});
		{timeout, TRef, ?MODULE} ->
			websocket_close(State, Req, HandlerState, {normal, timeout});
		{timeout, OlderTRef, ?MODULE} when is_reference(OlderTRef) ->
			handler_loop(State, Req, HandlerState, SoFar);
		Message ->
			handler_call(State, Req, HandlerState,
				SoFar, websocket_info, Message, fun handler_before_loop/4)
	end.

%% All frames passing through this function are considered valid,
%% with the only exception of text and close frames with a payload
%% which may still contain errors.
-spec websocket_data(#state{}, Req, any(), binary())
	-> {ok, Req, cowboy_middleware:env()}
	| {suspend, module(), atom(), [any()]}
	when Req::cowboy_req:req().
%% RSV bits MUST be 0 unless an extension is negotiated
%% that defines meanings for non-zero values.
websocket_data(State, Req, HandlerState, << _:1, Rsv:3, _/bits >>)
		when Rsv =/= 0, State#state.deflate_frame =:= false ->
	websocket_close(State, Req, HandlerState, {error, badframe});
%% Invalid opcode. Note that these opcodes may be used by extensions.
websocket_data(State, Req, HandlerState, << _:4, Opcode:4, _/bits >>)
		when Opcode > 2, Opcode =/= 8, Opcode =/= 9, Opcode =/= 10 ->
	websocket_close(State, Req, HandlerState, {error, badframe});
%% Control frames MUST NOT be fragmented.
websocket_data(State, Req, HandlerState, << 0:1, _:3, Opcode:4, _/bits >>)
		when Opcode >= 8 ->
	websocket_close(State, Req, HandlerState, {error, badframe});
%% A frame MUST NOT use the zero opcode unless fragmentation was initiated.
websocket_data(State=#state{frag_state=undefined}, Req, HandlerState,
		<< _:4, 0:4, _/bits >>) ->
	websocket_close(State, Req, HandlerState, {error, badframe});
%% Non-control opcode when expecting control message or next fragment.
websocket_data(State=#state{frag_state={nofin, _, _}}, Req, HandlerState,
		<< _:4, Opcode:4, _/bits >>)
		when Opcode =/= 0, Opcode < 8 ->
	websocket_close(State, Req, HandlerState, {error, badframe});
%% Close control frame length MUST be 0 or >= 2.
websocket_data(State, Req, HandlerState, << _:4, 8:4, _:1, 1:7, _/bits >>) ->
	websocket_close(State, Req, HandlerState, {error, badframe});
%% Close control frame with incomplete close code. Need more data.
websocket_data(State, Req, HandlerState,
		Data = << _:4, 8:4, 1:1, Len:7, _/bits >>)
		when Len > 1, byte_size(Data) < 8 ->
	handler_before_loop(State, Req, HandlerState, Data);
%% 7 bits payload length.
websocket_data(State, Req, HandlerState, << Fin:1, Rsv:3/bits, Opcode:4, 1:1,
		Len:7, MaskKey:32, Rest/bits >>)
		when Len < 126 ->
	websocket_data(State, Req, HandlerState,
		Opcode, Len, MaskKey, Rest, Rsv, Fin);
%% 16 bits payload length.
websocket_data(State, Req, HandlerState, << Fin:1, Rsv:3/bits, Opcode:4, 1:1,
		126:7, Len:16, MaskKey:32, Rest/bits >>)
		when Len > 125, Opcode < 8 ->
	websocket_data(State, Req, HandlerState,
		Opcode, Len, MaskKey, Rest, Rsv, Fin);
%% 63 bits payload length.
websocket_data(State, Req, HandlerState, << Fin:1, Rsv:3/bits, Opcode:4, 1:1,
		127:7, 0:1, Len:63, MaskKey:32, Rest/bits >>)
		when Len > 16#ffff, Opcode < 8 ->
	websocket_data(State, Req, HandlerState,
		Opcode, Len, MaskKey, Rest, Rsv, Fin);
%% When payload length is over 63 bits, the most significant bit MUST be 0.
websocket_data(State, Req, HandlerState, << _:8, 1:1, 127:7, 1:1, _:7, _/binary >>) ->
	websocket_close(State, Req, HandlerState, {error, badframe});
%% All frames sent from the client to the server are masked.
websocket_data(State, Req, HandlerState, << _:8, 0:1, _/bits >>) ->
	websocket_close(State, Req, HandlerState, {error, badframe});
%% For the next two clauses, it can be one of the following:
%%
%%  *  The minimal number of bytes MUST be used to encode the length
%%  *  All control frames MUST have a payload length of 125 bytes or less
websocket_data(State, Req, HandlerState, << _:9, 126:7, _:48, _/bits >>) ->
	websocket_close(State, Req, HandlerState, {error, badframe});
websocket_data(State, Req, HandlerState, << _:9, 127:7, _:96, _/bits >>) ->
	websocket_close(State, Req, HandlerState, {error, badframe});
%% Need more data.
websocket_data(State, Req, HandlerState, Data) ->
	handler_before_loop(State, Req, HandlerState, Data).

%% Initialize or update fragmentation state.
-spec websocket_data(#state{}, Req, any(),
	opcode(), non_neg_integer(), mask_key(), binary(), rsv(), 0 | 1)
	-> {ok, Req, cowboy_middleware:env()}
	| {suspend, module(), atom(), [any()]}
	when Req::cowboy_req:req().
%% The opcode is only included in the first frame fragment.
websocket_data(State=#state{frag_state=undefined}, Req, HandlerState,
		Opcode, Len, MaskKey, Data, Rsv, 0) ->
	websocket_payload(State#state{frag_state={nofin, Opcode, <<>>}},
		Req, HandlerState, 0, Len, MaskKey, <<>>, 0, Data, Rsv);
%% Subsequent frame fragments.
websocket_data(State=#state{frag_state={nofin, _, _}}, Req, HandlerState,
		0, Len, MaskKey, Data, Rsv, 0) ->
	websocket_payload(State, Req, HandlerState,
		0, Len, MaskKey, <<>>, 0, Data, Rsv);
%% Final frame fragment.
websocket_data(State=#state{frag_state={nofin, Opcode, SoFar}},
		Req, HandlerState, 0, Len, MaskKey, Data, Rsv, 1) ->
	websocket_payload(State#state{frag_state={fin, Opcode, SoFar}},
		Req, HandlerState, 0, Len, MaskKey, <<>>, 0, Data, Rsv);
%% Unfragmented frame.
websocket_data(State, Req, HandlerState, Opcode, Len, MaskKey, Data, Rsv, 1) ->
	websocket_payload(State, Req, HandlerState,
		Opcode, Len, MaskKey, <<>>, 0, Data, Rsv).

-spec websocket_payload(#state{}, Req, any(),
	opcode(), non_neg_integer(), mask_key(), binary(), non_neg_integer(),
	binary(), rsv())
	-> {ok, Req, cowboy_middleware:env()}
	| {suspend, module(), atom(), [any()]}
	when Req::cowboy_req:req().
%% Close control frames with a payload MUST contain a valid close code.
websocket_payload(State, Req, HandlerState,
		Opcode=8, Len, MaskKey, <<>>, 0,
		<< MaskedCode:2/binary, Rest/bits >>, Rsv) ->
	Unmasked = << Code:16 >> = websocket_unmask(MaskedCode, MaskKey, <<>>),
	if	Code < 1000; Code =:= 1004; Code =:= 1005; Code =:= 1006;
				(Code > 1011) and (Code < 3000); Code > 4999 ->
			websocket_close(State, Req, HandlerState, {error, badframe});
		true ->
			websocket_payload(State, Req, HandlerState,
				Opcode, Len - 2, MaskKey, Unmasked, byte_size(MaskedCode),
				Rest, Rsv)
	end;
%% Text frames and close control frames MUST have a payload that is valid UTF-8.
websocket_payload(State=#state{utf8_state=Incomplete},
		Req, HandlerState, Opcode, Len, MaskKey, Unmasked, UnmaskedLen,
		Data, Rsv)
		when (byte_size(Data) < Len) andalso ((Opcode =:= 1) orelse
			((Opcode =:= 8) andalso (Unmasked =/= <<>>))) ->
	Unmasked2 = websocket_unmask(Data,
		rotate_mask_key(MaskKey, UnmaskedLen), <<>>),
	{Unmasked3, State2} = websocket_inflate_frame(Unmasked2, Rsv, false, State),
	case is_utf8(<< Incomplete/binary, Unmasked3/binary >>) of
		false ->
			websocket_close(State2, Req, HandlerState, {error, badencoding});
		Utf8State ->
			websocket_payload_loop(State2#state{utf8_state=Utf8State},
				Req, HandlerState, Opcode, Len - byte_size(Data), MaskKey,
				<< Unmasked/binary, Unmasked3/binary >>,
				UnmaskedLen + byte_size(Data), Rsv)
	end;
websocket_payload(State=#state{utf8_state=Incomplete},
		Req, HandlerState, Opcode, Len, MaskKey, Unmasked, UnmaskedLen,
		Data, Rsv)
		when Opcode =:= 1; (Opcode =:= 8) and (Unmasked =/= <<>>) ->
	<< End:Len/binary, Rest/bits >> = Data,
	Unmasked2 = websocket_unmask(End,
		rotate_mask_key(MaskKey, UnmaskedLen), <<>>),
	{Unmasked3, State2} = websocket_inflate_frame(Unmasked2, Rsv, true, State),
	case is_utf8(<< Incomplete/binary, Unmasked3/binary >>) of
		<<>> ->
			websocket_dispatch(State2#state{utf8_state= <<>>},
				Req, HandlerState, Rest, Opcode,
				<< Unmasked/binary, Unmasked3/binary >>);
		_ ->
			websocket_close(State2, Req, HandlerState, {error, badencoding})
	end;
%% Fragmented text frames may cut payload in the middle of UTF-8 codepoints.
websocket_payload(State=#state{frag_state={_, 1, _}, utf8_state=Incomplete},
		Req, HandlerState, Opcode=0, Len, MaskKey, Unmasked, UnmaskedLen,
		Data, Rsv)
		when byte_size(Data) < Len ->
	Unmasked2 = websocket_unmask(Data,
		rotate_mask_key(MaskKey, UnmaskedLen), <<>>),
	{Unmasked3, State2} = websocket_inflate_frame(Unmasked2, Rsv, false, State),
	case is_utf8(<< Incomplete/binary, Unmasked3/binary >>) of
		false ->
			websocket_close(State2, Req, HandlerState, {error, badencoding});
		Utf8State ->
			websocket_payload_loop(State2#state{utf8_state=Utf8State},
				Req, HandlerState, Opcode, Len - byte_size(Data), MaskKey,
				<< Unmasked/binary, Unmasked3/binary >>,
				UnmaskedLen + byte_size(Data), Rsv)
	end;
websocket_payload(State=#state{frag_state={Fin, 1, _}, utf8_state=Incomplete},
		Req, HandlerState, Opcode=0, Len, MaskKey, Unmasked, UnmaskedLen,
		Data, Rsv) ->
	<< End:Len/binary, Rest/bits >> = Data,
	Unmasked2 = websocket_unmask(End,
		rotate_mask_key(MaskKey, UnmaskedLen), <<>>),
	{Unmasked3, State2} = websocket_inflate_frame(Unmasked2, Rsv, Fin =:= fin, State),
	case is_utf8(<< Incomplete/binary, Unmasked3/binary >>) of
		<<>> ->
			websocket_dispatch(State2#state{utf8_state= <<>>},
				Req, HandlerState, Rest, Opcode,
				<< Unmasked/binary, Unmasked3/binary >>);
		Utf8State when is_binary(Utf8State), Fin =:= nofin ->
			websocket_dispatch(State2#state{utf8_state=Utf8State},
				Req, HandlerState, Rest, Opcode,
				<< Unmasked/binary, Unmasked3/binary >>);
		_ ->
			websocket_close(State, Req, HandlerState, {error, badencoding})
	end;
%% Other frames have a binary payload.
websocket_payload(State, Req, HandlerState,
		Opcode, Len, MaskKey, Unmasked, UnmaskedLen, Data, Rsv)
		when byte_size(Data) < Len ->
	Unmasked2 = websocket_unmask(Data,
		rotate_mask_key(MaskKey, UnmaskedLen), <<>>),
	{Unmasked3, State2} = websocket_inflate_frame(Unmasked2, Rsv, false, State),
	websocket_payload_loop(State2, Req, HandlerState,
		Opcode, Len - byte_size(Data), MaskKey,
		<< Unmasked/binary, Unmasked3/binary >>, UnmaskedLen + byte_size(Data),
		Rsv);
websocket_payload(State, Req, HandlerState,
		Opcode, Len, MaskKey, Unmasked, UnmaskedLen, Data, Rsv) ->
	<< End:Len/binary, Rest/bits >> = Data,
	Unmasked2 = websocket_unmask(End,
		rotate_mask_key(MaskKey, UnmaskedLen), <<>>),
	{Unmasked3, State2} = websocket_inflate_frame(Unmasked2, Rsv, true, State),
	websocket_dispatch(State2, Req, HandlerState, Rest, Opcode,
		<< Unmasked/binary, Unmasked3/binary >>).

-spec websocket_inflate_frame(binary(), rsv(), boolean(), #state{}) ->
		{binary(), #state{}}.
websocket_inflate_frame(Data, << Rsv1:1, _:2 >>, _,
		#state{deflate_frame = DeflateFrame} = State)
		when DeflateFrame =:= false orelse Rsv1 =:= 0 ->
	{Data, State};
websocket_inflate_frame(Data, << 1:1, _:2 >>, false, State) ->
	Result = zlib:inflate(State#state.inflate_state, Data),
	{iolist_to_binary(Result), State};
websocket_inflate_frame(Data, << 1:1, _:2 >>, true, State) ->
	Result = zlib:inflate(State#state.inflate_state,
		<< Data/binary, 0:8, 0:8, 255:8, 255:8 >>),
	{iolist_to_binary(Result), State}.

-spec websocket_unmask(B, mask_key(), B) -> B when B::binary().
websocket_unmask(<<>>, _, Unmasked) ->
	Unmasked;
websocket_unmask(<< O:32, Rest/bits >>, MaskKey, Acc) ->
	T = O bxor MaskKey,
	websocket_unmask(Rest, MaskKey, << Acc/binary, T:32 >>);
websocket_unmask(<< O:24 >>, MaskKey, Acc) ->
	<< MaskKey2:24, _:8 >> = << MaskKey:32 >>,
	T = O bxor MaskKey2,
	<< Acc/binary, T:24 >>;
websocket_unmask(<< O:16 >>, MaskKey, Acc) ->
	<< MaskKey2:16, _:16 >> = << MaskKey:32 >>,
	T = O bxor MaskKey2,
	<< Acc/binary, T:16 >>;
websocket_unmask(<< O:8 >>, MaskKey, Acc) ->
	<< MaskKey2:8, _:24 >> = << MaskKey:32 >>,
	T = O bxor MaskKey2,
	<< Acc/binary, T:8 >>.

%% Because we unmask on the fly we need to continue from the right mask byte.
-spec rotate_mask_key(mask_key(), non_neg_integer()) -> mask_key().
rotate_mask_key(MaskKey, UnmaskedLen) ->
	Left = UnmaskedLen rem 4,
	Right = 4 - Left,
	(MaskKey bsl (Left * 8)) + (MaskKey bsr (Right * 8)).

%% Returns <<>> if the argument is valid UTF-8, false if not,
%% or the incomplete part of the argument if we need more data.
-spec is_utf8(binary()) -> false | binary().
is_utf8(Valid = <<>>) ->
	Valid;
is_utf8(<< _/utf8, Rest/binary >>) ->
	is_utf8(Rest);
%% 2 bytes. Codepages C0 and C1 are invalid; fail early.
is_utf8(<< 2#1100000:7, _/bits >>) ->
	false;
is_utf8(Incomplete = << 2#110:3, _:5 >>) ->
	Incomplete;
%% 3 bytes.
is_utf8(Incomplete = << 2#1110:4, _:4 >>) ->
	Incomplete;
is_utf8(Incomplete = << 2#1110:4, _:4, 2#10:2, _:6 >>) ->
	Incomplete;
%% 4 bytes. Codepage F4 may have invalid values greater than 0x10FFFF.
is_utf8(<< 2#11110100:8, 2#10:2, High:6, _/bits >>) when High >= 2#10000 ->
	false;
is_utf8(Incomplete = << 2#11110:5, _:3 >>) ->
	Incomplete;
is_utf8(Incomplete = << 2#11110:5, _:3, 2#10:2, _:6 >>) ->
	Incomplete;
is_utf8(Incomplete = << 2#11110:5, _:3, 2#10:2, _:6, 2#10:2, _:6 >>) ->
	Incomplete;
%% Invalid.
is_utf8(_) ->
	false.

-spec websocket_payload_loop(#state{}, Req, any(),
		opcode(), non_neg_integer(), mask_key(), binary(),
		non_neg_integer(), rsv())
	-> {ok, Req, cowboy_middleware:env()}
	| {suspend, module(), atom(), [any()]}
	when Req::cowboy_req:req().
websocket_payload_loop(State=#state{socket=Socket, transport=Transport,
		messages={OK, Closed, Error}, timeout_ref=TRef},
		Req, HandlerState, Opcode, Len, MaskKey, Unmasked, UnmaskedLen, Rsv) ->
	Transport:setopts(Socket, [{active, once}]),
	receive
		{OK, Socket, Data} ->
			State2 = handler_loop_timeout(State),
			websocket_payload(State2, Req, HandlerState,
				Opcode, Len, MaskKey, Unmasked, UnmaskedLen, Data, Rsv);
		{Closed, Socket} ->
			handler_terminate(State, Req, HandlerState, {error, closed});
		{Error, Socket, Reason} ->
			handler_terminate(State, Req, HandlerState, {error, Reason});
		{timeout, TRef, ?MODULE} ->
			websocket_close(State, Req, HandlerState, {normal, timeout});
		{timeout, OlderTRef, ?MODULE} when is_reference(OlderTRef) ->
			websocket_payload_loop(State, Req, HandlerState,
				Opcode, Len, MaskKey, Unmasked, UnmaskedLen, Rsv);
		Message ->
			handler_call(State, Req, HandlerState,
				<<>>, websocket_info, Message,
				fun (State2, Req2, HandlerState2, _) ->
					websocket_payload_loop(State2, Req2, HandlerState2,
						Opcode, Len, MaskKey, Unmasked, UnmaskedLen, Rsv)
				end)
	end.

-spec websocket_dispatch(#state{}, Req, any(), binary(), opcode(), binary())
	-> {ok, Req, cowboy_middleware:env()}
	| {suspend, module(), atom(), [any()]}
	when Req::cowboy_req:req().
%% Continuation frame.
websocket_dispatch(State=#state{frag_state={nofin, Opcode, SoFar}},
		Req, HandlerState, RemainingData, 0, Payload) ->
	websocket_data(State#state{frag_state={nofin, Opcode,
		<< SoFar/binary, Payload/binary >>}}, Req, HandlerState, RemainingData);
%% Last continuation frame.
websocket_dispatch(State=#state{frag_state={fin, Opcode, SoFar}},
		Req, HandlerState, RemainingData, 0, Payload) ->
	websocket_dispatch(State#state{frag_state=undefined}, Req, HandlerState,
		RemainingData, Opcode, << SoFar/binary, Payload/binary >>);
%% Text frame.
websocket_dispatch(State, Req, HandlerState, RemainingData, 1, Payload) ->
	handler_call(State, Req, HandlerState, RemainingData,
		websocket_handle, {text, Payload}, fun websocket_data/4);
%% Binary frame.
websocket_dispatch(State, Req, HandlerState, RemainingData, 2, Payload) ->
	handler_call(State, Req, HandlerState, RemainingData,
		websocket_handle, {binary, Payload}, fun websocket_data/4);
%% Close control frame.
websocket_dispatch(State, Req, HandlerState, _RemainingData, 8, <<>>) ->
	websocket_close(State, Req, HandlerState, {remote, closed});
websocket_dispatch(State, Req, HandlerState, _RemainingData, 8,
		<< Code:16, Payload/bits >>) ->
	websocket_close(State, Req, HandlerState, {remote, Code, Payload});
%% Ping control frame. Send a pong back and forward the ping to the handler.
websocket_dispatch(State=#state{socket=Socket, transport=Transport},
		Req, HandlerState, RemainingData, 9, Payload) ->
	Len = payload_length_to_binary(iolist_size(Payload)),
	Transport:send(Socket, << 1:1, 0:3, 10:4, 0:1, Len/bits, Payload/binary >>),
	handler_call(State, Req, HandlerState, RemainingData,
		websocket_handle, {ping, Payload}, fun websocket_data/4);
%% Pong control frame.
websocket_dispatch(State, Req, HandlerState, RemainingData, 10, Payload) ->
	handler_call(State, Req, HandlerState, RemainingData,
		websocket_handle, {pong, Payload}, fun websocket_data/4).

-spec handler_call(#state{}, Req, any(), binary(), atom(), any(), fun())
	-> {ok, Req, cowboy_middleware:env()}
	| {suspend, module(), atom(), [any()]}
	when Req::cowboy_req:req().
handler_call(State=#state{handler=Handler}, Req, HandlerState,
		RemainingData, Callback, Message, NextState) ->
	try Handler:Callback(Message, Req, HandlerState) of
		{ok, Req2, HandlerState2} ->
			NextState(State, Req2, HandlerState2, RemainingData);
		{ok, Req2, HandlerState2, hibernate} ->
			NextState(State#state{hibernate=true},
				Req2, HandlerState2, RemainingData);
		{reply, Payload, Req2, HandlerState2}
				when is_list(Payload) ->
			case websocket_send_many(Payload, State) of
				{ok, State2} ->
					NextState(State2, Req2, HandlerState2, RemainingData);
				{shutdown, State2} ->
					handler_terminate(State2, Req2, HandlerState2,
						{normal, shutdown});
				{{error, _} = Error, State2} ->
					handler_terminate(State2, Req2, HandlerState2, Error)
			end;
		{reply, Payload, Req2, HandlerState2, hibernate}
				when is_list(Payload) ->
			case websocket_send_many(Payload, State) of
				{ok, State2} ->
					NextState(State2#state{hibernate=true},
						Req2, HandlerState2, RemainingData);
				{shutdown, State2} ->
					handler_terminate(State2, Req2, HandlerState2,
						{normal, shutdown});
				{{error, _} = Error, State2} ->
					handler_terminate(State2, Req2, HandlerState2, Error)
			end;
		{reply, Payload, Req2, HandlerState2} ->
			case websocket_send(Payload, State) of
				{ok, State2} ->
					NextState(State2, Req2, HandlerState2, RemainingData);
				{shutdown, State2} ->
					handler_terminate(State2, Req2, HandlerState2,
						{normal, shutdown});
				{{error, _} = Error, State2} ->
					handler_terminate(State2, Req2, HandlerState2, Error)
			end;
		{reply, Payload, Req2, HandlerState2, hibernate} ->
			case websocket_send(Payload, State) of
				{ok, State2} ->
					NextState(State2#state{hibernate=true},
						Req2, HandlerState2, RemainingData);
				{shutdown, State2} ->
					handler_terminate(State2, Req2, HandlerState2,
						{normal, shutdown});
				{{error, _} = Error, State2} ->
					handler_terminate(State2, Req2, HandlerState2, Error)
			end;
		{shutdown, Req2, HandlerState2} ->
			websocket_close(State, Req2, HandlerState2, {normal, shutdown})
	catch Class:Reason ->
		_ = websocket_close(State, Req, HandlerState, {error, handler}),
		erlang:Class([
			{reason, Reason},
			{mfa, {Handler, Callback, 3}},
			{stacktrace, erlang:get_stacktrace()},
			{msg, Message},
			{req, cowboy_req:to_list(Req)},
			{state, HandlerState}
		])
	end.

websocket_opcode(text) -> 1;
websocket_opcode(binary) -> 2;
websocket_opcode(close) -> 8;
websocket_opcode(ping) -> 9;
websocket_opcode(pong) -> 10.

-spec websocket_deflate_frame(opcode(), binary(), #state{}) ->
	{binary(), rsv(), #state{}}.
websocket_deflate_frame(Opcode, Payload,
		State=#state{deflate_frame = DeflateFrame})
		when DeflateFrame =:= false orelse Opcode >= 8 ->
	{Payload, << 0:3 >>, State};
websocket_deflate_frame(_, Payload, State=#state{deflate_state = Deflate}) ->
	Deflated = iolist_to_binary(zlib:deflate(Deflate, Payload, sync)),
	DeflatedBodyLength = erlang:size(Deflated) - 4,
	Deflated1 = case Deflated of
		<< Body:DeflatedBodyLength/binary, 0:8, 0:8, 255:8, 255:8 >> -> Body;
		_ -> Deflated
	end,
	{Deflated1, << 1:1, 0:2 >>, State}.

-spec websocket_send(frame(), #state{})
-> {ok, #state{}} | {shutdown, #state{}} | {{error, atom()}, #state{}}.
websocket_send(Type, State=#state{socket=Socket, transport=Transport})
		when Type =:= close ->
	Opcode = websocket_opcode(Type),
	case Transport:send(Socket, << 1:1, 0:3, Opcode:4, 0:8 >>) of
		ok -> {shutdown, State};
		Error -> {Error, State}
	end;
websocket_send(Type, State=#state{socket=Socket, transport=Transport})
		when Type =:= ping; Type =:= pong ->
	Opcode = websocket_opcode(Type),
	{Transport:send(Socket, << 1:1, 0:3, Opcode:4, 0:8 >>), State};
websocket_send({close, Payload}, State) ->
	websocket_send({close, 1000, Payload}, State);
websocket_send({Type = close, StatusCode, Payload}, State=#state{
		socket=Socket, transport=Transport}) ->
	Opcode = websocket_opcode(Type),
	Len = 2 + iolist_size(Payload),
	%% Control packets must not be > 125 in length.
	true = Len =< 125,
	BinLen = payload_length_to_binary(Len),
	Transport:send(Socket,
		[<< 1:1, 0:3, Opcode:4, 0:1, BinLen/bits, StatusCode:16 >>, Payload]),
	{shutdown, State};
websocket_send({Type, Payload0}, State=#state{socket=Socket, transport=Transport}) ->
	Opcode = websocket_opcode(Type),
	{Payload, Rsv, State2} = websocket_deflate_frame(Opcode, iolist_to_binary(Payload0), State),
	Len = iolist_size(Payload),
	%% Control packets must not be > 125 in length.
	true = if Type =:= ping; Type =:= pong ->
			Len =< 125;
		true ->
			true
	end,
	BinLen = payload_length_to_binary(Len),
	{Transport:send(Socket,
		[<< 1:1, Rsv/bits, Opcode:4, 0:1, BinLen/bits >>, Payload]), State2}.

-spec websocket_send_many([frame()], #state{})
	-> {ok, #state{}} | {shutdown, #state{}} | {{error, atom()}, #state{}}.
websocket_send_many([], State) ->
	{ok, State};
websocket_send_many([Frame|Tail], State) ->
	case websocket_send(Frame, State) of
		{ok, State2} -> websocket_send_many(Tail, State2);
		{shutdown, State2} -> {shutdown, State2};
		{Error, State2} -> {Error, State2}
	end.

-spec websocket_close(#state{}, Req, any(), terminate_reason())
	-> {ok, Req, cowboy_middleware:env()}
	when Req::cowboy_req:req().
websocket_close(State=#state{socket=Socket, transport=Transport},
		Req, HandlerState, Reason) ->
	case Reason of
		{normal, _} ->
			Transport:send(Socket, << 1:1, 0:3, 8:4, 0:1, 2:7, 1000:16 >>);
		{error, badframe} ->
			Transport:send(Socket, << 1:1, 0:3, 8:4, 0:1, 2:7, 1002:16 >>);
		{error, badencoding} ->
			Transport:send(Socket, << 1:1, 0:3, 8:4, 0:1, 2:7, 1007:16 >>);
		{error, handler} ->
			Transport:send(Socket, << 1:1, 0:3, 8:4, 0:1, 2:7, 1011:16 >>);
		{remote, closed} ->
			Transport:send(Socket, << 1:1, 0:3, 8:4, 0:8 >>);
		{remote, Code, _} ->
			Transport:send(Socket, << 1:1, 0:3, 8:4, 0:1, 2:7, Code:16 >>)
	end,
	handler_terminate(State, Req, HandlerState, Reason).

-spec handler_terminate(#state{}, Req, any(), terminate_reason())
	-> {ok, Req, cowboy_middleware:env()}
	when Req::cowboy_req:req().
handler_terminate(#state{env=Env, handler=Handler},
		Req, HandlerState, TerminateReason) ->
	try
		Handler:websocket_terminate(TerminateReason, Req, HandlerState)
	catch Class:Reason ->
		erlang:Class([
			{reason, Reason},
			{mfa, {Handler, websocket_terminate, 3}},
			{stacktrace, erlang:get_stacktrace()},
			{req, cowboy_req:to_list(Req)},
			{state, HandlerState},
			{terminate_reason, TerminateReason}
		])
	end,
	{ok, Req, [{result, closed}|Env]}.

-spec payload_length_to_binary(0..16#7fffffffffffffff)
	-> << _:7 >> | << _:23 >> | << _:71 >>.
payload_length_to_binary(N) ->
	case N of
		N when N =< 125 -> << N:7 >>;
		N when N =< 16#ffff -> << 126:7, N:16 >>;
		N when N =< 16#7fffffffffffffff -> << 127:7, N:64 >>
	end.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted deps/cowboy/src/cowboy_websocket_handler.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
%% Copyright (c) 2011-2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(cowboy_websocket_handler).

-type opts() :: any().
-type state() :: any().
-type terminate_reason() :: {normal, shutdown}
	| {normal, timeout}
	| {error, closed}
	| {remote, closed}
	| {remote, cowboy_websocket:close_code(), binary()}
	| {error, badencoding}
	| {error, badframe}
	| {error, atom()}.

-callback websocket_init(atom(), Req, opts())
	-> {ok, Req, state()}
	| {ok, Req, state(), hibernate}
	| {ok, Req, state(), timeout()}
	| {ok, Req, state(), timeout(), hibernate}
	| {shutdown, Req}
	when Req::cowboy_req:req().
-callback websocket_handle({text | binary | ping | pong, binary()}, Req, State)
	-> {ok, Req, State}
	| {ok, Req, State, hibernate}
	| {reply, cowboy_websocket:frame() | [cowboy_websocket:frame()], Req, State}
	| {reply, cowboy_websocket:frame() | [cowboy_websocket:frame()], Req, State, hibernate}
	| {shutdown, Req, State}
	when Req::cowboy_req:req(), State::state().
-callback websocket_info(any(), Req, State)
	-> {ok, Req, State}
	| {ok, Req, State, hibernate}
	| {reply, cowboy_websocket:frame() | [cowboy_websocket:frame()], Req, State}
	| {reply, cowboy_websocket:frame() | [cowboy_websocket:frame()], Req, State, hibernate}
	| {shutdown, Req, State}
	when Req::cowboy_req:req(), State::state().
-callback websocket_terminate(terminate_reason(), cowboy_req:req(), state())
	-> ok.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




































































































Deleted deps/cowlib/LICENSE.

1
2
3
4
5
6
7
8
9
10
11
12
13
Copyright (c) 2013-2014, Loïc Hoguin <essen@ninenines.eu>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
<
<
<
<
<
<
<
<
<
<
<
<
<


























Deleted deps/cowlib/Makefile.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# See LICENSE for licensing information.

PROJECT = cowlib
PLT_APPS = crypto
CI_OTP = OTP_R15B OTP_R15B01 OTP_R15B02 OTP_R15B03-1 OTP_R16B OTP_R16B01 OTP_R16B02 OTP_R16B03-1 OTP-17.0.2 OTP-17.1.2 OTP-17.2.2 OTP-17.3.4 OTP-17.4.1 OTP-17.5.6.3 OTP-18.0.3

include erlang.mk

TEST_ERLC_OPTS += +'{parse_transform, eunit_autoexport}' -DEXTRA=1

.PHONY: gen perfs

# Mimetypes module generator.

GEN_URL = http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
GEN_SRC = src/cow_mimetypes.erl.src
GEN_OUT = src/cow_mimetypes.erl

gen:
	$(gen_verbose) cat $(GEN_SRC) \
		| head -n `grep -n "%% GENERATED" $(GEN_SRC) | cut -d : -f 1` \
		> $(GEN_OUT)
	$(gen_verbose) wget -qO - $(GEN_URL) \
		| grep -v ^# \
		| awk '{for (i=2; i<=NF; i++) if ($$i != "") { \
			split($$1, a, "/"); \
			print "all_ext(<<\"" $$i "\">>) -> {<<\"" \
				a[1] "\">>, <<\"" a[2] "\">>, []};"}}' \
		| sort \
		| uniq -w 25 \
		>> $(GEN_OUT)
	$(gen_verbose) cat $(GEN_SRC) \
		| tail -n +`grep -n "%% GENERATED" $(GEN_SRC) | cut -d : -f 1` \
		>> $(GEN_OUT)

# Performance testing.

deps/horse:
	git clone -n -- https://github.com/extend/horse $(DEPS_DIR)/horse
	cd $(DEPS_DIR)/horse ; git checkout -q master
	$(MAKE) -C $(DEPS_DIR)/horse

perfs: ERLC_OPTS += -DPERF=1 +'{parse_transform, horse_autoexport}' -DEXTRA=1
perfs: clean deps deps/horse app
	$(gen_verbose) erl -noshell -pa ebin deps/horse/ebin \
		-eval 'horse:app_perf($(PROJECT)), init:stop().'
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




























































































Deleted deps/cowlib/README.md.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Cowlib
======

Cowlib is a support library for manipulating Web protocols.

Goals
-----

Cowlib provides libraries for parsing and building messages
for various Web protocols, including SPDY, HTTP and Websocket.

It is optimized for completeness rather than speed. No value
is ignored, they are all returned.

Support
-------

 *  Official IRC Channel: #ninenines on irc.freenode.net
 *  [Mailing Lists](http://lists.ninenines.eu)
 *  [Commercial Support](http://ninenines.eu/support)
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<








































Deleted deps/cowlib/ebin/cow_cookie.beam.

cannot compute difference between binary files

Deleted deps/cowlib/ebin/cow_date.beam.

cannot compute difference between binary files

Deleted deps/cowlib/ebin/cow_http.beam.

cannot compute difference between binary files

Deleted deps/cowlib/ebin/cow_http_hd.beam.

cannot compute difference between binary files

Deleted deps/cowlib/ebin/cow_http_te.beam.

cannot compute difference between binary files

Deleted deps/cowlib/ebin/cow_mimetypes.beam.

cannot compute difference between binary files

Deleted deps/cowlib/ebin/cow_multipart.beam.

cannot compute difference between binary files

Deleted deps/cowlib/ebin/cow_qs.beam.

cannot compute difference between binary files

Deleted deps/cowlib/ebin/cow_spdy.beam.

cannot compute difference between binary files

Deleted deps/cowlib/ebin/cowlib.app.

1
2
3
4
5
6
7
8
{application,cowlib,
             [{description,"Support library for manipulating Web protocols."},
              {vsn,"1.0.2"},
              {id,"git"},
              {modules,[cow_cookie,cow_date,cow_http,cow_http_hd,cow_http_te,
                        cow_mimetypes,cow_multipart,cow_qs,cow_spdy]},
              {registered,[]},
              {applications,[kernel,stdlib,crypto]}]}.
<
<
<
<
<
<
<
<
















Deleted deps/cowlib/erlang.mk.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
4526
4527
4528
4529
4530
4531
4532
4533
4534
4535
4536
4537
4538
4539
4540
4541
4542
4543
4544
4545
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
4560
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
4576
4577
4578
4579
4580
4581
4582
4583
4584
4585
4586
4587
4588
4589
4590
4591
4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606
4607
4608
4609
4610
4611
4612
4613
4614
4615
4616
4617
4618
4619
4620
4621
4622
4623
4624
4625
4626
4627
4628
4629
4630
4631
4632
4633
4634
4635
4636
4637
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651
4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
4669
4670
4671
4672
4673
4674
4675
4676
4677
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
4693
4694
4695
4696
4697
4698
4699
4700
4701
4702
4703
4704
4705
4706
4707
4708
4709
4710
4711
4712
4713
4714
4715
4716
4717
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
4732
4733
4734
4735
4736
4737
4738
4739
4740
4741
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771
4772
4773
4774
4775
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
4794
4795
4796
4797
4798
4799
4800
4801
4802
4803
4804
4805
4806
4807
4808
4809
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819
4820
4821
4822
4823
4824
4825
4826
4827
4828
4829
4830
4831
4832
4833
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
4847
4848
4849
4850
4851
4852
4853
4854
4855
4856
4857
4858
4859
4860
4861
4862
4863
4864
4865
4866
4867
4868
4869
4870
4871
4872
4873
4874
4875
4876
4877
4878
4879
4880
4881
4882
4883
4884
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
4915
4916
4917
4918
4919
4920
4921
4922
4923
4924
4925
4926
4927
4928
4929
4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960
4961
4962
4963
4964
4965
4966
4967
4968
4969
4970
4971
4972
4973
4974
4975
4976
4977
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987
4988
4989
4990
4991
4992
4993
4994
4995
4996
4997
4998
4999
5000
5001
5002
5003
5004
5005
5006
5007
5008
5009
5010
5011
5012
5013
5014
5015
5016
5017
5018
5019
5020
5021
5022
5023
5024
5025
5026
5027
5028
5029
5030
5031
5032
5033
5034
5035
5036
5037
5038
5039
5040
5041
5042
5043
5044
5045
5046
5047
5048
5049
5050
5051
5052
5053
5054
5055
5056
5057
5058
5059
5060
5061
5062
5063
5064
5065
5066
5067
5068
5069
5070
5071
5072
5073
5074
5075
5076
5077
5078
5079
5080
5081
5082
5083
5084
5085
5086
5087
5088
5089
5090
5091
5092
5093
5094
5095
5096
5097
5098
5099
5100
5101
5102
5103
5104
5105
5106
5107
5108
5109
5110
5111
5112
5113
5114
5115
5116
5117
5118
5119
5120
5121
5122
5123
5124
5125
5126
5127
5128
5129
5130
5131
5132
5133
5134
5135
5136
5137
5138
5139
5140
5141
5142
5143
5144
5145
5146
5147
5148
5149
5150
5151
5152
5153
5154
5155
5156
5157
5158
5159
5160
5161
5162
5163
5164
5165
5166
5167
5168
5169
5170
5171
5172
5173
5174
5175
5176
5177
5178
5179
5180
5181
5182
5183
5184
5185
5186
5187
5188
5189
5190
5191
5192
5193
5194
5195
5196
5197
5198
5199
5200
5201
5202
5203
5204
5205
5206
5207
5208
5209
5210
5211
5212
5213
5214
5215
5216
5217
5218
5219
5220
5221
5222
5223
5224
5225
5226
5227
5228
5229
5230
5231
5232
5233
5234
5235
5236
5237
5238
5239
5240
5241
5242
5243
5244
5245
5246
5247
5248
5249
5250
5251
5252
5253
5254
5255
5256
5257
5258
5259
5260
5261
5262
5263
5264
5265
5266
5267
5268
5269
5270
5271
5272
5273
5274
5275
5276
5277
5278
5279
5280
5281
5282
5283
5284
5285
5286
5287
5288
5289
5290
5291
5292
5293
5294
5295
5296
5297
5298
5299
5300
5301
5302
5303
5304
5305
5306
5307
5308
5309
5310
5311
5312
5313
5314
5315
5316
5317
5318
5319
5320
5321
5322
5323
5324
5325
5326
5327
5328
5329
5330
5331
5332
5333
5334
5335
5336
5337
5338
5339
5340
5341
5342
5343
5344
5345
5346
5347
5348
5349
5350
5351
5352
5353
5354
5355
5356
5357
5358
5359
5360
5361
5362
5363
5364
5365
5366
5367
5368
5369
5370
5371
5372
5373
5374
5375
5376
5377
5378
5379
5380
5381
5382
5383
5384
5385
5386
5387
5388
5389
5390
5391
5392
5393
5394
5395
5396
5397
5398
5399
5400
5401
5402
5403
5404
5405
5406
5407
5408
5409
5410
5411
5412
5413
5414
5415
5416
5417
5418
5419
5420
5421
5422
5423
5424
5425
5426
5427
5428
5429
5430
5431
5432
5433
5434
5435
5436
5437
5438
5439
5440
5441
5442
5443
5444
5445
5446
5447
5448
5449
5450
5451
5452
5453
5454
5455
5456
5457
5458
5459
5460
5461
5462
5463
5464
5465
5466
5467
5468
5469
5470
5471
5472
5473
5474
5475
5476
5477
5478
5479
5480
5481
5482
5483
5484
5485
5486
5487
5488
5489
5490
5491
5492
5493
5494
5495
5496
5497
5498
5499
5500
5501
5502
5503
5504
5505
5506
5507
5508
5509
5510
5511
5512
5513
5514
5515
5516
5517
5518
5519
5520
5521
5522
5523
5524
5525
5526
5527
5528
5529
5530
5531
5532
5533
5534
5535
5536
5537
5538
5539
5540
5541
5542
5543
5544
5545
5546
5547
5548
5549
5550
5551
5552
5553
5554
5555
5556
5557
5558
5559
5560
5561
5562
5563
5564
5565
5566
5567
5568
5569
5570
5571
5572
5573
5574
5575
5576
5577
5578
5579
5580
5581
5582
5583
5584
5585
5586
5587
5588
5589
5590
5591
5592
5593
5594
5595
5596
5597
5598
5599
5600
5601
5602
5603
5604
5605
5606
5607
5608
5609
5610
5611
5612
5613
5614
5615
5616
5617
5618
5619
5620
5621
5622
5623
5624
5625
5626
5627
5628
5629
5630
5631
5632
5633
5634
5635
5636
5637
5638
5639
5640
5641
5642
5643
5644
5645
5646
5647
5648
5649
5650
5651
5652
5653
5654
5655
5656
5657
5658
5659
5660
5661
5662
5663
5664
5665
5666
5667
5668
5669
5670
5671
5672
5673
5674
5675
5676
5677
5678
5679
5680
5681
5682
5683
5684
5685
5686
5687
5688
5689
5690
5691
5692
5693
5694
5695
5696
5697
5698
5699
5700
5701
5702
5703
5704
5705
5706
5707
5708
5709
5710
5711
5712
5713
5714
5715
5716
5717
5718
5719
5720
5721
5722
5723
5724
5725
5726
5727
5728
5729
5730
5731
5732
5733
5734
5735
5736
5737
5738
5739
5740
5741
5742
5743
5744
5745
5746
5747
5748
5749
5750
5751
5752
5753
5754
5755
5756
5757
5758
5759
5760
5761
5762
5763
5764
5765
5766
5767
5768
5769
5770
5771
5772
5773
5774
5775
5776
5777
5778
5779
5780
5781
5782
5783
5784
5785
5786
5787
5788
5789
5790
5791
5792
5793
5794
5795
5796
5797
5798
5799
5800
5801
5802
5803
5804
5805
5806
5807
5808
5809
5810
5811
5812
5813
5814
5815
5816
5817
5818
5819
5820
5821
5822
5823
5824
5825
5826
5827
5828
5829
5830
5831
5832
5833
5834
5835
5836
5837
5838
5839
5840
5841
5842
5843
5844
5845
5846
5847
5848
5849
5850
5851
5852
5853
5854
5855
5856
5857
5858
5859
5860
5861
5862
5863
5864
5865
5866
5867
5868
5869
5870
5871
5872
5873
5874
5875
5876
5877
5878
5879
5880
5881
5882
5883
5884
5885
5886
5887
5888
5889
5890
5891
5892
5893
5894
5895
5896
5897
5898
5899
5900
5901
5902
5903
5904
5905
5906
5907
5908
5909
5910
5911
5912
5913
5914
5915
5916
5917
5918
5919
5920
5921
5922
5923
5924
5925
5926
5927
5928
5929
5930
5931
5932
5933
5934
5935
5936
5937
5938
5939
5940
5941
5942
5943
5944
5945
5946
5947
5948
5949
5950
5951
5952
5953
5954
5955
5956
5957
5958
5959
5960
5961
5962
5963
5964
5965
5966
5967
5968
5969
5970
5971
5972
5973
5974
5975
5976
5977
5978
5979
5980
5981
5982
5983
5984
5985
5986
5987
5988
5989
5990
5991
5992
5993
5994
5995
5996
5997
5998
5999
6000
6001
6002
6003
6004
6005
6006
6007
6008
6009
6010
6011
6012
6013
6014
6015
6016
6017
6018
6019
6020
6021
6022
6023
6024
6025
6026
6027
6028
6029
6030
6031
6032
6033
6034
6035
6036
6037
6038
6039
6040
6041
6042
6043
6044
6045
6046
6047
6048
6049
6050
6051
6052
6053
6054
6055
6056
# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

.PHONY: all app deps search rel docs install-docs check tests clean distclean help erlang-mk

ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))

ERLANG_MK_VERSION = 1.2.0-646-gcf43676

# Core configuration.

PROJECT ?= $(notdir $(CURDIR))
PROJECT := $(strip $(PROJECT))

PROJECT_VERSION ?= rolling

# Verbosity.

V ?= 0

verbose_0 = @
verbose = $(verbose_$(V))

gen_verbose_0 = @echo " GEN   " $@;
gen_verbose = $(gen_verbose_$(V))

# Temporary files directory.

ERLANG_MK_TMP ?= $(CURDIR)/.erlang.mk
export ERLANG_MK_TMP

# "erl" command.

ERL = erl +A0 -noinput -boot start_clean

# Platform detection.
# @todo Add Windows/Cygwin detection eventually.

ifeq ($(PLATFORM),)
UNAME_S := $(shell uname -s)

ifeq ($(UNAME_S),Linux)
PLATFORM = linux
else ifeq ($(UNAME_S),Darwin)
PLATFORM = darwin
else ifeq ($(UNAME_S),SunOS)
PLATFORM = solaris
else ifeq ($(UNAME_S),GNU)
PLATFORM = gnu
else ifeq ($(UNAME_S),FreeBSD)
PLATFORM = freebsd
else ifeq ($(UNAME_S),NetBSD)
PLATFORM = netbsd
else ifeq ($(UNAME_S),OpenBSD)
PLATFORM = openbsd
else
$(error Unable to detect platform. Please open a ticket with the output of uname -a.)
endif

export PLATFORM
endif

# Core targets.

ifneq ($(words $(MAKECMDGOALS)),1)
.NOTPARALLEL:
endif

all:: deps
	$(verbose) $(MAKE) --no-print-directory app
	$(verbose) $(MAKE) --no-print-directory rel

# Noop to avoid a Make warning when there's nothing to do.
rel::
	$(verbose) echo -n

check:: clean app tests

clean:: clean-crashdump

clean-crashdump:
ifneq ($(wildcard erl_crash.dump),)
	$(gen_verbose) rm -f erl_crash.dump
endif

distclean:: clean

help::
	$(verbose) printf "%s\n" \
		"erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
		"Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \
		"" \
		"Usage: [V=1] $(MAKE) [-jNUM] [target]..." \
		"" \
		"Core targets:" \
		"  all           Run deps, app and rel targets in that order" \
		"  app           Compile the project" \
		"  deps          Fetch dependencies (if needed) and compile them" \
		"  search q=...  Search for a package in the built-in index" \
		"  rel           Build a release for this project, if applicable" \
		"  docs          Build the documentation for this project" \
		"  install-docs  Install the man pages for this project" \
		"  check         Compile and run all tests and analysis for this project" \
		"  tests         Run the tests for this project" \
		"  clean         Delete temporary and output files from most targets" \
		"  distclean     Delete all temporary and output files" \
		"  help          Display this help and exit" \
		"  erlang-mk     Update erlang.mk to the latest version"

# Core functions.

empty :=
space := $(empty) $(empty)
tab := $(empty)	$(empty)
comma := ,

define newline


endef

define comma_list
$(subst $(space),$(comma),$(strip $(1)))
endef

# Adding erlang.mk to make Erlang scripts who call init:get_plain_arguments() happy.
define erlang
$(ERL) $(2) -pz $(ERLANG_MK_TMP)/rebar/ebin -eval "$(subst $(newline),,$(subst ",\",$(1)))" -- erlang.mk
endef

ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
define core_http_get
	wget --no-check-certificate -O $(1) $(2)|| rm $(1)
endef
else
define core_http_get.erl
	ssl:start(),
	inets:start(),
	case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
		{ok, {{_, 200, _}, _, Body}} ->
			case file:write_file("$(1)", Body) of
				ok -> ok;
				{error, R1} -> halt(R1)
			end;
		{error, R2} ->
			halt(R2)
	end,
	halt(0).
endef

define core_http_get
	$(call erlang,$(call core_http_get.erl,$(1),$(2)))
endef
endif

core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))

core_find = $(foreach d,$(call core_ls,$1*),$(call core_find,$d/,$2) $(filter $(subst *,%,$2),$d))

core_lc = $(subst A,a,$(subst B,b,$(subst C,c,$(subst D,d,$(subst E,e,$(subst F,f,$(subst G,g,$(subst H,h,$(subst I,i,$(subst J,j,$(subst K,k,$(subst L,l,$(subst M,m,$(subst N,n,$(subst O,o,$(subst P,p,$(subst Q,q,$(subst R,r,$(subst S,s,$(subst T,t,$(subst U,u,$(subst V,v,$(subst W,w,$(subst X,x,$(subst Y,y,$(subst Z,z,$(1)))))))))))))))))))))))))))

# @todo On Windows: $(shell dir /B $(1)); make sure to handle when no file exists.
core_ls = $(filter-out $(1),$(shell echo $(1)))

# Automated update.

ERLANG_MK_BUILD_CONFIG ?= build.config
ERLANG_MK_BUILD_DIR ?= .erlang.mk.build

erlang-mk:
	git clone https://github.com/ninenines/erlang.mk $(ERLANG_MK_BUILD_DIR)
	if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR); fi
	cd $(ERLANG_MK_BUILD_DIR) && $(MAKE)
	cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
	rm -rf $(ERLANG_MK_BUILD_DIR)

# The erlang.mk package index is bundled in the default erlang.mk build.
# Search for the string "copyright" to skip to the rest of the code.

PACKAGES += aberth
pkg_aberth_name = aberth
pkg_aberth_description = Generic BERT-RPC server in Erlang
pkg_aberth_homepage = https://github.com/a13x/aberth
pkg_aberth_fetch = git
pkg_aberth_repo = https://github.com/a13x/aberth
pkg_aberth_commit = master

PACKAGES += active
pkg_active_name = active
pkg_active_description = Active development for Erlang: rebuild and reload source/binary files while the VM is running
pkg_active_homepage = https://github.com/proger/active
pkg_active_fetch = git
pkg_active_repo = https://github.com/proger/active
pkg_active_commit = master

PACKAGES += actordb_core
pkg_actordb_core_name = actordb_core
pkg_actordb_core_description = ActorDB main source
pkg_actordb_core_homepage = http://www.actordb.com/
pkg_actordb_core_fetch = git
pkg_actordb_core_repo = https://github.com/biokoda/actordb_core
pkg_actordb_core_commit = master

PACKAGES += actordb_thrift
pkg_actordb_thrift_name = actordb_thrift
pkg_actordb_thrift_description = Thrift API for ActorDB
pkg_actordb_thrift_homepage = http://www.actordb.com/
pkg_actordb_thrift_fetch = git
pkg_actordb_thrift_repo = https://github.com/biokoda/actordb_thrift
pkg_actordb_thrift_commit = master

PACKAGES += aleppo
pkg_aleppo_name = aleppo
pkg_aleppo_description = Alternative Erlang Pre-Processor
pkg_aleppo_homepage = https://github.com/ErlyORM/aleppo
pkg_aleppo_fetch = git
pkg_aleppo_repo = https://github.com/ErlyORM/aleppo
pkg_aleppo_commit = master

PACKAGES += alog
pkg_alog_name = alog
pkg_alog_description = Simply the best logging framework for Erlang
pkg_alog_homepage = https://github.com/siberian-fast-food/alogger
pkg_alog_fetch = git
pkg_alog_repo = https://github.com/siberian-fast-food/alogger
pkg_alog_commit = master

PACKAGES += amqp_client
pkg_amqp_client_name = amqp_client
pkg_amqp_client_description = RabbitMQ Erlang AMQP client
pkg_amqp_client_homepage = https://www.rabbitmq.com/erlang-client-user-guide.html
pkg_amqp_client_fetch = git
pkg_amqp_client_repo = https://github.com/rabbitmq/rabbitmq-erlang-client.git
pkg_amqp_client_commit = master

PACKAGES += annotations
pkg_annotations_name = annotations
pkg_annotations_description = Simple code instrumentation utilities
pkg_annotations_homepage = https://github.com/hyperthunk/annotations
pkg_annotations_fetch = git
pkg_annotations_repo = https://github.com/hyperthunk/annotations
pkg_annotations_commit = master

PACKAGES += antidote
pkg_antidote_name = antidote
pkg_antidote_description = Large-scale computation without synchronisation
pkg_antidote_homepage = https://syncfree.lip6.fr/
pkg_antidote_fetch = git
pkg_antidote_repo = https://github.com/SyncFree/antidote
pkg_antidote_commit = master

PACKAGES += apns
pkg_apns_name = apns
pkg_apns_description = Apple Push Notification Server for Erlang
pkg_apns_homepage = http://inaka.github.com/apns4erl
pkg_apns_fetch = git
pkg_apns_repo = https://github.com/inaka/apns4erl
pkg_apns_commit = 1.0.4

PACKAGES += azdht
pkg_azdht_name = azdht
pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
pkg_azdht_homepage = https://github.com/arcusfelis/azdht
pkg_azdht_fetch = git
pkg_azdht_repo = https://github.com/arcusfelis/azdht
pkg_azdht_commit = master

PACKAGES += backoff
pkg_backoff_name = backoff
pkg_backoff_description = Simple exponential backoffs in Erlang
pkg_backoff_homepage = https://github.com/ferd/backoff
pkg_backoff_fetch = git
pkg_backoff_repo = https://github.com/ferd/backoff
pkg_backoff_commit = master

PACKAGES += barrel_tcp
pkg_barrel_tcp_name = barrel_tcp
pkg_barrel_tcp_description = barrel is a generic TCP acceptor pool with low latency in Erlang.
pkg_barrel_tcp_homepage = https://github.com/benoitc-attic/barrel_tcp
pkg_barrel_tcp_fetch = git
pkg_barrel_tcp_repo = https://github.com/benoitc-attic/barrel_tcp
pkg_barrel_tcp_commit = master

PACKAGES += basho_bench
pkg_basho_bench_name = basho_bench
pkg_basho_bench_description = A load-generation and testing tool for basically whatever you can write a returning Erlang function for.
pkg_basho_bench_homepage = https://github.com/basho/basho_bench
pkg_basho_bench_fetch = git
pkg_basho_bench_repo = https://github.com/basho/basho_bench
pkg_basho_bench_commit = master

PACKAGES += bcrypt
pkg_bcrypt_name = bcrypt
pkg_bcrypt_description = Bcrypt Erlang / C library
pkg_bcrypt_homepage = https://github.com/riverrun/branglecrypt
pkg_bcrypt_fetch = git
pkg_bcrypt_repo = https://github.com/riverrun/branglecrypt
pkg_bcrypt_commit = master

PACKAGES += beam
pkg_beam_name = beam
pkg_beam_description = BEAM emulator written in Erlang
pkg_beam_homepage = https://github.com/tonyrog/beam
pkg_beam_fetch = git
pkg_beam_repo = https://github.com/tonyrog/beam
pkg_beam_commit = master

PACKAGES += beanstalk
pkg_beanstalk_name = beanstalk
pkg_beanstalk_description = An Erlang client for beanstalkd
pkg_beanstalk_homepage = https://github.com/tim/erlang-beanstalk
pkg_beanstalk_fetch = git
pkg_beanstalk_repo = https://github.com/tim/erlang-beanstalk
pkg_beanstalk_commit = master

PACKAGES += bear
pkg_bear_name = bear
pkg_bear_description = a set of statistics functions for erlang
pkg_bear_homepage = https://github.com/boundary/bear
pkg_bear_fetch = git
pkg_bear_repo = https://github.com/boundary/bear
pkg_bear_commit = master

PACKAGES += bertconf
pkg_bertconf_name = bertconf
pkg_bertconf_description = Make ETS tables out of statc BERT files that are auto-reloaded
pkg_bertconf_homepage = https://github.com/ferd/bertconf
pkg_bertconf_fetch = git
pkg_bertconf_repo = https://github.com/ferd/bertconf
pkg_bertconf_commit = master

PACKAGES += bifrost
pkg_bifrost_name = bifrost
pkg_bifrost_description = Erlang FTP Server Framework
pkg_bifrost_homepage = https://github.com/thorstadt/bifrost
pkg_bifrost_fetch = git
pkg_bifrost_repo = https://github.com/thorstadt/bifrost
pkg_bifrost_commit = master

PACKAGES += binpp
pkg_binpp_name = binpp
pkg_binpp_description = Erlang Binary Pretty Printer
pkg_binpp_homepage = https://github.com/jtendo/binpp
pkg_binpp_fetch = git
pkg_binpp_repo = https://github.com/jtendo/binpp
pkg_binpp_commit = master

PACKAGES += bisect
pkg_bisect_name = bisect
pkg_bisect_description = Ordered fixed-size binary dictionary in Erlang
pkg_bisect_homepage = https://github.com/knutin/bisect
pkg_bisect_fetch = git
pkg_bisect_repo = https://github.com/knutin/bisect
pkg_bisect_commit = master

PACKAGES += bitcask
pkg_bitcask_name = bitcask
pkg_bitcask_description = because you need another a key/value storage engine
pkg_bitcask_homepage = https://github.com/basho/bitcask
pkg_bitcask_fetch = git
pkg_bitcask_repo = https://github.com/basho/bitcask
pkg_bitcask_commit = master

PACKAGES += bitstore
pkg_bitstore_name = bitstore
pkg_bitstore_description = A document based ontology development environment
pkg_bitstore_homepage = https://github.com/bdionne/bitstore
pkg_bitstore_fetch = git
pkg_bitstore_repo = https://github.com/bdionne/bitstore
pkg_bitstore_commit = master

PACKAGES += bootstrap
pkg_bootstrap_name = bootstrap
pkg_bootstrap_description = A simple, yet powerful Erlang cluster bootstrapping application.
pkg_bootstrap_homepage = https://github.com/schlagert/bootstrap
pkg_bootstrap_fetch = git
pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
pkg_bootstrap_commit = master

PACKAGES += boss_db
pkg_boss_db_name = boss_db
pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
pkg_boss_db_homepage = https://github.com/ErlyORM/boss_db
pkg_boss_db_fetch = git
pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
pkg_boss_db_commit = master

PACKAGES += boss
pkg_boss_name = boss
pkg_boss_description = Erlang web MVC, now featuring Comet
pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
pkg_boss_fetch = git
pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
pkg_boss_commit = master

PACKAGES += bson
pkg_bson_name = bson
pkg_bson_description = BSON documents in Erlang, see bsonspec.org
pkg_bson_homepage = https://github.com/comtihon/bson-erlang
pkg_bson_fetch = git
pkg_bson_repo = https://github.com/comtihon/bson-erlang
pkg_bson_commit = master

PACKAGES += bullet
pkg_bullet_name = bullet
pkg_bullet_description = Simple, reliable, efficient streaming for Cowboy.
pkg_bullet_homepage = http://ninenines.eu
pkg_bullet_fetch = git
pkg_bullet_repo = https://github.com/extend/bullet
pkg_bullet_commit = master

PACKAGES += cache
pkg_cache_name = cache
pkg_cache_description = Erlang in-memory cache
pkg_cache_homepage = https://github.com/fogfish/cache
pkg_cache_fetch = git
pkg_cache_repo = https://github.com/fogfish/cache
pkg_cache_commit = master

PACKAGES += cake
pkg_cake_name = cake
pkg_cake_description = Really simple terminal colorization
pkg_cake_homepage = https://github.com/darach/cake-erl
pkg_cake_fetch = git
pkg_cake_repo = https://github.com/darach/cake-erl
pkg_cake_commit = v0.1.2

PACKAGES += carotene
pkg_carotene_name = carotene
pkg_carotene_description = Real-time server
pkg_carotene_homepage = https://github.com/carotene/carotene
pkg_carotene_fetch = git
pkg_carotene_repo = https://github.com/carotene/carotene
pkg_carotene_commit = master

PACKAGES += cberl
pkg_cberl_name = cberl
pkg_cberl_description = NIF based Erlang bindings for Couchbase
pkg_cberl_homepage = https://github.com/chitika/cberl
pkg_cberl_fetch = git
pkg_cberl_repo = https://github.com/chitika/cberl
pkg_cberl_commit = master

PACKAGES += cecho
pkg_cecho_name = cecho
pkg_cecho_description = An ncurses library for Erlang
pkg_cecho_homepage = https://github.com/mazenharake/cecho
pkg_cecho_fetch = git
pkg_cecho_repo = https://github.com/mazenharake/cecho
pkg_cecho_commit = master

PACKAGES += cferl
pkg_cferl_name = cferl
pkg_cferl_description = Rackspace / Open Stack Cloud Files Erlang Client
pkg_cferl_homepage = https://github.com/ddossot/cferl
pkg_cferl_fetch = git
pkg_cferl_repo = https://github.com/ddossot/cferl
pkg_cferl_commit = master

PACKAGES += chaos_monkey
pkg_chaos_monkey_name = chaos_monkey
pkg_chaos_monkey_description = This is The CHAOS MONKEY.  It will kill your processes.
pkg_chaos_monkey_homepage = https://github.com/dLuna/chaos_monkey
pkg_chaos_monkey_fetch = git
pkg_chaos_monkey_repo = https://github.com/dLuna/chaos_monkey
pkg_chaos_monkey_commit = master

PACKAGES += check_node
pkg_check_node_name = check_node
pkg_check_node_description = Nagios Scripts for monitoring Riak
pkg_check_node_homepage = https://github.com/basho-labs/riak_nagios
pkg_check_node_fetch = git
pkg_check_node_repo = https://github.com/basho-labs/riak_nagios
pkg_check_node_commit = master

PACKAGES += chronos
pkg_chronos_name = chronos
pkg_chronos_description = Timer module for Erlang that makes it easy to abstact time out of the tests.
pkg_chronos_homepage = https://github.com/lehoff/chronos
pkg_chronos_fetch = git
pkg_chronos_repo = https://github.com/lehoff/chronos
pkg_chronos_commit = master

PACKAGES += classifier
pkg_classifier_name = classifier
pkg_classifier_description = An Erlang Bayesian Filter and Text Classifier
pkg_classifier_homepage = https://github.com/inaka/classifier
pkg_classifier_fetch = git
pkg_classifier_repo = https://github.com/inaka/classifier
pkg_classifier_commit = master

PACKAGES += clique
pkg_clique_name = clique
pkg_clique_description = CLI Framework for Erlang
pkg_clique_homepage = https://github.com/basho/clique
pkg_clique_fetch = git
pkg_clique_repo = https://github.com/basho/clique
pkg_clique_commit = develop

PACKAGES += cl
pkg_cl_name = cl
pkg_cl_description = OpenCL binding for Erlang
pkg_cl_homepage = https://github.com/tonyrog/cl
pkg_cl_fetch = git
pkg_cl_repo = https://github.com/tonyrog/cl
pkg_cl_commit = master

PACKAGES += cloudi_core
pkg_cloudi_core_name = cloudi_core
pkg_cloudi_core_description = CloudI internal service runtime
pkg_cloudi_core_homepage = http://cloudi.org/
pkg_cloudi_core_fetch = git
pkg_cloudi_core_repo = https://github.com/CloudI/cloudi_core
pkg_cloudi_core_commit = master

PACKAGES += cloudi_service_api_requests
pkg_cloudi_service_api_requests_name = cloudi_service_api_requests
pkg_cloudi_service_api_requests_description = CloudI Service API requests (JSON-RPC/Erlang-term support)
pkg_cloudi_service_api_requests_homepage = http://cloudi.org/
pkg_cloudi_service_api_requests_fetch = git
pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
pkg_cloudi_service_api_requests_commit = master

PACKAGES += cloudi_service_db_cassandra_cql
pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
pkg_cloudi_service_db_cassandra_cql_homepage = http://cloudi.org/
pkg_cloudi_service_db_cassandra_cql_fetch = git
pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
pkg_cloudi_service_db_cassandra_cql_commit = master

PACKAGES += cloudi_service_db_cassandra
pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
pkg_cloudi_service_db_cassandra_fetch = git
pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
pkg_cloudi_service_db_cassandra_commit = master

PACKAGES += cloudi_service_db_couchdb
pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
pkg_cloudi_service_db_couchdb_homepage = http://cloudi.org/
pkg_cloudi_service_db_couchdb_fetch = git
pkg_cloudi_service_db_couchdb_repo = https://github.com/CloudI/cloudi_service_db_couchdb
pkg_cloudi_service_db_couchdb_commit = master

PACKAGES += cloudi_service_db_elasticsearch
pkg_cloudi_service_db_elasticsearch_name = cloudi_service_db_elasticsearch
pkg_cloudi_service_db_elasticsearch_description = elasticsearch CloudI Service
pkg_cloudi_service_db_elasticsearch_homepage = http://cloudi.org/
pkg_cloudi_service_db_elasticsearch_fetch = git
pkg_cloudi_service_db_elasticsearch_repo = https://github.com/CloudI/cloudi_service_db_elasticsearch
pkg_cloudi_service_db_elasticsearch_commit = master

PACKAGES += cloudi_service_db_memcached
pkg_cloudi_service_db_memcached_name = cloudi_service_db_memcached
pkg_cloudi_service_db_memcached_description = memcached CloudI Service
pkg_cloudi_service_db_memcached_homepage = http://cloudi.org/
pkg_cloudi_service_db_memcached_fetch = git
pkg_cloudi_service_db_memcached_repo = https://github.com/CloudI/cloudi_service_db_memcached
pkg_cloudi_service_db_memcached_commit = master

PACKAGES += cloudi_service_db
pkg_cloudi_service_db_name = cloudi_service_db
pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
pkg_cloudi_service_db_homepage = http://cloudi.org/
pkg_cloudi_service_db_fetch = git
pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
pkg_cloudi_service_db_commit = master

PACKAGES += cloudi_service_db_mysql
pkg_cloudi_service_db_mysql_name = cloudi_service_db_mysql
pkg_cloudi_service_db_mysql_description = MySQL CloudI Service
pkg_cloudi_service_db_mysql_homepage = http://cloudi.org/
pkg_cloudi_service_db_mysql_fetch = git
pkg_cloudi_service_db_mysql_repo = https://github.com/CloudI/cloudi_service_db_mysql
pkg_cloudi_service_db_mysql_commit = master

PACKAGES += cloudi_service_db_pgsql
pkg_cloudi_service_db_pgsql_name = cloudi_service_db_pgsql
pkg_cloudi_service_db_pgsql_description = PostgreSQL CloudI Service
pkg_cloudi_service_db_pgsql_homepage = http://cloudi.org/
pkg_cloudi_service_db_pgsql_fetch = git
pkg_cloudi_service_db_pgsql_repo = https://github.com/CloudI/cloudi_service_db_pgsql
pkg_cloudi_service_db_pgsql_commit = master

PACKAGES += cloudi_service_db_riak
pkg_cloudi_service_db_riak_name = cloudi_service_db_riak
pkg_cloudi_service_db_riak_description = Riak CloudI Service
pkg_cloudi_service_db_riak_homepage = http://cloudi.org/
pkg_cloudi_service_db_riak_fetch = git
pkg_cloudi_service_db_riak_repo = https://github.com/CloudI/cloudi_service_db_riak
pkg_cloudi_service_db_riak_commit = master

PACKAGES += cloudi_service_db_tokyotyrant
pkg_cloudi_service_db_tokyotyrant_name = cloudi_service_db_tokyotyrant
pkg_cloudi_service_db_tokyotyrant_description = Tokyo Tyrant CloudI Service
pkg_cloudi_service_db_tokyotyrant_homepage = http://cloudi.org/
pkg_cloudi_service_db_tokyotyrant_fetch = git
pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
pkg_cloudi_service_db_tokyotyrant_commit = master

PACKAGES += cloudi_service_filesystem
pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
pkg_cloudi_service_filesystem_homepage = http://cloudi.org/
pkg_cloudi_service_filesystem_fetch = git
pkg_cloudi_service_filesystem_repo = https://github.com/CloudI/cloudi_service_filesystem
pkg_cloudi_service_filesystem_commit = master

PACKAGES += cloudi_service_http_client
pkg_cloudi_service_http_client_name = cloudi_service_http_client
pkg_cloudi_service_http_client_description = HTTP client CloudI Service
pkg_cloudi_service_http_client_homepage = http://cloudi.org/
pkg_cloudi_service_http_client_fetch = git
pkg_cloudi_service_http_client_repo = https://github.com/CloudI/cloudi_service_http_client
pkg_cloudi_service_http_client_commit = master

PACKAGES += cloudi_service_http_cowboy
pkg_cloudi_service_http_cowboy_name = cloudi_service_http_cowboy
pkg_cloudi_service_http_cowboy_description = cowboy HTTP/HTTPS CloudI Service
pkg_cloudi_service_http_cowboy_homepage = http://cloudi.org/
pkg_cloudi_service_http_cowboy_fetch = git
pkg_cloudi_service_http_cowboy_repo = https://github.com/CloudI/cloudi_service_http_cowboy
pkg_cloudi_service_http_cowboy_commit = master

PACKAGES += cloudi_service_http_elli
pkg_cloudi_service_http_elli_name = cloudi_service_http_elli
pkg_cloudi_service_http_elli_description = elli HTTP CloudI Service
pkg_cloudi_service_http_elli_homepage = http://cloudi.org/
pkg_cloudi_service_http_elli_fetch = git
pkg_cloudi_service_http_elli_repo = https://github.com/CloudI/cloudi_service_http_elli
pkg_cloudi_service_http_elli_commit = master

PACKAGES += cloudi_service_map_reduce
pkg_cloudi_service_map_reduce_name = cloudi_service_map_reduce
pkg_cloudi_service_map_reduce_description = Map/Reduce CloudI Service
pkg_cloudi_service_map_reduce_homepage = http://cloudi.org/
pkg_cloudi_service_map_reduce_fetch = git
pkg_cloudi_service_map_reduce_repo = https://github.com/CloudI/cloudi_service_map_reduce
pkg_cloudi_service_map_reduce_commit = master

PACKAGES += cloudi_service_oauth1
pkg_cloudi_service_oauth1_name = cloudi_service_oauth1
pkg_cloudi_service_oauth1_description = OAuth v1.0 CloudI Service
pkg_cloudi_service_oauth1_homepage = http://cloudi.org/
pkg_cloudi_service_oauth1_fetch = git
pkg_cloudi_service_oauth1_repo = https://github.com/CloudI/cloudi_service_oauth1
pkg_cloudi_service_oauth1_commit = master

PACKAGES += cloudi_service_queue
pkg_cloudi_service_queue_name = cloudi_service_queue
pkg_cloudi_service_queue_description = Persistent Queue Service
pkg_cloudi_service_queue_homepage = http://cloudi.org/
pkg_cloudi_service_queue_fetch = git
pkg_cloudi_service_queue_repo = https://github.com/CloudI/cloudi_service_queue
pkg_cloudi_service_queue_commit = master

PACKAGES += cloudi_service_quorum
pkg_cloudi_service_quorum_name = cloudi_service_quorum
pkg_cloudi_service_quorum_description = CloudI Quorum Service
pkg_cloudi_service_quorum_homepage = http://cloudi.org/
pkg_cloudi_service_quorum_fetch = git
pkg_cloudi_service_quorum_repo = https://github.com/CloudI/cloudi_service_quorum
pkg_cloudi_service_quorum_commit = master

PACKAGES += cloudi_service_router
pkg_cloudi_service_router_name = cloudi_service_router
pkg_cloudi_service_router_description = CloudI Router Service
pkg_cloudi_service_router_homepage = http://cloudi.org/
pkg_cloudi_service_router_fetch = git
pkg_cloudi_service_router_repo = https://github.com/CloudI/cloudi_service_router
pkg_cloudi_service_router_commit = master

PACKAGES += cloudi_service_tcp
pkg_cloudi_service_tcp_name = cloudi_service_tcp
pkg_cloudi_service_tcp_description = TCP CloudI Service
pkg_cloudi_service_tcp_homepage = http://cloudi.org/
pkg_cloudi_service_tcp_fetch = git
pkg_cloudi_service_tcp_repo = https://github.com/CloudI/cloudi_service_tcp
pkg_cloudi_service_tcp_commit = master

PACKAGES += cloudi_service_timers
pkg_cloudi_service_timers_name = cloudi_service_timers
pkg_cloudi_service_timers_description = Timers CloudI Service
pkg_cloudi_service_timers_homepage = http://cloudi.org/
pkg_cloudi_service_timers_fetch = git
pkg_cloudi_service_timers_repo = https://github.com/CloudI/cloudi_service_timers
pkg_cloudi_service_timers_commit = master

PACKAGES += cloudi_service_udp
pkg_cloudi_service_udp_name = cloudi_service_udp
pkg_cloudi_service_udp_description = UDP CloudI Service
pkg_cloudi_service_udp_homepage = http://cloudi.org/
pkg_cloudi_service_udp_fetch = git
pkg_cloudi_service_udp_repo = https://github.com/CloudI/cloudi_service_udp
pkg_cloudi_service_udp_commit = master

PACKAGES += cloudi_service_validate
pkg_cloudi_service_validate_name = cloudi_service_validate
pkg_cloudi_service_validate_description = CloudI Validate Service
pkg_cloudi_service_validate_homepage = http://cloudi.org/
pkg_cloudi_service_validate_fetch = git
pkg_cloudi_service_validate_repo = https://github.com/CloudI/cloudi_service_validate
pkg_cloudi_service_validate_commit = master

PACKAGES += cloudi_service_zeromq
pkg_cloudi_service_zeromq_name = cloudi_service_zeromq
pkg_cloudi_service_zeromq_description = ZeroMQ CloudI Service
pkg_cloudi_service_zeromq_homepage = http://cloudi.org/
pkg_cloudi_service_zeromq_fetch = git
pkg_cloudi_service_zeromq_repo = https://github.com/CloudI/cloudi_service_zeromq
pkg_cloudi_service_zeromq_commit = master

PACKAGES += cluster_info
pkg_cluster_info_name = cluster_info
pkg_cluster_info_description = Fork of Hibari's nifty cluster_info OTP app
pkg_cluster_info_homepage = https://github.com/basho/cluster_info
pkg_cluster_info_fetch = git
pkg_cluster_info_repo = https://github.com/basho/cluster_info
pkg_cluster_info_commit = master

PACKAGES += color
pkg_color_name = color
pkg_color_description = ANSI colors for your Erlang
pkg_color_homepage = https://github.com/julianduque/erlang-color
pkg_color_fetch = git
pkg_color_repo = https://github.com/julianduque/erlang-color
pkg_color_commit = master

PACKAGES += confetti
pkg_confetti_name = confetti
pkg_confetti_description = Erlang configuration provider / application:get_env/2 on steroids
pkg_confetti_homepage = https://github.com/jtendo/confetti
pkg_confetti_fetch = git
pkg_confetti_repo = https://github.com/jtendo/confetti
pkg_confetti_commit = master

PACKAGES += couchbeam
pkg_couchbeam_name = couchbeam
pkg_couchbeam_description = Apache CouchDB client in Erlang
pkg_couchbeam_homepage = https://github.com/benoitc/couchbeam
pkg_couchbeam_fetch = git
pkg_couchbeam_repo = https://github.com/benoitc/couchbeam
pkg_couchbeam_commit = master

PACKAGES += couch
pkg_couch_name = couch
pkg_couch_description = A embeddable document oriented database compatible with Apache CouchDB
pkg_couch_homepage = https://github.com/benoitc/opencouch
pkg_couch_fetch = git
pkg_couch_repo = https://github.com/benoitc/opencouch
pkg_couch_commit = master

PACKAGES += covertool
pkg_covertool_name = covertool
pkg_covertool_description = Tool to convert Erlang cover data files into Cobertura XML reports
pkg_covertool_homepage = https://github.com/idubrov/covertool
pkg_covertool_fetch = git
pkg_covertool_repo = https://github.com/idubrov/covertool
pkg_covertool_commit = master

PACKAGES += cowboy
pkg_cowboy_name = cowboy
pkg_cowboy_description = Small, fast and modular HTTP server.
pkg_cowboy_homepage = http://ninenines.eu
pkg_cowboy_fetch = git
pkg_cowboy_repo = https://github.com/ninenines/cowboy
pkg_cowboy_commit = 1.0.1

PACKAGES += cowdb
pkg_cowdb_name = cowdb
pkg_cowdb_description = Pure Key/Value database library for Erlang Applications
pkg_cowdb_homepage = https://github.com/refuge/cowdb
pkg_cowdb_fetch = git
pkg_cowdb_repo = https://github.com/refuge/cowdb
pkg_cowdb_commit = master

PACKAGES += cowlib
pkg_cowlib_name = cowlib
pkg_cowlib_description = Support library for manipulating Web protocols.
pkg_cowlib_homepage = http://ninenines.eu
pkg_cowlib_fetch = git
pkg_cowlib_repo = https://github.com/ninenines/cowlib
pkg_cowlib_commit = 1.0.1

PACKAGES += cpg
pkg_cpg_name = cpg
pkg_cpg_description = CloudI Process Groups
pkg_cpg_homepage = https://github.com/okeuday/cpg
pkg_cpg_fetch = git
pkg_cpg_repo = https://github.com/okeuday/cpg
pkg_cpg_commit = master

PACKAGES += cqerl
pkg_cqerl_name = cqerl
pkg_cqerl_description = Native Erlang CQL client for Cassandra
pkg_cqerl_homepage = https://matehat.github.io/cqerl/
pkg_cqerl_fetch = git
pkg_cqerl_repo = https://github.com/matehat/cqerl
pkg_cqerl_commit = master

PACKAGES += cr
pkg_cr_name = cr
pkg_cr_description = Chain Replication
pkg_cr_homepage = https://synrc.com/apps/cr/doc/cr.htm
pkg_cr_fetch = git
pkg_cr_repo = https://github.com/spawnproc/cr
pkg_cr_commit = master

PACKAGES += cuttlefish
pkg_cuttlefish_name = cuttlefish
pkg_cuttlefish_description = never lose your childlike sense of wonder baby cuttlefish, promise me?
pkg_cuttlefish_homepage = https://github.com/basho/cuttlefish
pkg_cuttlefish_fetch = git
pkg_cuttlefish_repo = https://github.com/basho/cuttlefish
pkg_cuttlefish_commit = master

PACKAGES += damocles
pkg_damocles_name = damocles
pkg_damocles_description = Erlang library for generating adversarial network conditions for QAing distributed applications/systems on a single Linux box.
pkg_damocles_homepage = https://github.com/lostcolony/damocles
pkg_damocles_fetch = git
pkg_damocles_repo = https://github.com/lostcolony/damocles
pkg_damocles_commit = master

PACKAGES += debbie
pkg_debbie_name = debbie
pkg_debbie_description = .DEB Built In Erlang
pkg_debbie_homepage = https://github.com/crownedgrouse/debbie
pkg_debbie_fetch = git
pkg_debbie_repo = https://github.com/crownedgrouse/debbie
pkg_debbie_commit = master

PACKAGES += decimal
pkg_decimal_name = decimal
pkg_decimal_description = An Erlang decimal arithmetic library
pkg_decimal_homepage = https://github.com/tim/erlang-decimal
pkg_decimal_fetch = git
pkg_decimal_repo = https://github.com/tim/erlang-decimal
pkg_decimal_commit = master

PACKAGES += detergent
pkg_detergent_name = detergent
pkg_detergent_description = An emulsifying Erlang SOAP library
pkg_detergent_homepage = https://github.com/devinus/detergent
pkg_detergent_fetch = git
pkg_detergent_repo = https://github.com/devinus/detergent
pkg_detergent_commit = master

PACKAGES += detest
pkg_detest_name = detest
pkg_detest_description = Tool for running tests on a cluster of erlang nodes
pkg_detest_homepage = https://github.com/biokoda/detest
pkg_detest_fetch = git
pkg_detest_repo = https://github.com/biokoda/detest
pkg_detest_commit = master

PACKAGES += dh_date
pkg_dh_date_name = dh_date
pkg_dh_date_description = Date formatting / parsing library for erlang
pkg_dh_date_homepage = https://github.com/daleharvey/dh_date
pkg_dh_date_fetch = git
pkg_dh_date_repo = https://github.com/daleharvey/dh_date
pkg_dh_date_commit = master

PACKAGES += dhtcrawler
pkg_dhtcrawler_name = dhtcrawler
pkg_dhtcrawler_description = dhtcrawler is a DHT crawler written in erlang. It can join a DHT network and crawl many P2P torrents.
pkg_dhtcrawler_homepage = https://github.com/kevinlynx/dhtcrawler
pkg_dhtcrawler_fetch = git
pkg_dhtcrawler_repo = https://github.com/kevinlynx/dhtcrawler
pkg_dhtcrawler_commit = master

PACKAGES += dirbusterl
pkg_dirbusterl_name = dirbusterl
pkg_dirbusterl_description = DirBuster successor in Erlang
pkg_dirbusterl_homepage = https://github.com/silentsignal/DirBustErl
pkg_dirbusterl_fetch = git
pkg_dirbusterl_repo = https://github.com/silentsignal/DirBustErl
pkg_dirbusterl_commit = master

PACKAGES += dispcount
pkg_dispcount_name = dispcount
pkg_dispcount_description = Erlang task dispatcher based on ETS counters.
pkg_dispcount_homepage = https://github.com/ferd/dispcount
pkg_dispcount_fetch = git
pkg_dispcount_repo = https://github.com/ferd/dispcount
pkg_dispcount_commit = master

PACKAGES += dlhttpc
pkg_dlhttpc_name = dlhttpc
pkg_dlhttpc_description = dispcount-based lhttpc fork for massive amounts of requests to limited endpoints
pkg_dlhttpc_homepage = https://github.com/ferd/dlhttpc
pkg_dlhttpc_fetch = git
pkg_dlhttpc_repo = https://github.com/ferd/dlhttpc
pkg_dlhttpc_commit = master

PACKAGES += dns
pkg_dns_name = dns
pkg_dns_description = Erlang DNS library
pkg_dns_homepage = https://github.com/aetrion/dns_erlang
pkg_dns_fetch = git
pkg_dns_repo = https://github.com/aetrion/dns_erlang
pkg_dns_commit = master

PACKAGES += dnssd
pkg_dnssd_name = dnssd
pkg_dnssd_description = Erlang interface to Apple's Bonjour D    NS Service Discovery implementation
pkg_dnssd_homepage = https://github.com/benoitc/dnssd_erlang
pkg_dnssd_fetch = git
pkg_dnssd_repo = https://github.com/benoitc/dnssd_erlang
pkg_dnssd_commit = master

PACKAGES += dtl
pkg_dtl_name = dtl
pkg_dtl_description = Django Template Language: A full-featured port of the Django template engine to Erlang.
pkg_dtl_homepage = https://github.com/oinksoft/dtl
pkg_dtl_fetch = git
pkg_dtl_repo = https://github.com/oinksoft/dtl
pkg_dtl_commit = master

PACKAGES += dynamic_compile
pkg_dynamic_compile_name = dynamic_compile
pkg_dynamic_compile_description = compile and load erlang modules from string input
pkg_dynamic_compile_homepage = https://github.com/jkvor/dynamic_compile
pkg_dynamic_compile_fetch = git
pkg_dynamic_compile_repo = https://github.com/jkvor/dynamic_compile
pkg_dynamic_compile_commit = master

PACKAGES += e2
pkg_e2_name = e2
pkg_e2_description = Library to simply writing correct OTP applications.
pkg_e2_homepage = http://e2project.org
pkg_e2_fetch = git
pkg_e2_repo = https://github.com/gar1t/e2
pkg_e2_commit = master

PACKAGES += eamf
pkg_eamf_name = eamf
pkg_eamf_description = eAMF provides Action Message Format (AMF) support for Erlang
pkg_eamf_homepage = https://github.com/mrinalwadhwa/eamf
pkg_eamf_fetch = git
pkg_eamf_repo = https://github.com/mrinalwadhwa/eamf
pkg_eamf_commit = master

PACKAGES += eavro
pkg_eavro_name = eavro
pkg_eavro_description = Apache Avro encoder/decoder
pkg_eavro_homepage = https://github.com/SIfoxDevTeam/eavro
pkg_eavro_fetch = git
pkg_eavro_repo = https://github.com/SIfoxDevTeam/eavro
pkg_eavro_commit = master

PACKAGES += ecapnp
pkg_ecapnp_name = ecapnp
pkg_ecapnp_description = Cap'n Proto library for Erlang
pkg_ecapnp_homepage = https://github.com/kaos/ecapnp
pkg_ecapnp_fetch = git
pkg_ecapnp_repo = https://github.com/kaos/ecapnp
pkg_ecapnp_commit = master

PACKAGES += econfig
pkg_econfig_name = econfig
pkg_econfig_description = simple Erlang config handler using INI files
pkg_econfig_homepage = https://github.com/benoitc/econfig
pkg_econfig_fetch = git
pkg_econfig_repo = https://github.com/benoitc/econfig
pkg_econfig_commit = master

PACKAGES += edate
pkg_edate_name = edate
pkg_edate_description = date manipulation library for erlang
pkg_edate_homepage = https://github.com/dweldon/edate
pkg_edate_fetch = git
pkg_edate_repo = https://github.com/dweldon/edate
pkg_edate_commit = master

PACKAGES += edgar
pkg_edgar_name = edgar
pkg_edgar_description = Erlang Does GNU AR
pkg_edgar_homepage = https://github.com/crownedgrouse/edgar
pkg_edgar_fetch = git
pkg_edgar_repo = https://github.com/crownedgrouse/edgar
pkg_edgar_commit = master

PACKAGES += edis
pkg_edis_name = edis
pkg_edis_description = An Erlang implementation of Redis KV Store
pkg_edis_homepage = http://inaka.github.com/edis/
pkg_edis_fetch = git
pkg_edis_repo = https://github.com/inaka/edis
pkg_edis_commit = master

PACKAGES += edns
pkg_edns_name = edns
pkg_edns_description = Erlang/OTP DNS server
pkg_edns_homepage = https://github.com/hcvst/erlang-dns
pkg_edns_fetch = git
pkg_edns_repo = https://github.com/hcvst/erlang-dns
pkg_edns_commit = master

PACKAGES += edown
pkg_edown_name = edown
pkg_edown_description = EDoc extension for generating Github-flavored Markdown
pkg_edown_homepage = https://github.com/uwiger/edown
pkg_edown_fetch = git
pkg_edown_repo = https://github.com/uwiger/edown
pkg_edown_commit = master

PACKAGES += eep_app
pkg_eep_app_name = eep_app
pkg_eep_app_description = Embedded Event Processing
pkg_eep_app_homepage = https://github.com/darach/eep-erl
pkg_eep_app_fetch = git
pkg_eep_app_repo = https://github.com/darach/eep-erl
pkg_eep_app_commit = master

PACKAGES += eep
pkg_eep_name = eep
pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
pkg_eep_homepage = https://github.com/virtan/eep
pkg_eep_fetch = git
pkg_eep_repo = https://github.com/virtan/eep
pkg_eep_commit = master

PACKAGES += efene
pkg_efene_name = efene
pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
pkg_efene_homepage = https://github.com/efene/efene
pkg_efene_fetch = git
pkg_efene_repo = https://github.com/efene/efene
pkg_efene_commit = master

PACKAGES += eganglia
pkg_eganglia_name = eganglia
pkg_eganglia_description = Erlang library to interact with Ganglia
pkg_eganglia_homepage = https://github.com/inaka/eganglia
pkg_eganglia_fetch = git
pkg_eganglia_repo = https://github.com/inaka/eganglia
pkg_eganglia_commit = v0.9.1

PACKAGES += egeoip
pkg_egeoip_name = egeoip
pkg_egeoip_description = Erlang IP Geolocation module, currently supporting the MaxMind GeoLite City Database.
pkg_egeoip_homepage = https://github.com/mochi/egeoip
pkg_egeoip_fetch = git
pkg_egeoip_repo = https://github.com/mochi/egeoip
pkg_egeoip_commit = master

PACKAGES += ehsa
pkg_ehsa_name = ehsa
pkg_ehsa_description = Erlang HTTP server basic and digest authentication modules
pkg_ehsa_homepage = https://bitbucket.org/a12n/ehsa
pkg_ehsa_fetch = hg
pkg_ehsa_repo = https://bitbucket.org/a12n/ehsa
pkg_ehsa_commit = 2.0.4

PACKAGES += ejabberd
pkg_ejabberd_name = ejabberd
pkg_ejabberd_description = Robust, ubiquitous and massively scalable Jabber / XMPP Instant Messaging platform
pkg_ejabberd_homepage = https://github.com/processone/ejabberd
pkg_ejabberd_fetch = git
pkg_ejabberd_repo = https://github.com/processone/ejabberd
pkg_ejabberd_commit = master

PACKAGES += ej
pkg_ej_name = ej
pkg_ej_description = Helper module for working with Erlang terms representing JSON
pkg_ej_homepage = https://github.com/seth/ej
pkg_ej_fetch = git
pkg_ej_repo = https://github.com/seth/ej
pkg_ej_commit = master

PACKAGES += ejwt
pkg_ejwt_name = ejwt
pkg_ejwt_description = erlang library for JSON Web Token
pkg_ejwt_homepage = https://github.com/artefactop/ejwt
pkg_ejwt_fetch = git
pkg_ejwt_repo = https://github.com/artefactop/ejwt
pkg_ejwt_commit = master

PACKAGES += ekaf
pkg_ekaf_name = ekaf
pkg_ekaf_description = A minimal, high-performance Kafka client in Erlang.
pkg_ekaf_homepage = https://github.com/helpshift/ekaf
pkg_ekaf_fetch = git
pkg_ekaf_repo = https://github.com/helpshift/ekaf
pkg_ekaf_commit = master

PACKAGES += elarm
pkg_elarm_name = elarm
pkg_elarm_description = Alarm Manager for Erlang.
pkg_elarm_homepage = https://github.com/esl/elarm
pkg_elarm_fetch = git
pkg_elarm_repo = https://github.com/esl/elarm
pkg_elarm_commit = master

PACKAGES += eleveldb
pkg_eleveldb_name = eleveldb
pkg_eleveldb_description = Erlang LevelDB API
pkg_eleveldb_homepage = https://github.com/basho/eleveldb
pkg_eleveldb_fetch = git
pkg_eleveldb_repo = https://github.com/basho/eleveldb
pkg_eleveldb_commit = master

PACKAGES += elli
pkg_elli_name = elli
pkg_elli_description = Simple, robust and performant Erlang web server
pkg_elli_homepage = https://github.com/knutin/elli
pkg_elli_fetch = git
pkg_elli_repo = https://github.com/knutin/elli
pkg_elli_commit = master

PACKAGES += elvis
pkg_elvis_name = elvis
pkg_elvis_description = Erlang Style Reviewer
pkg_elvis_homepage = https://github.com/inaka/elvis
pkg_elvis_fetch = git
pkg_elvis_repo = https://github.com/inaka/elvis
pkg_elvis_commit = 0.2.4

PACKAGES += emagick
pkg_emagick_name = emagick
pkg_emagick_description = Wrapper for Graphics/ImageMagick command line tool.
pkg_emagick_homepage = https://github.com/kivra/emagick
pkg_emagick_fetch = git
pkg_emagick_repo = https://github.com/kivra/emagick
pkg_emagick_commit = master

PACKAGES += emysql
pkg_emysql_name = emysql
pkg_emysql_description = Stable, pure Erlang MySQL driver.
pkg_emysql_homepage = https://github.com/Eonblast/Emysql
pkg_emysql_fetch = git
pkg_emysql_repo = https://github.com/Eonblast/Emysql
pkg_emysql_commit = master

PACKAGES += enm
pkg_enm_name = enm
pkg_enm_description = Erlang driver for nanomsg
pkg_enm_homepage = https://github.com/basho/enm
pkg_enm_fetch = git
pkg_enm_repo = https://github.com/basho/enm
pkg_enm_commit = master

PACKAGES += entop
pkg_entop_name = entop
pkg_entop_description = A top-like tool for monitoring an Erlang node
pkg_entop_homepage = https://github.com/mazenharake/entop
pkg_entop_fetch = git
pkg_entop_repo = https://github.com/mazenharake/entop
pkg_entop_commit = master

PACKAGES += epcap
pkg_epcap_name = epcap
pkg_epcap_description = Erlang packet capture interface using pcap
pkg_epcap_homepage = https://github.com/msantos/epcap
pkg_epcap_fetch = git
pkg_epcap_repo = https://github.com/msantos/epcap
pkg_epcap_commit = master

PACKAGES += eper
pkg_eper_name = eper
pkg_eper_description = Erlang performance and debugging tools.
pkg_eper_homepage = https://github.com/massemanet/eper
pkg_eper_fetch = git
pkg_eper_repo = https://github.com/massemanet/eper
pkg_eper_commit = master

PACKAGES += epgsql
pkg_epgsql_name = epgsql
pkg_epgsql_description = Erlang PostgreSQL client library.
pkg_epgsql_homepage = https://github.com/epgsql/epgsql
pkg_epgsql_fetch = git
pkg_epgsql_repo = https://github.com/epgsql/epgsql
pkg_epgsql_commit = master

PACKAGES += episcina
pkg_episcina_name = episcina
pkg_episcina_description = A simple non intrusive resource pool for connections
pkg_episcina_homepage = https://github.com/erlware/episcina
pkg_episcina_fetch = git
pkg_episcina_repo = https://github.com/erlware/episcina
pkg_episcina_commit = master

PACKAGES += eplot
pkg_eplot_name = eplot
pkg_eplot_description = A plot engine written in erlang.
pkg_eplot_homepage = https://github.com/psyeugenic/eplot
pkg_eplot_fetch = git
pkg_eplot_repo = https://github.com/psyeugenic/eplot
pkg_eplot_commit = master

PACKAGES += epocxy
pkg_epocxy_name = epocxy
pkg_epocxy_description = Erlang Patterns of Concurrency
pkg_epocxy_homepage = https://github.com/duomark/epocxy
pkg_epocxy_fetch = git
pkg_epocxy_repo = https://github.com/duomark/epocxy
pkg_epocxy_commit = master

PACKAGES += epubnub
pkg_epubnub_name = epubnub
pkg_epubnub_description = Erlang PubNub API
pkg_epubnub_homepage = https://github.com/tsloughter/epubnub
pkg_epubnub_fetch = git
pkg_epubnub_repo = https://github.com/tsloughter/epubnub
pkg_epubnub_commit = master

PACKAGES += eqm
pkg_eqm_name = eqm
pkg_eqm_description = Erlang pub sub with supply-demand channels
pkg_eqm_homepage = https://github.com/loucash/eqm
pkg_eqm_fetch = git
pkg_eqm_repo = https://github.com/loucash/eqm
pkg_eqm_commit = master

PACKAGES += eredis
pkg_eredis_name = eredis
pkg_eredis_description = Erlang Redis client
pkg_eredis_homepage = https://github.com/wooga/eredis
pkg_eredis_fetch = git
pkg_eredis_repo = https://github.com/wooga/eredis
pkg_eredis_commit = master

PACKAGES += eredis_pool
pkg_eredis_pool_name = eredis_pool
pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
pkg_eredis_pool_homepage = https://github.com/hiroeorz/eredis_pool
pkg_eredis_pool_fetch = git
pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
pkg_eredis_pool_commit = master

PACKAGES += erlang_cep
pkg_erlang_cep_name = erlang_cep
pkg_erlang_cep_description = A basic CEP package written in erlang
pkg_erlang_cep_homepage = https://github.com/danmacklin/erlang_cep
pkg_erlang_cep_fetch = git
pkg_erlang_cep_repo = https://github.com/danmacklin/erlang_cep
pkg_erlang_cep_commit = master

PACKAGES += erlang_js
pkg_erlang_js_name = erlang_js
pkg_erlang_js_description = A linked-in driver for Erlang to Mozilla's Spidermonkey Javascript runtime.
pkg_erlang_js_homepage = https://github.com/basho/erlang_js
pkg_erlang_js_fetch = git
pkg_erlang_js_repo = https://github.com/basho/erlang_js
pkg_erlang_js_commit = master

PACKAGES += erlang_localtime
pkg_erlang_localtime_name = erlang_localtime
pkg_erlang_localtime_description = Erlang library for conversion from one local time to another
pkg_erlang_localtime_homepage = https://github.com/dmitryme/erlang_localtime
pkg_erlang_localtime_fetch = git
pkg_erlang_localtime_repo = https://github.com/dmitryme/erlang_localtime
pkg_erlang_localtime_commit = master

PACKAGES += erlang_smtp
pkg_erlang_smtp_name = erlang_smtp
pkg_erlang_smtp_description = Erlang SMTP and POP3 server code.
pkg_erlang_smtp_homepage = https://github.com/tonyg/erlang-smtp
pkg_erlang_smtp_fetch = git
pkg_erlang_smtp_repo = https://github.com/tonyg/erlang-smtp
pkg_erlang_smtp_commit = master

PACKAGES += erlang_term
pkg_erlang_term_name = erlang_term
pkg_erlang_term_description = Erlang Term Info
pkg_erlang_term_homepage = https://github.com/okeuday/erlang_term
pkg_erlang_term_fetch = git
pkg_erlang_term_repo = https://github.com/okeuday/erlang_term
pkg_erlang_term_commit = master

PACKAGES += erlastic_search
pkg_erlastic_search_name = erlastic_search
pkg_erlastic_search_description = An Erlang app for communicating with Elastic Search's rest interface.
pkg_erlastic_search_homepage = https://github.com/tsloughter/erlastic_search
pkg_erlastic_search_fetch = git
pkg_erlastic_search_repo = https://github.com/tsloughter/erlastic_search
pkg_erlastic_search_commit = master

PACKAGES += erlasticsearch
pkg_erlasticsearch_name = erlasticsearch
pkg_erlasticsearch_description = Erlang thrift interface to elastic_search
pkg_erlasticsearch_homepage = https://github.com/dieswaytoofast/erlasticsearch
pkg_erlasticsearch_fetch = git
pkg_erlasticsearch_repo = https://github.com/dieswaytoofast/erlasticsearch
pkg_erlasticsearch_commit = master

PACKAGES += erlbrake
pkg_erlbrake_name = erlbrake
pkg_erlbrake_description = Erlang Airbrake notification client
pkg_erlbrake_homepage = https://github.com/kenpratt/erlbrake
pkg_erlbrake_fetch = git
pkg_erlbrake_repo = https://github.com/kenpratt/erlbrake
pkg_erlbrake_commit = master

PACKAGES += erlcloud
pkg_erlcloud_name = erlcloud
pkg_erlcloud_description = Cloud Computing library for erlang (Amazon EC2, S3, SQS, SimpleDB, Mechanical Turk, ELB)
pkg_erlcloud_homepage = https://github.com/gleber/erlcloud
pkg_erlcloud_fetch = git
pkg_erlcloud_repo = https://github.com/gleber/erlcloud
pkg_erlcloud_commit = master

PACKAGES += erlcron
pkg_erlcron_name = erlcron
pkg_erlcron_description = Erlang cronish system
pkg_erlcron_homepage = https://github.com/erlware/erlcron
pkg_erlcron_fetch = git
pkg_erlcron_repo = https://github.com/erlware/erlcron
pkg_erlcron_commit = master

PACKAGES += erldb
pkg_erldb_name = erldb
pkg_erldb_description = ORM (Object-relational mapping) application implemented in Erlang
pkg_erldb_homepage = http://erldb.org
pkg_erldb_fetch = git
pkg_erldb_repo = https://github.com/erldb/erldb
pkg_erldb_commit = master

PACKAGES += erldis
pkg_erldis_name = erldis
pkg_erldis_description = redis erlang client library
pkg_erldis_homepage = https://github.com/cstar/erldis
pkg_erldis_fetch = git
pkg_erldis_repo = https://github.com/cstar/erldis
pkg_erldis_commit = master

PACKAGES += erldns
pkg_erldns_name = erldns
pkg_erldns_description = DNS server, in erlang.
pkg_erldns_homepage = https://github.com/aetrion/erl-dns
pkg_erldns_fetch = git
pkg_erldns_repo = https://github.com/aetrion/erl-dns
pkg_erldns_commit = master

PACKAGES += erldocker
pkg_erldocker_name = erldocker
pkg_erldocker_description = Docker Remote API client for Erlang
pkg_erldocker_homepage = https://github.com/proger/erldocker
pkg_erldocker_fetch = git
pkg_erldocker_repo = https://github.com/proger/erldocker
pkg_erldocker_commit = master

PACKAGES += erlfsmon
pkg_erlfsmon_name = erlfsmon
pkg_erlfsmon_description = Erlang filesystem event watcher for Linux and OSX
pkg_erlfsmon_homepage = https://github.com/proger/erlfsmon
pkg_erlfsmon_fetch = git
pkg_erlfsmon_repo = https://github.com/proger/erlfsmon
pkg_erlfsmon_commit = master

PACKAGES += erlgit
pkg_erlgit_name = erlgit
pkg_erlgit_description = Erlang convenience wrapper around git executable
pkg_erlgit_homepage = https://github.com/gleber/erlgit
pkg_erlgit_fetch = git
pkg_erlgit_repo = https://github.com/gleber/erlgit
pkg_erlgit_commit = master

PACKAGES += erlguten
pkg_erlguten_name = erlguten
pkg_erlguten_description = ErlGuten is a system for high-quality typesetting, written purely in Erlang.
pkg_erlguten_homepage = https://github.com/richcarl/erlguten
pkg_erlguten_fetch = git
pkg_erlguten_repo = https://github.com/richcarl/erlguten
pkg_erlguten_commit = master

PACKAGES += erlmc
pkg_erlmc_name = erlmc
pkg_erlmc_description = Erlang memcached binary protocol client
pkg_erlmc_homepage = https://github.com/jkvor/erlmc
pkg_erlmc_fetch = git
pkg_erlmc_repo = https://github.com/jkvor/erlmc
pkg_erlmc_commit = master

PACKAGES += erlmongo
pkg_erlmongo_name = erlmongo
pkg_erlmongo_description = Record based Erlang driver for MongoDB with gridfs support
pkg_erlmongo_homepage = https://github.com/SergejJurecko/erlmongo
pkg_erlmongo_fetch = git
pkg_erlmongo_repo = https://github.com/SergejJurecko/erlmongo
pkg_erlmongo_commit = master

PACKAGES += erlog
pkg_erlog_name = erlog
pkg_erlog_description = Prolog interpreter in and for Erlang
pkg_erlog_homepage = https://github.com/rvirding/erlog
pkg_erlog_fetch = git
pkg_erlog_repo = https://github.com/rvirding/erlog
pkg_erlog_commit = master

PACKAGES += erlpass
pkg_erlpass_name = erlpass
pkg_erlpass_description = A library to handle password hashing and changing in a safe manner, independent from any kind of storage whatsoever.
pkg_erlpass_homepage = https://github.com/ferd/erlpass
pkg_erlpass_fetch = git
pkg_erlpass_repo = https://github.com/ferd/erlpass
pkg_erlpass_commit = master

PACKAGES += erlport
pkg_erlport_name = erlport
pkg_erlport_description = ErlPort - connect Erlang to other languages
pkg_erlport_homepage = https://github.com/hdima/erlport
pkg_erlport_fetch = git
pkg_erlport_repo = https://github.com/hdima/erlport
pkg_erlport_commit = master

PACKAGES += erlsha2
pkg_erlsha2_name = erlsha2
pkg_erlsha2_description = SHA-224, SHA-256, SHA-384, SHA-512 implemented in Erlang NIFs.
pkg_erlsha2_homepage = https://github.com/vinoski/erlsha2
pkg_erlsha2_fetch = git
pkg_erlsha2_repo = https://github.com/vinoski/erlsha2
pkg_erlsha2_commit = master

PACKAGES += erlsh
pkg_erlsh_name = erlsh
pkg_erlsh_description = Erlang shell tools
pkg_erlsh_homepage = https://github.com/proger/erlsh
pkg_erlsh_fetch = git
pkg_erlsh_repo = https://github.com/proger/erlsh
pkg_erlsh_commit = master

PACKAGES += erlsom
pkg_erlsom_name = erlsom
pkg_erlsom_description = XML parser for Erlang
pkg_erlsom_homepage = https://github.com/willemdj/erlsom
pkg_erlsom_fetch = git
pkg_erlsom_repo = https://github.com/willemdj/erlsom
pkg_erlsom_commit = master

PACKAGES += erl_streams
pkg_erl_streams_name = erl_streams
pkg_erl_streams_description = Streams in Erlang
pkg_erl_streams_homepage = https://github.com/epappas/erl_streams
pkg_erl_streams_fetch = git
pkg_erl_streams_repo = https://github.com/epappas/erl_streams
pkg_erl_streams_commit = master

PACKAGES += erlubi
pkg_erlubi_name = erlubi
pkg_erlubi_description = Ubigraph Erlang Client (and Process Visualizer)
pkg_erlubi_homepage = https://github.com/krestenkrab/erlubi
pkg_erlubi_fetch = git
pkg_erlubi_repo = https://github.com/krestenkrab/erlubi
pkg_erlubi_commit = master

PACKAGES += erlvolt
pkg_erlvolt_name = erlvolt
pkg_erlvolt_description = VoltDB Erlang Client Driver
pkg_erlvolt_homepage = https://github.com/VoltDB/voltdb-client-erlang
pkg_erlvolt_fetch = git
pkg_erlvolt_repo = https://github.com/VoltDB/voltdb-client-erlang
pkg_erlvolt_commit = master

PACKAGES += erlware_commons
pkg_erlware_commons_name = erlware_commons
pkg_erlware_commons_description = Erlware Commons is an Erlware project focused on all aspects of reusable Erlang components.
pkg_erlware_commons_homepage = https://github.com/erlware/erlware_commons
pkg_erlware_commons_fetch = git
pkg_erlware_commons_repo = https://github.com/erlware/erlware_commons
pkg_erlware_commons_commit = master

PACKAGES += erlydtl
pkg_erlydtl_name = erlydtl
pkg_erlydtl_description = Django Template Language for Erlang.
pkg_erlydtl_homepage = https://github.com/erlydtl/erlydtl
pkg_erlydtl_fetch = git
pkg_erlydtl_repo = https://github.com/erlydtl/erlydtl
pkg_erlydtl_commit = master

PACKAGES += errd
pkg_errd_name = errd
pkg_errd_description = Erlang RRDTool library
pkg_errd_homepage = https://github.com/archaelus/errd
pkg_errd_fetch = git
pkg_errd_repo = https://github.com/archaelus/errd
pkg_errd_commit = master

PACKAGES += erserve
pkg_erserve_name = erserve
pkg_erserve_description = Erlang/Rserve communication interface
pkg_erserve_homepage = https://github.com/del/erserve
pkg_erserve_fetch = git
pkg_erserve_repo = https://github.com/del/erserve
pkg_erserve_commit = master

PACKAGES += erwa
pkg_erwa_name = erwa
pkg_erwa_description = A WAMP router and client written in Erlang.
pkg_erwa_homepage = https://github.com/bwegh/erwa
pkg_erwa_fetch = git
pkg_erwa_repo = https://github.com/bwegh/erwa
pkg_erwa_commit = 0.1.1

PACKAGES += espec
pkg_espec_name = espec
pkg_espec_description = ESpec: Behaviour driven development framework for Erlang
pkg_espec_homepage = https://github.com/lucaspiller/espec
pkg_espec_fetch = git
pkg_espec_repo = https://github.com/lucaspiller/espec
pkg_espec_commit = master

PACKAGES += estatsd
pkg_estatsd_name = estatsd
pkg_estatsd_description = Erlang stats aggregation app that periodically flushes data to graphite
pkg_estatsd_homepage = https://github.com/RJ/estatsd
pkg_estatsd_fetch = git
pkg_estatsd_repo = https://github.com/RJ/estatsd
pkg_estatsd_commit = master

PACKAGES += etap
pkg_etap_name = etap
pkg_etap_description = etap is a simple erlang testing library that provides TAP compliant output.
pkg_etap_homepage = https://github.com/ngerakines/etap
pkg_etap_fetch = git
pkg_etap_repo = https://github.com/ngerakines/etap
pkg_etap_commit = master

PACKAGES += etest_http
pkg_etest_http_name = etest_http
pkg_etest_http_description = etest Assertions around HTTP (client-side)
pkg_etest_http_homepage = https://github.com/wooga/etest_http
pkg_etest_http_fetch = git
pkg_etest_http_repo = https://github.com/wooga/etest_http
pkg_etest_http_commit = master

PACKAGES += etest
pkg_etest_name = etest
pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
pkg_etest_homepage = https://github.com/wooga/etest
pkg_etest_fetch = git
pkg_etest_repo = https://github.com/wooga/etest
pkg_etest_commit = master

PACKAGES += etoml
pkg_etoml_name = etoml
pkg_etoml_description = TOML language erlang parser
pkg_etoml_homepage = https://github.com/kalta/etoml
pkg_etoml_fetch = git
pkg_etoml_repo = https://github.com/kalta/etoml
pkg_etoml_commit = master

PACKAGES += eunit_formatters
pkg_eunit_formatters_name = eunit_formatters
pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
pkg_eunit_formatters_homepage = https://github.com/seancribbs/eunit_formatters
pkg_eunit_formatters_fetch = git
pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
pkg_eunit_formatters_commit = master

PACKAGES += eunit
pkg_eunit_name = eunit
pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
pkg_eunit_homepage = https://github.com/richcarl/eunit
pkg_eunit_fetch = git
pkg_eunit_repo = https://github.com/richcarl/eunit
pkg_eunit_commit = master

PACKAGES += euthanasia
pkg_euthanasia_name = euthanasia
pkg_euthanasia_description = Merciful killer for your Erlang processes
pkg_euthanasia_homepage = https://github.com/doubleyou/euthanasia
pkg_euthanasia_fetch = git
pkg_euthanasia_repo = https://github.com/doubleyou/euthanasia
pkg_euthanasia_commit = master

PACKAGES += evum
pkg_evum_name = evum
pkg_evum_description = Spawn Linux VMs as Erlang processes in the Erlang VM
pkg_evum_homepage = https://github.com/msantos/evum
pkg_evum_fetch = git
pkg_evum_repo = https://github.com/msantos/evum
pkg_evum_commit = master

PACKAGES += exec
pkg_exec_name = exec
pkg_exec_description = Execute and control OS processes from Erlang/OTP.
pkg_exec_homepage = http://saleyn.github.com/erlexec
pkg_exec_fetch = git
pkg_exec_repo = https://github.com/saleyn/erlexec
pkg_exec_commit = master

PACKAGES += exml
pkg_exml_name = exml
pkg_exml_description = XML parsing library in Erlang
pkg_exml_homepage = https://github.com/paulgray/exml
pkg_exml_fetch = git
pkg_exml_repo = https://github.com/paulgray/exml
pkg_exml_commit = master

PACKAGES += exometer
pkg_exometer_name = exometer
pkg_exometer_description = Basic measurement objects and probe behavior
pkg_exometer_homepage = https://github.com/Feuerlabs/exometer
pkg_exometer_fetch = git
pkg_exometer_repo = https://github.com/Feuerlabs/exometer
pkg_exometer_commit = 1.2

PACKAGES += exs1024
pkg_exs1024_name = exs1024
pkg_exs1024_description = Xorshift1024star pseudo random number generator for Erlang.
pkg_exs1024_homepage = https://github.com/jj1bdx/exs1024
pkg_exs1024_fetch = git
pkg_exs1024_repo = https://github.com/jj1bdx/exs1024
pkg_exs1024_commit = master

PACKAGES += exs64
pkg_exs64_name = exs64
pkg_exs64_description = Xorshift64star pseudo random number generator for Erlang.
pkg_exs64_homepage = https://github.com/jj1bdx/exs64
pkg_exs64_fetch = git
pkg_exs64_repo = https://github.com/jj1bdx/exs64
pkg_exs64_commit = master

PACKAGES += exsplus116
pkg_exsplus116_name = exsplus116
pkg_exsplus116_description = Xorshift116plus for Erlang
pkg_exsplus116_homepage = https://github.com/jj1bdx/exsplus116
pkg_exsplus116_fetch = git
pkg_exsplus116_repo = https://github.com/jj1bdx/exsplus116
pkg_exsplus116_commit = master

PACKAGES += exsplus128
pkg_exsplus128_name = exsplus128
pkg_exsplus128_description = Xorshift128plus pseudo random number generator for Erlang.
pkg_exsplus128_homepage = https://github.com/jj1bdx/exsplus128
pkg_exsplus128_fetch = git
pkg_exsplus128_repo = https://github.com/jj1bdx/exsplus128
pkg_exsplus128_commit = master

PACKAGES += ezmq
pkg_ezmq_name = ezmq
pkg_ezmq_description = zMQ implemented in Erlang
pkg_ezmq_homepage = https://github.com/RoadRunnr/ezmq
pkg_ezmq_fetch = git
pkg_ezmq_repo = https://github.com/RoadRunnr/ezmq
pkg_ezmq_commit = master

PACKAGES += ezmtp
pkg_ezmtp_name = ezmtp
pkg_ezmtp_description = ZMTP protocol in pure Erlang.
pkg_ezmtp_homepage = https://github.com/a13x/ezmtp
pkg_ezmtp_fetch = git
pkg_ezmtp_repo = https://github.com/a13x/ezmtp
pkg_ezmtp_commit = master

PACKAGES += fast_disk_log
pkg_fast_disk_log_name = fast_disk_log
pkg_fast_disk_log_description = Pool-based asynchronous Erlang disk logger
pkg_fast_disk_log_homepage = https://github.com/lpgauth/fast_disk_log
pkg_fast_disk_log_fetch = git
pkg_fast_disk_log_repo = https://github.com/lpgauth/fast_disk_log
pkg_fast_disk_log_commit = master

PACKAGES += feeder
pkg_feeder_name = feeder
pkg_feeder_description = Stream parse RSS and Atom formatted XML feeds.
pkg_feeder_homepage = https://github.com/michaelnisi/feeder
pkg_feeder_fetch = git
pkg_feeder_repo = https://github.com/michaelnisi/feeder
pkg_feeder_commit = v1.4.6

PACKAGES += fix
pkg_fix_name = fix
pkg_fix_description = http://fixprotocol.org/ implementation.
pkg_fix_homepage = https://github.com/maxlapshin/fix
pkg_fix_fetch = git
pkg_fix_repo = https://github.com/maxlapshin/fix
pkg_fix_commit = master

PACKAGES += flower
pkg_flower_name = flower
pkg_flower_description = FlowER - a Erlang OpenFlow development platform
pkg_flower_homepage = https://github.com/travelping/flower
pkg_flower_fetch = git
pkg_flower_repo = https://github.com/travelping/flower
pkg_flower_commit = master

PACKAGES += fn
pkg_fn_name = fn
pkg_fn_description = Function utilities for Erlang
pkg_fn_homepage = https://github.com/reiddraper/fn
pkg_fn_fetch = git
pkg_fn_repo = https://github.com/reiddraper/fn
pkg_fn_commit = master

PACKAGES += folsom_cowboy
pkg_folsom_cowboy_name = folsom_cowboy
pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
pkg_folsom_cowboy_homepage = https://github.com/boundary/folsom_cowboy
pkg_folsom_cowboy_fetch = git
pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
pkg_folsom_cowboy_commit = master

PACKAGES += folsomite
pkg_folsomite_name = folsomite
pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
pkg_folsomite_homepage = https://github.com/campanja/folsomite
pkg_folsomite_fetch = git
pkg_folsomite_repo = https://github.com/campanja/folsomite
pkg_folsomite_commit = master

PACKAGES += folsom
pkg_folsom_name = folsom
pkg_folsom_description = Expose Erlang Events and Metrics
pkg_folsom_homepage = https://github.com/boundary/folsom
pkg_folsom_fetch = git
pkg_folsom_repo = https://github.com/boundary/folsom
pkg_folsom_commit = master

PACKAGES += fs
pkg_fs_name = fs
pkg_fs_description = Erlang FileSystem Listener
pkg_fs_homepage = https://github.com/synrc/fs
pkg_fs_fetch = git
pkg_fs_repo = https://github.com/synrc/fs
pkg_fs_commit = master

PACKAGES += fuse
pkg_fuse_name = fuse
pkg_fuse_description = A Circuit Breaker for Erlang
pkg_fuse_homepage = https://github.com/jlouis/fuse
pkg_fuse_fetch = git
pkg_fuse_repo = https://github.com/jlouis/fuse
pkg_fuse_commit = master

PACKAGES += gcm
pkg_gcm_name = gcm
pkg_gcm_description = An Erlang application for Google Cloud Messaging
pkg_gcm_homepage = https://github.com/pdincau/gcm-erlang
pkg_gcm_fetch = git
pkg_gcm_repo = https://github.com/pdincau/gcm-erlang
pkg_gcm_commit = master

PACKAGES += gcprof
pkg_gcprof_name = gcprof
pkg_gcprof_description = Garbage Collection profiler for Erlang
pkg_gcprof_homepage = https://github.com/knutin/gcprof
pkg_gcprof_fetch = git
pkg_gcprof_repo = https://github.com/knutin/gcprof
pkg_gcprof_commit = master

PACKAGES += geas
pkg_geas_name = geas
pkg_geas_description = Guess Erlang Application Scattering
pkg_geas_homepage = https://github.com/crownedgrouse/geas
pkg_geas_fetch = git
pkg_geas_repo = https://github.com/crownedgrouse/geas
pkg_geas_commit = master

PACKAGES += geef
pkg_geef_name = geef
pkg_geef_description = Git NEEEEF (Erlang NIF)
pkg_geef_homepage = https://github.com/carlosmn/geef
pkg_geef_fetch = git
pkg_geef_repo = https://github.com/carlosmn/geef
pkg_geef_commit = master

PACKAGES += gen_cycle
pkg_gen_cycle_name = gen_cycle
pkg_gen_cycle_description = Simple, generic OTP behaviour for recurring tasks
pkg_gen_cycle_homepage = https://github.com/aerosol/gen_cycle
pkg_gen_cycle_fetch = git
pkg_gen_cycle_repo = https://github.com/aerosol/gen_cycle
pkg_gen_cycle_commit = develop

PACKAGES += gen_icmp
pkg_gen_icmp_name = gen_icmp
pkg_gen_icmp_description = Erlang interface to ICMP sockets
pkg_gen_icmp_homepage = https://github.com/msantos/gen_icmp
pkg_gen_icmp_fetch = git
pkg_gen_icmp_repo = https://github.com/msantos/gen_icmp
pkg_gen_icmp_commit = master

PACKAGES += gen_nb_server
pkg_gen_nb_server_name = gen_nb_server
pkg_gen_nb_server_description = OTP behavior for writing non-blocking servers
pkg_gen_nb_server_homepage = https://github.com/kevsmith/gen_nb_server
pkg_gen_nb_server_fetch = git
pkg_gen_nb_server_repo = https://github.com/kevsmith/gen_nb_server
pkg_gen_nb_server_commit = master

PACKAGES += gen_paxos
pkg_gen_paxos_name = gen_paxos
pkg_gen_paxos_description = An Erlang/OTP-style implementation of the PAXOS distributed consensus protocol
pkg_gen_paxos_homepage = https://github.com/gburd/gen_paxos
pkg_gen_paxos_fetch = git
pkg_gen_paxos_repo = https://github.com/gburd/gen_paxos
pkg_gen_paxos_commit = master

PACKAGES += gen_smtp
pkg_gen_smtp_name = gen_smtp
pkg_gen_smtp_description = A generic Erlang SMTP server and client that can be extended via callback modules
pkg_gen_smtp_homepage = https://github.com/Vagabond/gen_smtp
pkg_gen_smtp_fetch = git
pkg_gen_smtp_repo = https://github.com/Vagabond/gen_smtp
pkg_gen_smtp_commit = master

PACKAGES += gen_tracker
pkg_gen_tracker_name = gen_tracker
pkg_gen_tracker_description = supervisor with ets handling of children and their metadata
pkg_gen_tracker_homepage = https://github.com/erlyvideo/gen_tracker
pkg_gen_tracker_fetch = git
pkg_gen_tracker_repo = https://github.com/erlyvideo/gen_tracker
pkg_gen_tracker_commit = master

PACKAGES += gen_unix
pkg_gen_unix_name = gen_unix
pkg_gen_unix_description = Erlang Unix socket interface
pkg_gen_unix_homepage = https://github.com/msantos/gen_unix
pkg_gen_unix_fetch = git
pkg_gen_unix_repo = https://github.com/msantos/gen_unix
pkg_gen_unix_commit = master

PACKAGES += getopt
pkg_getopt_name = getopt
pkg_getopt_description = Module to parse command line arguments using the GNU getopt syntax
pkg_getopt_homepage = https://github.com/jcomellas/getopt
pkg_getopt_fetch = git
pkg_getopt_repo = https://github.com/jcomellas/getopt
pkg_getopt_commit = master

PACKAGES += gettext
pkg_gettext_name = gettext
pkg_gettext_description = Erlang internationalization library.
pkg_gettext_homepage = https://github.com/etnt/gettext
pkg_gettext_fetch = git
pkg_gettext_repo = https://github.com/etnt/gettext
pkg_gettext_commit = master

PACKAGES += giallo
pkg_giallo_name = giallo
pkg_giallo_description = Small and flexible web framework on top of Cowboy
pkg_giallo_homepage = https://github.com/kivra/giallo
pkg_giallo_fetch = git
pkg_giallo_repo = https://github.com/kivra/giallo
pkg_giallo_commit = master

PACKAGES += gin
pkg_gin_name = gin
pkg_gin_description = The guards  and  for Erlang parse_transform
pkg_gin_homepage = https://github.com/mad-cocktail/gin
pkg_gin_fetch = git
pkg_gin_repo = https://github.com/mad-cocktail/gin
pkg_gin_commit = master

PACKAGES += gitty
pkg_gitty_name = gitty
pkg_gitty_description = Git access in erlang
pkg_gitty_homepage = https://github.com/maxlapshin/gitty
pkg_gitty_fetch = git
pkg_gitty_repo = https://github.com/maxlapshin/gitty
pkg_gitty_commit = master

PACKAGES += gold_fever
pkg_gold_fever_name = gold_fever
pkg_gold_fever_description = A Treasure Hunt for Erlangers
pkg_gold_fever_homepage = https://github.com/inaka/gold_fever
pkg_gold_fever_fetch = git
pkg_gold_fever_repo = https://github.com/inaka/gold_fever
pkg_gold_fever_commit = master

PACKAGES += gossiperl
pkg_gossiperl_name = gossiperl
pkg_gossiperl_description = Gossip middleware in Erlang
pkg_gossiperl_homepage = http://gossiperl.com/
pkg_gossiperl_fetch = git
pkg_gossiperl_repo = https://github.com/gossiperl/gossiperl
pkg_gossiperl_commit = master

PACKAGES += gpb
pkg_gpb_name = gpb
pkg_gpb_description = A Google Protobuf implementation for Erlang
pkg_gpb_homepage = https://github.com/tomas-abrahamsson/gpb
pkg_gpb_fetch = git
pkg_gpb_repo = https://github.com/tomas-abrahamsson/gpb
pkg_gpb_commit = master

PACKAGES += gproc
pkg_gproc_name = gproc
pkg_gproc_description = Extended process registry for Erlang
pkg_gproc_homepage = https://github.com/uwiger/gproc
pkg_gproc_fetch = git
pkg_gproc_repo = https://github.com/uwiger/gproc
pkg_gproc_commit = master

PACKAGES += grapherl
pkg_grapherl_name = grapherl
pkg_grapherl_description = Create graphs of Erlang systems and programs
pkg_grapherl_homepage = https://github.com/eproxus/grapherl
pkg_grapherl_fetch = git
pkg_grapherl_repo = https://github.com/eproxus/grapherl
pkg_grapherl_commit = master

PACKAGES += gun
pkg_gun_name = gun
pkg_gun_description = Asynchronous SPDY, HTTP and Websocket client written in Erlang.
pkg_gun_homepage = http//ninenines.eu
pkg_gun_fetch = git
pkg_gun_repo = https://github.com/ninenines/gun
pkg_gun_commit = master

PACKAGES += gut
pkg_gut_name = gut
pkg_gut_description = gut is a template printing, aka scaffolding, tool for Erlang. Like rails generate or yeoman
pkg_gut_homepage = https://github.com/unbalancedparentheses/gut
pkg_gut_fetch = git
pkg_gut_repo = https://github.com/unbalancedparentheses/gut
pkg_gut_commit = master

PACKAGES += hackney
pkg_hackney_name = hackney
pkg_hackney_description = simple HTTP client in Erlang
pkg_hackney_homepage = https://github.com/benoitc/hackney
pkg_hackney_fetch = git
pkg_hackney_repo = https://github.com/benoitc/hackney
pkg_hackney_commit = master

PACKAGES += hamcrest
pkg_hamcrest_name = hamcrest
pkg_hamcrest_description = Erlang port of Hamcrest
pkg_hamcrest_homepage = https://github.com/hyperthunk/hamcrest-erlang
pkg_hamcrest_fetch = git
pkg_hamcrest_repo = https://github.com/hyperthunk/hamcrest-erlang
pkg_hamcrest_commit = master

PACKAGES += hanoidb
pkg_hanoidb_name = hanoidb
pkg_hanoidb_description = Erlang LSM BTree Storage
pkg_hanoidb_homepage = https://github.com/krestenkrab/hanoidb
pkg_hanoidb_fetch = git
pkg_hanoidb_repo = https://github.com/krestenkrab/hanoidb
pkg_hanoidb_commit = master

PACKAGES += hottub
pkg_hottub_name = hottub
pkg_hottub_description = Permanent Erlang Worker Pool
pkg_hottub_homepage = https://github.com/bfrog/hottub
pkg_hottub_fetch = git
pkg_hottub_repo = https://github.com/bfrog/hottub
pkg_hottub_commit = master

PACKAGES += hpack
pkg_hpack_name = hpack
pkg_hpack_description = HPACK Implementation for Erlang
pkg_hpack_homepage = https://github.com/joedevivo/hpack
pkg_hpack_fetch = git
pkg_hpack_repo = https://github.com/joedevivo/hpack
pkg_hpack_commit = master

PACKAGES += hyper
pkg_hyper_name = hyper
pkg_hyper_description = Erlang implementation of HyperLogLog
pkg_hyper_homepage = https://github.com/GameAnalytics/hyper
pkg_hyper_fetch = git
pkg_hyper_repo = https://github.com/GameAnalytics/hyper
pkg_hyper_commit = master

PACKAGES += ibrowse
pkg_ibrowse_name = ibrowse
pkg_ibrowse_description = Erlang HTTP client
pkg_ibrowse_homepage = https://github.com/cmullaparthi/ibrowse
pkg_ibrowse_fetch = git
pkg_ibrowse_repo = https://github.com/cmullaparthi/ibrowse
pkg_ibrowse_commit = v4.1.1

PACKAGES += ierlang
pkg_ierlang_name = ierlang
pkg_ierlang_description = An Erlang language kernel for IPython.
pkg_ierlang_homepage = https://github.com/robbielynch/ierlang
pkg_ierlang_fetch = git
pkg_ierlang_repo = https://github.com/robbielynch/ierlang
pkg_ierlang_commit = master

PACKAGES += iota
pkg_iota_name = iota
pkg_iota_description = iota (Inter-dependency Objective Testing Apparatus) - a tool to enforce clean separation of responsibilities in Erlang code
pkg_iota_homepage = https://github.com/jpgneves/iota
pkg_iota_fetch = git
pkg_iota_repo = https://github.com/jpgneves/iota
pkg_iota_commit = master

PACKAGES += ircd
pkg_ircd_name = ircd
pkg_ircd_description = A pluggable IRC daemon application/library for Erlang.
pkg_ircd_homepage = https://github.com/tonyg/erlang-ircd
pkg_ircd_fetch = git
pkg_ircd_repo = https://github.com/tonyg/erlang-ircd
pkg_ircd_commit = master

PACKAGES += irc_lib
pkg_irc_lib_name = irc_lib
pkg_irc_lib_description = Erlang irc client library
pkg_irc_lib_homepage = https://github.com/OtpChatBot/irc_lib
pkg_irc_lib_fetch = git
pkg_irc_lib_repo = https://github.com/OtpChatBot/irc_lib
pkg_irc_lib_commit = master

PACKAGES += iris
pkg_iris_name = iris
pkg_iris_description = Iris Erlang binding
pkg_iris_homepage = https://github.com/project-iris/iris-erl
pkg_iris_fetch = git
pkg_iris_repo = https://github.com/project-iris/iris-erl
pkg_iris_commit = master

PACKAGES += iso8601
pkg_iso8601_name = iso8601
pkg_iso8601_description = Erlang ISO 8601 date formatter/parser
pkg_iso8601_homepage = https://github.com/seansawyer/erlang_iso8601
pkg_iso8601_fetch = git
pkg_iso8601_repo = https://github.com/seansawyer/erlang_iso8601
pkg_iso8601_commit = master

PACKAGES += itweet
pkg_itweet_name = itweet
pkg_itweet_description = Twitter Stream API on ibrowse
pkg_itweet_homepage = http://inaka.github.com/itweet/
pkg_itweet_fetch = git
pkg_itweet_repo = https://github.com/inaka/itweet
pkg_itweet_commit = v2.0

PACKAGES += jerg
pkg_jerg_name = jerg
pkg_jerg_description = JSON Schema to Erlang Records Generator
pkg_jerg_homepage = https://github.com/ddossot/jerg
pkg_jerg_fetch = git
pkg_jerg_repo = https://github.com/ddossot/jerg
pkg_jerg_commit = master

PACKAGES += jesse
pkg_jesse_name = jesse
pkg_jesse_description = jesse (JSon Schema Erlang) is an implementation of a json schema validator for Erlang.
pkg_jesse_homepage = https://github.com/klarna/jesse
pkg_jesse_fetch = git
pkg_jesse_repo = https://github.com/klarna/jesse
pkg_jesse_commit = master

PACKAGES += jiffy
pkg_jiffy_name = jiffy
pkg_jiffy_description = JSON NIFs for Erlang.
pkg_jiffy_homepage = https://github.com/davisp/jiffy
pkg_jiffy_fetch = git
pkg_jiffy_repo = https://github.com/davisp/jiffy
pkg_jiffy_commit = master

PACKAGES += jiffy_v
pkg_jiffy_v_name = jiffy_v
pkg_jiffy_v_description = JSON validation utility
pkg_jiffy_v_homepage = https://github.com/shizzard/jiffy-v
pkg_jiffy_v_fetch = git
pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
pkg_jiffy_v_commit = 0.3.3

PACKAGES += jobs
pkg_jobs_name = jobs
pkg_jobs_description = a Job scheduler for load regulation
pkg_jobs_homepage = https://github.com/esl/jobs
pkg_jobs_fetch = git
pkg_jobs_repo = https://github.com/esl/jobs
pkg_jobs_commit = 0.3

PACKAGES += joxa
pkg_joxa_name = joxa
pkg_joxa_description = A Modern Lisp for the Erlang VM
pkg_joxa_homepage = https://github.com/joxa/joxa
pkg_joxa_fetch = git
pkg_joxa_repo = https://github.com/joxa/joxa
pkg_joxa_commit = master

PACKAGES += jsonerl
pkg_jsonerl_name = jsonerl
pkg_jsonerl_description = yet another but slightly different erlang <-> json encoder/decoder
pkg_jsonerl_homepage = https://github.com/lambder/jsonerl
pkg_jsonerl_fetch = git
pkg_jsonerl_repo = https://github.com/lambder/jsonerl
pkg_jsonerl_commit = master

PACKAGES += json
pkg_json_name = json
pkg_json_description = a high level json library for erlang (17.0+)
pkg_json_homepage = https://github.com/talentdeficit/json
pkg_json_fetch = git
pkg_json_repo = https://github.com/talentdeficit/json
pkg_json_commit = master

PACKAGES += jsonpath
pkg_jsonpath_name = jsonpath
pkg_jsonpath_description = Fast Erlang JSON data retrieval and updates via javascript-like notation
pkg_jsonpath_homepage = https://github.com/GeneStevens/jsonpath
pkg_jsonpath_fetch = git
pkg_jsonpath_repo = https://github.com/GeneStevens/jsonpath
pkg_jsonpath_commit = master

PACKAGES += json_rec
pkg_json_rec_name = json_rec
pkg_json_rec_description = JSON to erlang record
pkg_json_rec_homepage = https://github.com/justinkirby/json_rec
pkg_json_rec_fetch = git
pkg_json_rec_repo = https://github.com/justinkirby/json_rec
pkg_json_rec_commit = master

PACKAGES += jsonx
pkg_jsonx_name = jsonx
pkg_jsonx_description = JSONX is an Erlang library for efficient decode and encode JSON, written in C.
pkg_jsonx_homepage = https://github.com/iskra/jsonx
pkg_jsonx_fetch = git
pkg_jsonx_repo = https://github.com/iskra/jsonx
pkg_jsonx_commit = master

PACKAGES += jsx
pkg_jsx_name = jsx
pkg_jsx_description = An Erlang application for consuming, producing and manipulating JSON.
pkg_jsx_homepage = https://github.com/talentdeficit/jsx
pkg_jsx_fetch = git
pkg_jsx_repo = https://github.com/talentdeficit/jsx
pkg_jsx_commit = master

PACKAGES += kafka
pkg_kafka_name = kafka
pkg_kafka_description = Kafka consumer and producer in Erlang
pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
pkg_kafka_fetch = git
pkg_kafka_repo = https://github.com/wooga/kafka-erlang
pkg_kafka_commit = master

PACKAGES += kai
pkg_kai_name = kai
pkg_kai_description = DHT storage by Takeshi Inoue
pkg_kai_homepage = https://github.com/synrc/kai
pkg_kai_fetch = git
pkg_kai_repo = https://github.com/synrc/kai
pkg_kai_commit = master

PACKAGES += katja
pkg_katja_name = katja
pkg_katja_description = A simple Riemann client written in Erlang.
pkg_katja_homepage = https://github.com/nifoc/katja
pkg_katja_fetch = git
pkg_katja_repo = https://github.com/nifoc/katja
pkg_katja_commit = master

PACKAGES += kdht
pkg_kdht_name = kdht
pkg_kdht_description = kdht is an erlang DHT implementation
pkg_kdht_homepage = https://github.com/kevinlynx/kdht
pkg_kdht_fetch = git
pkg_kdht_repo = https://github.com/kevinlynx/kdht
pkg_kdht_commit = master

PACKAGES += key2value
pkg_key2value_name = key2value
pkg_key2value_description = Erlang 2-way map
pkg_key2value_homepage = https://github.com/okeuday/key2value
pkg_key2value_fetch = git
pkg_key2value_repo = https://github.com/okeuday/key2value
pkg_key2value_commit = master

PACKAGES += keys1value
pkg_keys1value_name = keys1value
pkg_keys1value_description = Erlang set associative map for key lists
pkg_keys1value_homepage = https://github.com/okeuday/keys1value
pkg_keys1value_fetch = git
pkg_keys1value_repo = https://github.com/okeuday/keys1value
pkg_keys1value_commit = master

PACKAGES += kinetic
pkg_kinetic_name = kinetic
pkg_kinetic_description = Erlang Kinesis Client
pkg_kinetic_homepage = https://github.com/AdRoll/kinetic
pkg_kinetic_fetch = git
pkg_kinetic_repo = https://github.com/AdRoll/kinetic
pkg_kinetic_commit = master

PACKAGES += kjell
pkg_kjell_name = kjell
pkg_kjell_description = Erlang Shell
pkg_kjell_homepage = https://github.com/karlll/kjell
pkg_kjell_fetch = git
pkg_kjell_repo = https://github.com/karlll/kjell
pkg_kjell_commit = master

PACKAGES += kraken
pkg_kraken_name = kraken
pkg_kraken_description = Distributed Pubsub Server for Realtime Apps
pkg_kraken_homepage = https://github.com/Asana/kraken
pkg_kraken_fetch = git
pkg_kraken_repo = https://github.com/Asana/kraken
pkg_kraken_commit = master

PACKAGES += kucumberl
pkg_kucumberl_name = kucumberl
pkg_kucumberl_description = A pure-erlang, open-source, implementation of Cucumber
pkg_kucumberl_homepage = https://github.com/openshine/kucumberl
pkg_kucumberl_fetch = git
pkg_kucumberl_repo = https://github.com/openshine/kucumberl
pkg_kucumberl_commit = master

PACKAGES += kvc
pkg_kvc_name = kvc
pkg_kvc_description = KVC - Key Value Coding for Erlang data structures
pkg_kvc_homepage = https://github.com/etrepum/kvc
pkg_kvc_fetch = git
pkg_kvc_repo = https://github.com/etrepum/kvc
pkg_kvc_commit = master

PACKAGES += kvlists
pkg_kvlists_name = kvlists
pkg_kvlists_description = Lists of key-value pairs (decoded JSON) in Erlang
pkg_kvlists_homepage = https://github.com/jcomellas/kvlists
pkg_kvlists_fetch = git
pkg_kvlists_repo = https://github.com/jcomellas/kvlists
pkg_kvlists_commit = master

PACKAGES += kvs
pkg_kvs_name = kvs
pkg_kvs_description = Container and Iterator
pkg_kvs_homepage = https://github.com/synrc/kvs
pkg_kvs_fetch = git
pkg_kvs_repo = https://github.com/synrc/kvs
pkg_kvs_commit = master

PACKAGES += lager_amqp_backend
pkg_lager_amqp_backend_name = lager_amqp_backend
pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
pkg_lager_amqp_backend_homepage = https://github.com/jbrisbin/lager_amqp_backend
pkg_lager_amqp_backend_fetch = git
pkg_lager_amqp_backend_repo = https://github.com/jbrisbin/lager_amqp_backend
pkg_lager_amqp_backend_commit = master

PACKAGES += lager
pkg_lager_name = lager
pkg_lager_description = A logging framework for Erlang/OTP.
pkg_lager_homepage = https://github.com/basho/lager
pkg_lager_fetch = git
pkg_lager_repo = https://github.com/basho/lager
pkg_lager_commit = master

PACKAGES += lager_syslog
pkg_lager_syslog_name = lager_syslog
pkg_lager_syslog_description = Syslog backend for lager
pkg_lager_syslog_homepage = https://github.com/basho/lager_syslog
pkg_lager_syslog_fetch = git
pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
pkg_lager_syslog_commit = master

PACKAGES += lambdapad
pkg_lambdapad_name = lambdapad
pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
pkg_lambdapad_homepage = https://github.com/gar1t/lambdapad
pkg_lambdapad_fetch = git
pkg_lambdapad_repo = https://github.com/gar1t/lambdapad
pkg_lambdapad_commit = master

PACKAGES += lasp
pkg_lasp_name = lasp
pkg_lasp_description = A Language for Distributed, Eventually Consistent Computations
pkg_lasp_homepage = http://lasp-lang.org/
pkg_lasp_fetch = git
pkg_lasp_repo = https://github.com/lasp-lang/lasp
pkg_lasp_commit = master

PACKAGES += lasse
pkg_lasse_name = lasse
pkg_lasse_description = SSE handler for Cowboy
pkg_lasse_homepage = https://github.com/inaka/lasse
pkg_lasse_fetch = git
pkg_lasse_repo = https://github.com/inaka/lasse
pkg_lasse_commit = 0.1.0

PACKAGES += ldap
pkg_ldap_name = ldap
pkg_ldap_description = LDAP server written in Erlang
pkg_ldap_homepage = https://github.com/spawnproc/ldap
pkg_ldap_fetch = git
pkg_ldap_repo = https://github.com/spawnproc/ldap
pkg_ldap_commit = master

PACKAGES += lethink
pkg_lethink_name = lethink
pkg_lethink_description = erlang driver for rethinkdb
pkg_lethink_homepage = https://github.com/taybin/lethink
pkg_lethink_fetch = git
pkg_lethink_repo = https://github.com/taybin/lethink
pkg_lethink_commit = master

PACKAGES += lfe
pkg_lfe_name = lfe
pkg_lfe_description = Lisp Flavoured Erlang (LFE)
pkg_lfe_homepage = https://github.com/rvirding/lfe
pkg_lfe_fetch = git
pkg_lfe_repo = https://github.com/rvirding/lfe
pkg_lfe_commit = master

PACKAGES += ling
pkg_ling_name = ling
pkg_ling_description = Erlang on Xen
pkg_ling_homepage = https://github.com/cloudozer/ling
pkg_ling_fetch = git
pkg_ling_repo = https://github.com/cloudozer/ling
pkg_ling_commit = master

PACKAGES += live
pkg_live_name = live
pkg_live_description = Automated module and configuration reloader.
pkg_live_homepage = http://ninenines.eu
pkg_live_fetch = git
pkg_live_repo = https://github.com/ninenines/live
pkg_live_commit = master

PACKAGES += lmq
pkg_lmq_name = lmq
pkg_lmq_description = Lightweight Message Queue
pkg_lmq_homepage = https://github.com/iij/lmq
pkg_lmq_fetch = git
pkg_lmq_repo = https://github.com/iij/lmq
pkg_lmq_commit = master

PACKAGES += locker
pkg_locker_name = locker
pkg_locker_description = Atomic distributed 'check and set' for short-lived keys
pkg_locker_homepage = https://github.com/wooga/locker
pkg_locker_fetch = git
pkg_locker_repo = https://github.com/wooga/locker
pkg_locker_commit = master

PACKAGES += locks
pkg_locks_name = locks
pkg_locks_description = A scalable, deadlock-resolving resource locker
pkg_locks_homepage = https://github.com/uwiger/locks
pkg_locks_fetch = git
pkg_locks_repo = https://github.com/uwiger/locks
pkg_locks_commit = master

PACKAGES += log4erl
pkg_log4erl_name = log4erl
pkg_log4erl_description = A logger for erlang in the spirit of Log4J.
pkg_log4erl_homepage = https://github.com/ahmednawras/log4erl
pkg_log4erl_fetch = git
pkg_log4erl_repo = https://github.com/ahmednawras/log4erl
pkg_log4erl_commit = master

PACKAGES += lol
pkg_lol_name = lol
pkg_lol_description = Lisp on erLang, and programming is fun again
pkg_lol_homepage = https://github.com/b0oh/lol
pkg_lol_fetch = git
pkg_lol_repo = https://github.com/b0oh/lol
pkg_lol_commit = master

PACKAGES += lucid
pkg_lucid_name = lucid
pkg_lucid_description = HTTP/2 server written in Erlang
pkg_lucid_homepage = https://github.com/tatsuhiro-t/lucid
pkg_lucid_fetch = git
pkg_lucid_repo = https://github.com/tatsuhiro-t/lucid
pkg_lucid_commit = master

PACKAGES += luerl
pkg_luerl_name = luerl
pkg_luerl_description = Lua in Erlang
pkg_luerl_homepage = https://github.com/rvirding/luerl
pkg_luerl_fetch = git
pkg_luerl_repo = https://github.com/rvirding/luerl
pkg_luerl_commit = develop

PACKAGES += luwak
pkg_luwak_name = luwak
pkg_luwak_description = Large-object storage interface for Riak
pkg_luwak_homepage = https://github.com/basho/luwak
pkg_luwak_fetch = git
pkg_luwak_repo = https://github.com/basho/luwak
pkg_luwak_commit = master

PACKAGES += lux
pkg_lux_name = lux
pkg_lux_description = Lux (LUcid eXpect scripting) simplifies test automation and provides an Expect-style execution of commands
pkg_lux_homepage = https://github.com/hawk/lux
pkg_lux_fetch = git
pkg_lux_repo = https://github.com/hawk/lux
pkg_lux_commit = master

PACKAGES += machi
pkg_machi_name = machi
pkg_machi_description = Machi file store
pkg_machi_homepage = https://github.com/basho/machi
pkg_machi_fetch = git
pkg_machi_repo = https://github.com/basho/machi
pkg_machi_commit = master

PACKAGES += mad
pkg_mad_name = mad
pkg_mad_description = Small and Fast Rebar Replacement
pkg_mad_homepage = https://github.com/synrc/mad
pkg_mad_fetch = git
pkg_mad_repo = https://github.com/synrc/mad
pkg_mad_commit = master

PACKAGES += marina
pkg_marina_name = marina
pkg_marina_description = Non-blocking Erlang Cassandra CQL3 client
pkg_marina_homepage = https://github.com/lpgauth/marina
pkg_marina_fetch = git
pkg_marina_repo = https://github.com/lpgauth/marina
pkg_marina_commit = master

PACKAGES += mavg
pkg_mavg_name = mavg
pkg_mavg_description = Erlang :: Exponential moving average library
pkg_mavg_homepage = https://github.com/EchoTeam/mavg
pkg_mavg_fetch = git
pkg_mavg_repo = https://github.com/EchoTeam/mavg
pkg_mavg_commit = master

PACKAGES += mcd
pkg_mcd_name = mcd
pkg_mcd_description = Fast memcached protocol client in pure Erlang
pkg_mcd_homepage = https://github.com/EchoTeam/mcd
pkg_mcd_fetch = git
pkg_mcd_repo = https://github.com/EchoTeam/mcd
pkg_mcd_commit = master

PACKAGES += mcerlang
pkg_mcerlang_name = mcerlang
pkg_mcerlang_description = The McErlang model checker for Erlang
pkg_mcerlang_homepage = https://github.com/fredlund/McErlang
pkg_mcerlang_fetch = git
pkg_mcerlang_repo = https://github.com/fredlund/McErlang
pkg_mcerlang_commit = master

PACKAGES += mc_erl
pkg_mc_erl_name = mc_erl
pkg_mc_erl_description = mc-erl is a server for Minecraft 1.4.7 written in Erlang.
pkg_mc_erl_homepage = https://github.com/clonejo/mc-erl
pkg_mc_erl_fetch = git
pkg_mc_erl_repo = https://github.com/clonejo/mc-erl
pkg_mc_erl_commit = master

PACKAGES += meck
pkg_meck_name = meck
pkg_meck_description = A mocking library for Erlang
pkg_meck_homepage = https://github.com/eproxus/meck
pkg_meck_fetch = git
pkg_meck_repo = https://github.com/eproxus/meck
pkg_meck_commit = master

PACKAGES += mekao
pkg_mekao_name = mekao
pkg_mekao_description = SQL constructor
pkg_mekao_homepage = https://github.com/ddosia/mekao
pkg_mekao_fetch = git
pkg_mekao_repo = https://github.com/ddosia/mekao
pkg_mekao_commit = master

PACKAGES += memo
pkg_memo_name = memo
pkg_memo_description = Erlang memoization server
pkg_memo_homepage = https://github.com/tuncer/memo
pkg_memo_fetch = git
pkg_memo_repo = https://github.com/tuncer/memo
pkg_memo_commit = master

PACKAGES += merge_index
pkg_merge_index_name = merge_index
pkg_merge_index_description = MergeIndex is an Erlang library for storing ordered sets on disk. It is very similar to an SSTable (in Google's Bigtable) or an HFile (in Hadoop).
pkg_merge_index_homepage = https://github.com/basho/merge_index
pkg_merge_index_fetch = git
pkg_merge_index_repo = https://github.com/basho/merge_index
pkg_merge_index_commit = master

PACKAGES += merl
pkg_merl_name = merl
pkg_merl_description = Metaprogramming in Erlang
pkg_merl_homepage = https://github.com/richcarl/merl
pkg_merl_fetch = git
pkg_merl_repo = https://github.com/richcarl/merl
pkg_merl_commit = master

PACKAGES += mimetypes
pkg_mimetypes_name = mimetypes
pkg_mimetypes_description = Erlang MIME types library
pkg_mimetypes_homepage = https://github.com/spawngrid/mimetypes
pkg_mimetypes_fetch = git
pkg_mimetypes_repo = https://github.com/spawngrid/mimetypes
pkg_mimetypes_commit = master

PACKAGES += mixer
pkg_mixer_name = mixer
pkg_mixer_description = Mix in functions from other modules
pkg_mixer_homepage = https://github.com/chef/mixer
pkg_mixer_fetch = git
pkg_mixer_repo = https://github.com/chef/mixer
pkg_mixer_commit = master

PACKAGES += mochiweb
pkg_mochiweb_name = mochiweb
pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
pkg_mochiweb_fetch = git
pkg_mochiweb_repo = https://github.com/mochi/mochiweb
pkg_mochiweb_commit = master

PACKAGES += mochiweb_xpath
pkg_mochiweb_xpath_name = mochiweb_xpath
pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
pkg_mochiweb_xpath_homepage = https://github.com/retnuh/mochiweb_xpath
pkg_mochiweb_xpath_fetch = git
pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
pkg_mochiweb_xpath_commit = master

PACKAGES += mockgyver
pkg_mockgyver_name = mockgyver
pkg_mockgyver_description = A mocking library for Erlang
pkg_mockgyver_homepage = https://github.com/klajo/mockgyver
pkg_mockgyver_fetch = git
pkg_mockgyver_repo = https://github.com/klajo/mockgyver
pkg_mockgyver_commit = master

PACKAGES += modlib
pkg_modlib_name = modlib
pkg_modlib_description = Web framework based on Erlang's inets httpd
pkg_modlib_homepage = https://github.com/gar1t/modlib
pkg_modlib_fetch = git
pkg_modlib_repo = https://github.com/gar1t/modlib
pkg_modlib_commit = master

PACKAGES += mongodb
pkg_mongodb_name = mongodb
pkg_mongodb_description = MongoDB driver for Erlang
pkg_mongodb_homepage = https://github.com/comtihon/mongodb-erlang
pkg_mongodb_fetch = git
pkg_mongodb_repo = https://github.com/comtihon/mongodb-erlang
pkg_mongodb_commit = master

PACKAGES += mongooseim
pkg_mongooseim_name = mongooseim
pkg_mongooseim_description = Jabber / XMPP server with focus on performance and scalability, by Erlang Solutions
pkg_mongooseim_homepage = https://www.erlang-solutions.com/products/mongooseim-massively-scalable-ejabberd-platform
pkg_mongooseim_fetch = git
pkg_mongooseim_repo = https://github.com/esl/MongooseIM
pkg_mongooseim_commit = master

PACKAGES += moyo
pkg_moyo_name = moyo
pkg_moyo_description = Erlang utility functions library
pkg_moyo_homepage = https://github.com/dwango/moyo
pkg_moyo_fetch = git
pkg_moyo_repo = https://github.com/dwango/moyo
pkg_moyo_commit = master

PACKAGES += msgpack
pkg_msgpack_name = msgpack
pkg_msgpack_description = MessagePack (de)serializer implementation for Erlang
pkg_msgpack_homepage = https://github.com/msgpack/msgpack-erlang
pkg_msgpack_fetch = git
pkg_msgpack_repo = https://github.com/msgpack/msgpack-erlang
pkg_msgpack_commit = master

PACKAGES += mu2
pkg_mu2_name = mu2
pkg_mu2_description = Erlang mutation testing tool
pkg_mu2_homepage = https://github.com/ramsay-t/mu2
pkg_mu2_fetch = git
pkg_mu2_repo = https://github.com/ramsay-t/mu2
pkg_mu2_commit = master

PACKAGES += mustache
pkg_mustache_name = mustache
pkg_mustache_description = Mustache template engine for Erlang.
pkg_mustache_homepage = https://github.com/mojombo/mustache.erl
pkg_mustache_fetch = git
pkg_mustache_repo = https://github.com/mojombo/mustache.erl
pkg_mustache_commit = master

PACKAGES += myproto
pkg_myproto_name = myproto
pkg_myproto_description = MySQL Server Protocol in Erlang
pkg_myproto_homepage = https://github.com/altenwald/myproto
pkg_myproto_fetch = git
pkg_myproto_repo = https://github.com/altenwald/myproto
pkg_myproto_commit = master

PACKAGES += mysql
pkg_mysql_name = mysql
pkg_mysql_description = Erlang MySQL Driver (from code.google.com)
pkg_mysql_homepage = https://github.com/dizzyd/erlang-mysql-driver
pkg_mysql_fetch = git
pkg_mysql_repo = https://github.com/dizzyd/erlang-mysql-driver
pkg_mysql_commit = master

PACKAGES += n2o
pkg_n2o_name = n2o
pkg_n2o_description = WebSocket Application Server
pkg_n2o_homepage = https://github.com/5HT/n2o
pkg_n2o_fetch = git
pkg_n2o_repo = https://github.com/5HT/n2o
pkg_n2o_commit = master

PACKAGES += nat_upnp
pkg_nat_upnp_name = nat_upnp
pkg_nat_upnp_description = Erlang library to map your internal port to an external using UNP IGD
pkg_nat_upnp_homepage = https://github.com/benoitc/nat_upnp
pkg_nat_upnp_fetch = git
pkg_nat_upnp_repo = https://github.com/benoitc/nat_upnp
pkg_nat_upnp_commit = master

PACKAGES += neo4j
pkg_neo4j_name = neo4j
pkg_neo4j_description = Erlang client library for Neo4J.
pkg_neo4j_homepage = https://github.com/dmitriid/neo4j-erlang
pkg_neo4j_fetch = git
pkg_neo4j_repo = https://github.com/dmitriid/neo4j-erlang
pkg_neo4j_commit = master

PACKAGES += neotoma
pkg_neotoma_name = neotoma
pkg_neotoma_description = Erlang library and packrat parser-generator for parsing expression grammars.
pkg_neotoma_homepage = https://github.com/seancribbs/neotoma
pkg_neotoma_fetch = git
pkg_neotoma_repo = https://github.com/seancribbs/neotoma
pkg_neotoma_commit = master

PACKAGES += newrelic
pkg_newrelic_name = newrelic
pkg_newrelic_description = Erlang library for sending metrics to New Relic
pkg_newrelic_homepage = https://github.com/wooga/newrelic-erlang
pkg_newrelic_fetch = git
pkg_newrelic_repo = https://github.com/wooga/newrelic-erlang
pkg_newrelic_commit = master

PACKAGES += nifty
pkg_nifty_name = nifty
pkg_nifty_description = Erlang NIF wrapper generator
pkg_nifty_homepage = https://github.com/parapluu/nifty
pkg_nifty_fetch = git
pkg_nifty_repo = https://github.com/parapluu/nifty
pkg_nifty_commit = master

PACKAGES += nitrogen_core
pkg_nitrogen_core_name = nitrogen_core
pkg_nitrogen_core_description = The core Nitrogen library.
pkg_nitrogen_core_homepage = http://nitrogenproject.com/
pkg_nitrogen_core_fetch = git
pkg_nitrogen_core_repo = https://github.com/nitrogen/nitrogen_core
pkg_nitrogen_core_commit = master

PACKAGES += nkbase
pkg_nkbase_name = nkbase
pkg_nkbase_description = NkBASE distributed database
pkg_nkbase_homepage = https://github.com/Nekso/nkbase
pkg_nkbase_fetch = git
pkg_nkbase_repo = https://github.com/Nekso/nkbase
pkg_nkbase_commit = develop

PACKAGES += nkdocker
pkg_nkdocker_name = nkdocker
pkg_nkdocker_description = Erlang Docker client
pkg_nkdocker_homepage = https://github.com/Nekso/nkdocker
pkg_nkdocker_fetch = git
pkg_nkdocker_repo = https://github.com/Nekso/nkdocker
pkg_nkdocker_commit = master

PACKAGES += nkpacket
pkg_nkpacket_name = nkpacket
pkg_nkpacket_description = Generic Erlang transport layer
pkg_nkpacket_homepage = https://github.com/Nekso/nkpacket
pkg_nkpacket_fetch = git
pkg_nkpacket_repo = https://github.com/Nekso/nkpacket
pkg_nkpacket_commit = master

PACKAGES += nodefinder
pkg_nodefinder_name = nodefinder
pkg_nodefinder_description = automatic node discovery via UDP multicast
pkg_nodefinder_homepage = https://github.com/erlanger/nodefinder
pkg_nodefinder_fetch = git
pkg_nodefinder_repo = https://github.com/okeuday/nodefinder
pkg_nodefinder_commit = master

PACKAGES += nprocreg
pkg_nprocreg_name = nprocreg
pkg_nprocreg_description = Minimal Distributed Erlang Process Registry
pkg_nprocreg_homepage = http://nitrogenproject.com/
pkg_nprocreg_fetch = git
pkg_nprocreg_repo = https://github.com/nitrogen/nprocreg
pkg_nprocreg_commit = master

PACKAGES += oauth2c
pkg_oauth2c_name = oauth2c
pkg_oauth2c_description = Erlang OAuth2 Client
pkg_oauth2c_homepage = https://github.com/kivra/oauth2_client
pkg_oauth2c_fetch = git
pkg_oauth2c_repo = https://github.com/kivra/oauth2_client
pkg_oauth2c_commit = master

PACKAGES += oauth2
pkg_oauth2_name = oauth2
pkg_oauth2_description = Erlang Oauth2 implementation
pkg_oauth2_homepage = https://github.com/kivra/oauth2
pkg_oauth2_fetch = git
pkg_oauth2_repo = https://github.com/kivra/oauth2
pkg_oauth2_commit = master

PACKAGES += oauth
pkg_oauth_name = oauth
pkg_oauth_description = An Erlang OAuth 1.0 implementation
pkg_oauth_homepage = https://github.com/tim/erlang-oauth
pkg_oauth_fetch = git
pkg_oauth_repo = https://github.com/tim/erlang-oauth
pkg_oauth_commit = master

PACKAGES += of_protocol
pkg_of_protocol_name = of_protocol
pkg_of_protocol_description = OpenFlow Protocol Library for Erlang
pkg_of_protocol_homepage = https://github.com/FlowForwarding/of_protocol
pkg_of_protocol_fetch = git
pkg_of_protocol_repo = https://github.com/FlowForwarding/of_protocol
pkg_of_protocol_commit = master

PACKAGES += openflow
pkg_openflow_name = openflow
pkg_openflow_description = An OpenFlow controller written in pure erlang
pkg_openflow_homepage = https://github.com/renatoaguiar/erlang-openflow
pkg_openflow_fetch = git
pkg_openflow_repo = https://github.com/renatoaguiar/erlang-openflow
pkg_openflow_commit = master

PACKAGES += openid
pkg_openid_name = openid
pkg_openid_description = Erlang OpenID
pkg_openid_homepage = https://github.com/brendonh/erl_openid
pkg_openid_fetch = git
pkg_openid_repo = https://github.com/brendonh/erl_openid
pkg_openid_commit = master

PACKAGES += openpoker
pkg_openpoker_name = openpoker
pkg_openpoker_description = Genesis Texas hold'em Game Server
pkg_openpoker_homepage = https://github.com/hpyhacking/openpoker
pkg_openpoker_fetch = git
pkg_openpoker_repo = https://github.com/hpyhacking/openpoker
pkg_openpoker_commit = master

PACKAGES += pal
pkg_pal_name = pal
pkg_pal_description = Pragmatic Authentication Library
pkg_pal_homepage = https://github.com/manifest/pal
pkg_pal_fetch = git
pkg_pal_repo = https://github.com/manifest/pal
pkg_pal_commit = master

PACKAGES += parse_trans
pkg_parse_trans_name = parse_trans
pkg_parse_trans_description = Parse transform utilities for Erlang
pkg_parse_trans_homepage = https://github.com/uwiger/parse_trans
pkg_parse_trans_fetch = git
pkg_parse_trans_repo = https://github.com/uwiger/parse_trans
pkg_parse_trans_commit = master

PACKAGES += parsexml
pkg_parsexml_name = parsexml
pkg_parsexml_description = Simple DOM XML parser with convenient and very simple API
pkg_parsexml_homepage = https://github.com/maxlapshin/parsexml
pkg_parsexml_fetch = git
pkg_parsexml_repo = https://github.com/maxlapshin/parsexml
pkg_parsexml_commit = master

PACKAGES += pegjs
pkg_pegjs_name = pegjs
pkg_pegjs_description = An implementation of PEG.js grammar for Erlang.
pkg_pegjs_homepage = https://github.com/dmitriid/pegjs
pkg_pegjs_fetch = git
pkg_pegjs_repo = https://github.com/dmitriid/pegjs
pkg_pegjs_commit = 0.3

PACKAGES += percept2
pkg_percept2_name = percept2
pkg_percept2_description = Concurrent profiling tool for Erlang
pkg_percept2_homepage = https://github.com/huiqing/percept2
pkg_percept2_fetch = git
pkg_percept2_repo = https://github.com/huiqing/percept2
pkg_percept2_commit = master

PACKAGES += pgsql
pkg_pgsql_name = pgsql
pkg_pgsql_description = Erlang PostgreSQL driver
pkg_pgsql_homepage = https://github.com/semiocast/pgsql
pkg_pgsql_fetch = git
pkg_pgsql_repo = https://github.com/semiocast/pgsql
pkg_pgsql_commit = master

PACKAGES += pkgx
pkg_pkgx_name = pkgx
pkg_pkgx_description = Build .deb packages from Erlang releases
pkg_pkgx_homepage = https://github.com/arjan/pkgx
pkg_pkgx_fetch = git
pkg_pkgx_repo = https://github.com/arjan/pkgx
pkg_pkgx_commit = master

PACKAGES += pkt
pkg_pkt_name = pkt
pkg_pkt_description = Erlang network protocol library
pkg_pkt_homepage = https://github.com/msantos/pkt
pkg_pkt_fetch = git
pkg_pkt_repo = https://github.com/msantos/pkt
pkg_pkt_commit = master

PACKAGES += plain_fsm
pkg_plain_fsm_name = plain_fsm
pkg_plain_fsm_description = A behaviour/support library for writing plain Erlang FSMs.
pkg_plain_fsm_homepage = https://github.com/uwiger/plain_fsm
pkg_plain_fsm_fetch = git
pkg_plain_fsm_repo = https://github.com/uwiger/plain_fsm
pkg_plain_fsm_commit = master

PACKAGES += plumtree
pkg_plumtree_name = plumtree
pkg_plumtree_description = Epidemic Broadcast Trees
pkg_plumtree_homepage = https://github.com/helium/plumtree
pkg_plumtree_fetch = git
pkg_plumtree_repo = https://github.com/helium/plumtree
pkg_plumtree_commit = master

PACKAGES += pmod_transform
pkg_pmod_transform_name = pmod_transform
pkg_pmod_transform_description = Parse transform for parameterized modules
pkg_pmod_transform_homepage = https://github.com/erlang/pmod_transform
pkg_pmod_transform_fetch = git
pkg_pmod_transform_repo = https://github.com/erlang/pmod_transform
pkg_pmod_transform_commit = master

PACKAGES += pobox
pkg_pobox_name = pobox
pkg_pobox_description = External buffer processes to protect against mailbox overflow in Erlang
pkg_pobox_homepage = https://github.com/ferd/pobox
pkg_pobox_fetch = git
pkg_pobox_repo = https://github.com/ferd/pobox
pkg_pobox_commit = master

PACKAGES += ponos
pkg_ponos_name = ponos
pkg_ponos_description = ponos is a simple yet powerful load generator written in erlang
pkg_ponos_homepage = https://github.com/klarna/ponos
pkg_ponos_fetch = git
pkg_ponos_repo = https://github.com/klarna/ponos
pkg_ponos_commit = master

PACKAGES += poolboy
pkg_poolboy_name = poolboy
pkg_poolboy_description = A hunky Erlang worker pool factory
pkg_poolboy_homepage = https://github.com/devinus/poolboy
pkg_poolboy_fetch = git
pkg_poolboy_repo = https://github.com/devinus/poolboy
pkg_poolboy_commit = master

PACKAGES += pooler
pkg_pooler_name = pooler
pkg_pooler_description = An OTP Process Pool Application
pkg_pooler_homepage = https://github.com/seth/pooler
pkg_pooler_fetch = git
pkg_pooler_repo = https://github.com/seth/pooler
pkg_pooler_commit = master

PACKAGES += pqueue
pkg_pqueue_name = pqueue
pkg_pqueue_description = Erlang Priority Queues
pkg_pqueue_homepage = https://github.com/okeuday/pqueue
pkg_pqueue_fetch = git
pkg_pqueue_repo = https://github.com/okeuday/pqueue
pkg_pqueue_commit = master

PACKAGES += procket
pkg_procket_name = procket
pkg_procket_description = Erlang interface to low level socket operations
pkg_procket_homepage = http://blog.listincomprehension.com/search/label/procket
pkg_procket_fetch = git
pkg_procket_repo = https://github.com/msantos/procket
pkg_procket_commit = master

PACKAGES += proper
pkg_proper_name = proper
pkg_proper_description = PropEr: a QuickCheck-inspired property-based testing tool for Erlang.
pkg_proper_homepage = http://proper.softlab.ntua.gr
pkg_proper_fetch = git
pkg_proper_repo = https://github.com/manopapad/proper
pkg_proper_commit = master

PACKAGES += prop
pkg_prop_name = prop
pkg_prop_description = An Erlang code scaffolding and generator system.
pkg_prop_homepage = https://github.com/nuex/prop
pkg_prop_fetch = git
pkg_prop_repo = https://github.com/nuex/prop
pkg_prop_commit = master

PACKAGES += props
pkg_props_name = props
pkg_props_description = Property structure library
pkg_props_homepage = https://github.com/greyarea/props
pkg_props_fetch = git
pkg_props_repo = https://github.com/greyarea/props
pkg_props_commit = master

PACKAGES += protobuffs
pkg_protobuffs_name = protobuffs
pkg_protobuffs_description = An implementation of Google's Protocol Buffers for Erlang, based on ngerakines/erlang_protobuffs.
pkg_protobuffs_homepage = https://github.com/basho/erlang_protobuffs
pkg_protobuffs_fetch = git
pkg_protobuffs_repo = https://github.com/basho/erlang_protobuffs
pkg_protobuffs_commit = master

PACKAGES += psycho
pkg_psycho_name = psycho
pkg_psycho_description = HTTP server that provides a WSGI-like interface for applications and middleware.
pkg_psycho_homepage = https://github.com/gar1t/psycho
pkg_psycho_fetch = git
pkg_psycho_repo = https://github.com/gar1t/psycho
pkg_psycho_commit = master

PACKAGES += ptrackerl
pkg_ptrackerl_name = ptrackerl
pkg_ptrackerl_description = Pivotal Tracker API Client written in Erlang
pkg_ptrackerl_homepage = https://github.com/inaka/ptrackerl
pkg_ptrackerl_fetch = git
pkg_ptrackerl_repo = https://github.com/inaka/ptrackerl
pkg_ptrackerl_commit = master

PACKAGES += purity
pkg_purity_name = purity
pkg_purity_description = A side-effect analyzer for Erlang
pkg_purity_homepage = https://github.com/mpitid/purity
pkg_purity_fetch = git
pkg_purity_repo = https://github.com/mpitid/purity
pkg_purity_commit = master

PACKAGES += push_service
pkg_push_service_name = push_service
pkg_push_service_description = Push service
pkg_push_service_homepage = https://github.com/hairyhum/push_service
pkg_push_service_fetch = git
pkg_push_service_repo = https://github.com/hairyhum/push_service
pkg_push_service_commit = master

PACKAGES += qdate
pkg_qdate_name = qdate
pkg_qdate_description = Date, time, and timezone parsing, formatting, and conversion for Erlang.
pkg_qdate_homepage = https://github.com/choptastic/qdate
pkg_qdate_fetch = git
pkg_qdate_repo = https://github.com/choptastic/qdate
pkg_qdate_commit = 0.4.0

PACKAGES += qrcode
pkg_qrcode_name = qrcode
pkg_qrcode_description = QR Code encoder in Erlang
pkg_qrcode_homepage = https://github.com/komone/qrcode
pkg_qrcode_fetch = git
pkg_qrcode_repo = https://github.com/komone/qrcode
pkg_qrcode_commit = master

PACKAGES += quest
pkg_quest_name = quest
pkg_quest_description = Learn Erlang through this set of challenges. An interactive system for getting to know Erlang.
pkg_quest_homepage = https://github.com/eriksoe/ErlangQuest
pkg_quest_fetch = git
pkg_quest_repo = https://github.com/eriksoe/ErlangQuest
pkg_quest_commit = master

PACKAGES += quickrand
pkg_quickrand_name = quickrand
pkg_quickrand_description = Quick Erlang Random Number Generation
pkg_quickrand_homepage = https://github.com/okeuday/quickrand
pkg_quickrand_fetch = git
pkg_quickrand_repo = https://github.com/okeuday/quickrand
pkg_quickrand_commit = master

PACKAGES += rabbit_exchange_type_riak
pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
pkg_rabbit_exchange_type_riak_homepage = https://github.com/jbrisbin/riak-exchange
pkg_rabbit_exchange_type_riak_fetch = git
pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
pkg_rabbit_exchange_type_riak_commit = master

PACKAGES += rabbit
pkg_rabbit_name = rabbit
pkg_rabbit_description = RabbitMQ Server
pkg_rabbit_homepage = https://www.rabbitmq.com/
pkg_rabbit_fetch = git
pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
pkg_rabbit_commit = master

PACKAGES += rack
pkg_rack_name = rack
pkg_rack_description = Rack handler for erlang
pkg_rack_homepage = https://github.com/erlyvideo/rack
pkg_rack_fetch = git
pkg_rack_repo = https://github.com/erlyvideo/rack
pkg_rack_commit = master

PACKAGES += radierl
pkg_radierl_name = radierl
pkg_radierl_description = RADIUS protocol stack implemented in Erlang.
pkg_radierl_homepage = https://github.com/vances/radierl
pkg_radierl_fetch = git
pkg_radierl_repo = https://github.com/vances/radierl
pkg_radierl_commit = master

PACKAGES += rafter
pkg_rafter_name = rafter
pkg_rafter_description = An Erlang library application which implements the Raft consensus protocol
pkg_rafter_homepage = https://github.com/andrewjstone/rafter
pkg_rafter_fetch = git
pkg_rafter_repo = https://github.com/andrewjstone/rafter
pkg_rafter_commit = master

PACKAGES += ranch
pkg_ranch_name = ranch
pkg_ranch_description = Socket acceptor pool for TCP protocols.
pkg_ranch_homepage = http://ninenines.eu
pkg_ranch_fetch = git
pkg_ranch_repo = https://github.com/ninenines/ranch
pkg_ranch_commit = 1.1.0

PACKAGES += rbeacon
pkg_rbeacon_name = rbeacon
pkg_rbeacon_description = LAN discovery and presence in Erlang.
pkg_rbeacon_homepage = https://github.com/refuge/rbeacon
pkg_rbeacon_fetch = git
pkg_rbeacon_repo = https://github.com/refuge/rbeacon
pkg_rbeacon_commit = master

PACKAGES += rebar
pkg_rebar_name = rebar
pkg_rebar_description = Erlang build tool that makes it easy to compile and test Erlang applications, port drivers and releases.
pkg_rebar_homepage = http://www.rebar3.org
pkg_rebar_fetch = git
pkg_rebar_repo = https://github.com/rebar/rebar3
pkg_rebar_commit = master

PACKAGES += rebus
pkg_rebus_name = rebus
pkg_rebus_description = A stupid simple, internal, pub/sub event bus written in- and for Erlang.
pkg_rebus_homepage = https://github.com/olle/rebus
pkg_rebus_fetch = git
pkg_rebus_repo = https://github.com/olle/rebus
pkg_rebus_commit = master

PACKAGES += rec2json
pkg_rec2json_name = rec2json
pkg_rec2json_description = Compile erlang record definitions into modules to convert them to/from json easily.
pkg_rec2json_homepage = https://github.com/lordnull/rec2json
pkg_rec2json_fetch = git
pkg_rec2json_repo = https://github.com/lordnull/rec2json
pkg_rec2json_commit = master

PACKAGES += recon
pkg_recon_name = recon
pkg_recon_description = Collection of functions and scripts to debug Erlang in production.
pkg_recon_homepage = https://github.com/ferd/recon
pkg_recon_fetch = git
pkg_recon_repo = https://github.com/ferd/recon
pkg_recon_commit = 2.2.1

PACKAGES += record_info
pkg_record_info_name = record_info
pkg_record_info_description = Convert between record and proplist
pkg_record_info_homepage = https://github.com/bipthelin/erlang-record_info
pkg_record_info_fetch = git
pkg_record_info_repo = https://github.com/bipthelin/erlang-record_info
pkg_record_info_commit = master

PACKAGES += redgrid
pkg_redgrid_name = redgrid
pkg_redgrid_description = automatic Erlang node discovery via redis
pkg_redgrid_homepage = https://github.com/jkvor/redgrid
pkg_redgrid_fetch = git
pkg_redgrid_repo = https://github.com/jkvor/redgrid
pkg_redgrid_commit = master

PACKAGES += redo
pkg_redo_name = redo
pkg_redo_description = pipelined erlang redis client
pkg_redo_homepage = https://github.com/jkvor/redo
pkg_redo_fetch = git
pkg_redo_repo = https://github.com/jkvor/redo
pkg_redo_commit = master

PACKAGES += reltool_util
pkg_reltool_util_name = reltool_util
pkg_reltool_util_description = Erlang reltool utility functionality application
pkg_reltool_util_homepage = https://github.com/okeuday/reltool_util
pkg_reltool_util_fetch = git
pkg_reltool_util_repo = https://github.com/okeuday/reltool_util
pkg_reltool_util_commit = master

PACKAGES += relx
pkg_relx_name = relx
pkg_relx_description = Sane, simple release creation for Erlang
pkg_relx_homepage = https://github.com/erlware/relx
pkg_relx_fetch = git
pkg_relx_repo = https://github.com/erlware/relx
pkg_relx_commit = master

PACKAGES += resource_discovery
pkg_resource_discovery_name = resource_discovery
pkg_resource_discovery_description = An application used to dynamically discover resources present in an Erlang node cluster.
pkg_resource_discovery_homepage = http://erlware.org/
pkg_resource_discovery_fetch = git
pkg_resource_discovery_repo = https://github.com/erlware/resource_discovery
pkg_resource_discovery_commit = master

PACKAGES += restc
pkg_restc_name = restc
pkg_restc_description = Erlang Rest Client
pkg_restc_homepage = https://github.com/kivra/restclient
pkg_restc_fetch = git
pkg_restc_repo = https://github.com/kivra/restclient
pkg_restc_commit = master

PACKAGES += rfc4627_jsonrpc
pkg_rfc4627_jsonrpc_name = rfc4627_jsonrpc
pkg_rfc4627_jsonrpc_description = Erlang RFC4627 (JSON) codec and JSON-RPC server implementation.
pkg_rfc4627_jsonrpc_homepage = https://github.com/tonyg/erlang-rfc4627
pkg_rfc4627_jsonrpc_fetch = git
pkg_rfc4627_jsonrpc_repo = https://github.com/tonyg/erlang-rfc4627
pkg_rfc4627_jsonrpc_commit = master

PACKAGES += riakc
pkg_riakc_name = riakc
pkg_riakc_description = Erlang clients for Riak.
pkg_riakc_homepage = https://github.com/basho/riak-erlang-client
pkg_riakc_fetch = git
pkg_riakc_repo = https://github.com/basho/riak-erlang-client
pkg_riakc_commit = master

PACKAGES += riak_control
pkg_riak_control_name = riak_control
pkg_riak_control_description = Webmachine-based administration interface for Riak.
pkg_riak_control_homepage = https://github.com/basho/riak_control
pkg_riak_control_fetch = git
pkg_riak_control_repo = https://github.com/basho/riak_control
pkg_riak_control_commit = master

PACKAGES += riak_core
pkg_riak_core_name = riak_core
pkg_riak_core_description = Distributed systems infrastructure used by Riak.
pkg_riak_core_homepage = https://github.com/basho/riak_core
pkg_riak_core_fetch = git
pkg_riak_core_repo = https://github.com/basho/riak_core
pkg_riak_core_commit = master

PACKAGES += riak_dt
pkg_riak_dt_name = riak_dt
pkg_riak_dt_description = Convergent replicated datatypes in Erlang
pkg_riak_dt_homepage = https://github.com/basho/riak_dt
pkg_riak_dt_fetch = git
pkg_riak_dt_repo = https://github.com/basho/riak_dt
pkg_riak_dt_commit = master

PACKAGES += riak_ensemble
pkg_riak_ensemble_name = riak_ensemble
pkg_riak_ensemble_description = Multi-Paxos framework in Erlang
pkg_riak_ensemble_homepage = https://github.com/basho/riak_ensemble
pkg_riak_ensemble_fetch = git
pkg_riak_ensemble_repo = https://github.com/basho/riak_ensemble
pkg_riak_ensemble_commit = master

PACKAGES += riakhttpc
pkg_riakhttpc_name = riakhttpc
pkg_riakhttpc_description = Riak Erlang client using the HTTP interface
pkg_riakhttpc_homepage = https://github.com/basho/riak-erlang-http-client
pkg_riakhttpc_fetch = git
pkg_riakhttpc_repo = https://github.com/basho/riak-erlang-http-client
pkg_riakhttpc_commit = master

PACKAGES += riak_kv
pkg_riak_kv_name = riak_kv
pkg_riak_kv_description = Riak Key/Value Store
pkg_riak_kv_homepage = https://github.com/basho/riak_kv
pkg_riak_kv_fetch = git
pkg_riak_kv_repo = https://github.com/basho/riak_kv
pkg_riak_kv_commit = master

PACKAGES += riaknostic
pkg_riaknostic_name = riaknostic
pkg_riaknostic_description = A diagnostic tool for Riak installations, to find common errors asap
pkg_riaknostic_homepage = https://github.com/basho/riaknostic
pkg_riaknostic_fetch = git
pkg_riaknostic_repo = https://github.com/basho/riaknostic
pkg_riaknostic_commit = master

PACKAGES += riak_pg
pkg_riak_pg_name = riak_pg
pkg_riak_pg_description = Distributed process groups with riak_core.
pkg_riak_pg_homepage = https://github.com/cmeiklejohn/riak_pg
pkg_riak_pg_fetch = git
pkg_riak_pg_repo = https://github.com/cmeiklejohn/riak_pg
pkg_riak_pg_commit = master

PACKAGES += riak_pipe
pkg_riak_pipe_name = riak_pipe
pkg_riak_pipe_description = Riak Pipelines
pkg_riak_pipe_homepage = https://github.com/basho/riak_pipe
pkg_riak_pipe_fetch = git
pkg_riak_pipe_repo = https://github.com/basho/riak_pipe
pkg_riak_pipe_commit = master

PACKAGES += riakpool
pkg_riakpool_name = riakpool
pkg_riakpool_description = erlang riak client pool
pkg_riakpool_homepage = https://github.com/dweldon/riakpool
pkg_riakpool_fetch = git
pkg_riakpool_repo = https://github.com/dweldon/riakpool
pkg_riakpool_commit = master

PACKAGES += riak_sysmon
pkg_riak_sysmon_name = riak_sysmon
pkg_riak_sysmon_description = Simple OTP app for managing Erlang VM system_monitor event messages
pkg_riak_sysmon_homepage = https://github.com/basho/riak_sysmon
pkg_riak_sysmon_fetch = git
pkg_riak_sysmon_repo = https://github.com/basho/riak_sysmon
pkg_riak_sysmon_commit = master

PACKAGES += riak_test
pkg_riak_test_name = riak_test
pkg_riak_test_description = I'm in your cluster, testing your riaks
pkg_riak_test_homepage = https://github.com/basho/riak_test
pkg_riak_test_fetch = git
pkg_riak_test_repo = https://github.com/basho/riak_test
pkg_riak_test_commit = master

PACKAGES += rivus_cep
pkg_rivus_cep_name = rivus_cep
pkg_rivus_cep_description = Complex event processing in Erlang
pkg_rivus_cep_homepage = https://github.com/vascokk/rivus_cep
pkg_rivus_cep_fetch = git
pkg_rivus_cep_repo = https://github.com/vascokk/rivus_cep
pkg_rivus_cep_commit = master

PACKAGES += rlimit
pkg_rlimit_name = rlimit
pkg_rlimit_description = Magnus Klaar's rate limiter code from etorrent
pkg_rlimit_homepage = https://github.com/jlouis/rlimit
pkg_rlimit_fetch = git
pkg_rlimit_repo = https://github.com/jlouis/rlimit
pkg_rlimit_commit = master

PACKAGES += safetyvalve
pkg_safetyvalve_name = safetyvalve
pkg_safetyvalve_description = A safety valve for your erlang node
pkg_safetyvalve_homepage = https://github.com/jlouis/safetyvalve
pkg_safetyvalve_fetch = git
pkg_safetyvalve_repo = https://github.com/jlouis/safetyvalve
pkg_safetyvalve_commit = master

PACKAGES += seestar
pkg_seestar_name = seestar
pkg_seestar_description = The Erlang client for Cassandra 1.2+ binary protocol
pkg_seestar_homepage = https://github.com/iamaleksey/seestar
pkg_seestar_fetch = git
pkg_seestar_repo = https://github.com/iamaleksey/seestar
pkg_seestar_commit = master

PACKAGES += service
pkg_service_name = service
pkg_service_description = A minimal Erlang behavior for creating CloudI internal services
pkg_service_homepage = http://cloudi.org/
pkg_service_fetch = git
pkg_service_repo = https://github.com/CloudI/service
pkg_service_commit = master

PACKAGES += setup
pkg_setup_name = setup
pkg_setup_description = Generic setup utility for Erlang-based systems
pkg_setup_homepage = https://github.com/uwiger/setup
pkg_setup_fetch = git
pkg_setup_repo = https://github.com/uwiger/setup
pkg_setup_commit = master

PACKAGES += sext
pkg_sext_name = sext
pkg_sext_description = Sortable Erlang Term Serialization
pkg_sext_homepage = https://github.com/uwiger/sext
pkg_sext_fetch = git
pkg_sext_repo = https://github.com/uwiger/sext
pkg_sext_commit = master

PACKAGES += sfmt
pkg_sfmt_name = sfmt
pkg_sfmt_description = SFMT pseudo random number generator for Erlang.
pkg_sfmt_homepage = https://github.com/jj1bdx/sfmt-erlang
pkg_sfmt_fetch = git
pkg_sfmt_repo = https://github.com/jj1bdx/sfmt-erlang
pkg_sfmt_commit = master

PACKAGES += sgte
pkg_sgte_name = sgte
pkg_sgte_description = A simple Erlang Template Engine
pkg_sgte_homepage = https://github.com/filippo/sgte
pkg_sgte_fetch = git
pkg_sgte_repo = https://github.com/filippo/sgte
pkg_sgte_commit = master

PACKAGES += sheriff
pkg_sheriff_name = sheriff
pkg_sheriff_description = Parse transform for type based validation.
pkg_sheriff_homepage = http://ninenines.eu
pkg_sheriff_fetch = git
pkg_sheriff_repo = https://github.com/extend/sheriff
pkg_sheriff_commit = master

PACKAGES += shotgun
pkg_shotgun_name = shotgun
pkg_shotgun_description = better than just a gun
pkg_shotgun_homepage = https://github.com/inaka/shotgun
pkg_shotgun_fetch = git
pkg_shotgun_repo = https://github.com/inaka/shotgun
pkg_shotgun_commit = 0.1.0

PACKAGES += sidejob
pkg_sidejob_name = sidejob
pkg_sidejob_description = Parallel worker and capacity limiting library for Erlang
pkg_sidejob_homepage = https://github.com/basho/sidejob
pkg_sidejob_fetch = git
pkg_sidejob_repo = https://github.com/basho/sidejob
pkg_sidejob_commit = master

PACKAGES += sieve
pkg_sieve_name = sieve
pkg_sieve_description = sieve is a simple TCP routing proxy (layer 7) in erlang
pkg_sieve_homepage = https://github.com/benoitc/sieve
pkg_sieve_fetch = git
pkg_sieve_repo = https://github.com/benoitc/sieve
pkg_sieve_commit = master

PACKAGES += sighandler
pkg_sighandler_name = sighandler
pkg_sighandler_description = Handle UNIX signals in Er    lang
pkg_sighandler_homepage = https://github.com/jkingsbery/sighandler
pkg_sighandler_fetch = git
pkg_sighandler_repo = https://github.com/jkingsbery/sighandler
pkg_sighandler_commit = master

PACKAGES += simhash
pkg_simhash_name = simhash
pkg_simhash_description = Simhashing for Erlang -- hashing algorithm to find near-duplicates in binary data.
pkg_simhash_homepage = https://github.com/ferd/simhash
pkg_simhash_fetch = git
pkg_simhash_repo = https://github.com/ferd/simhash
pkg_simhash_commit = master

PACKAGES += simple_bridge
pkg_simple_bridge_name = simple_bridge
pkg_simple_bridge_description = A simple, standardized interface library to Erlang HTTP Servers.
pkg_simple_bridge_homepage = https://github.com/nitrogen/simple_bridge
pkg_simple_bridge_fetch = git
pkg_simple_bridge_repo = https://github.com/nitrogen/simple_bridge
pkg_simple_bridge_commit = master

PACKAGES += simple_oauth2
pkg_simple_oauth2_name = simple_oauth2
pkg_simple_oauth2_description = Simple erlang OAuth2 client module for any http server framework (Google, Facebook, Yandex, Vkontakte are preconfigured)
pkg_simple_oauth2_homepage = https://github.com/virtan/simple_oauth2
pkg_simple_oauth2_fetch = git
pkg_simple_oauth2_repo = https://github.com/virtan/simple_oauth2
pkg_simple_oauth2_commit = master

PACKAGES += skel
pkg_skel_name = skel
pkg_skel_description = A Streaming Process-based Skeleton Library for Erlang
pkg_skel_homepage = https://github.com/ParaPhrase/skel
pkg_skel_fetch = git
pkg_skel_repo = https://github.com/ParaPhrase/skel
pkg_skel_commit = master

PACKAGES += smother
pkg_smother_name = smother
pkg_smother_description = Extended code coverage metrics for Erlang.
pkg_smother_homepage = https://ramsay-t.github.io/Smother/
pkg_smother_fetch = git
pkg_smother_repo = https://github.com/ramsay-t/Smother
pkg_smother_commit = master

PACKAGES += social
pkg_social_name = social
pkg_social_description = Cowboy handler for social login via OAuth2 providers
pkg_social_homepage = https://github.com/dvv/social
pkg_social_fetch = git
pkg_social_repo = https://github.com/dvv/social
pkg_social_commit = master

PACKAGES += spapi_router
pkg_spapi_router_name = spapi_router
pkg_spapi_router_description = Partially-connected Erlang clustering
pkg_spapi_router_homepage = https://github.com/spilgames/spapi-router
pkg_spapi_router_fetch = git
pkg_spapi_router_repo = https://github.com/spilgames/spapi-router
pkg_spapi_router_commit = master

PACKAGES += sqerl
pkg_sqerl_name = sqerl
pkg_sqerl_description = An Erlang-flavoured SQL DSL
pkg_sqerl_homepage = https://github.com/hairyhum/sqerl
pkg_sqerl_fetch = git
pkg_sqerl_repo = https://github.com/hairyhum/sqerl
pkg_sqerl_commit = master

PACKAGES += srly
pkg_srly_name = srly
pkg_srly_description = Native Erlang Unix serial interface
pkg_srly_homepage = https://github.com/msantos/srly
pkg_srly_fetch = git
pkg_srly_repo = https://github.com/msantos/srly
pkg_srly_commit = master

PACKAGES += sshrpc
pkg_sshrpc_name = sshrpc
pkg_sshrpc_description = Erlang SSH RPC module (experimental)
pkg_sshrpc_homepage = https://github.com/jj1bdx/sshrpc
pkg_sshrpc_fetch = git
pkg_sshrpc_repo = https://github.com/jj1bdx/sshrpc
pkg_sshrpc_commit = master

PACKAGES += stable
pkg_stable_name = stable
pkg_stable_description = Library of assorted helpers for Cowboy web server.
pkg_stable_homepage = https://github.com/dvv/stable
pkg_stable_fetch = git
pkg_stable_repo = https://github.com/dvv/stable
pkg_stable_commit = master

PACKAGES += statebox
pkg_statebox_name = statebox
pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
pkg_statebox_homepage = https://github.com/mochi/statebox
pkg_statebox_fetch = git
pkg_statebox_repo = https://github.com/mochi/statebox
pkg_statebox_commit = master

PACKAGES += statebox_riak
pkg_statebox_riak_name = statebox_riak
pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
pkg_statebox_riak_homepage = https://github.com/mochi/statebox_riak
pkg_statebox_riak_fetch = git
pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
pkg_statebox_riak_commit = master

PACKAGES += statman
pkg_statman_name = statman
pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
pkg_statman_homepage = https://github.com/knutin/statman
pkg_statman_fetch = git
pkg_statman_repo = https://github.com/knutin/statman
pkg_statman_commit = master

PACKAGES += statsderl
pkg_statsderl_name = statsderl
pkg_statsderl_description = StatsD client (erlang)
pkg_statsderl_homepage = https://github.com/lpgauth/statsderl
pkg_statsderl_fetch = git
pkg_statsderl_repo = https://github.com/lpgauth/statsderl
pkg_statsderl_commit = master

PACKAGES += stdinout_pool
pkg_stdinout_pool_name = stdinout_pool
pkg_stdinout_pool_description = stdinout_pool    : stuff goes in, stuff goes out. there's never any miscommunication.
pkg_stdinout_pool_homepage = https://github.com/mattsta/erlang-stdinout-pool
pkg_stdinout_pool_fetch = git
pkg_stdinout_pool_repo = https://github.com/mattsta/erlang-stdinout-pool
pkg_stdinout_pool_commit = master

PACKAGES += stockdb
pkg_stockdb_name = stockdb
pkg_stockdb_description = Database for storing Stock Exchange quotes in erlang
pkg_stockdb_homepage = https://github.com/maxlapshin/stockdb
pkg_stockdb_fetch = git
pkg_stockdb_repo = https://github.com/maxlapshin/stockdb
pkg_stockdb_commit = master

PACKAGES += stripe
pkg_stripe_name = stripe
pkg_stripe_description = Erlang interface to the stripe.com API
pkg_stripe_homepage = https://github.com/mattsta/stripe-erlang
pkg_stripe_fetch = git
pkg_stripe_repo = https://github.com/mattsta/stripe-erlang
pkg_stripe_commit = v1

PACKAGES += surrogate
pkg_surrogate_name = surrogate
pkg_surrogate_description = Proxy server written in erlang. Supports reverse proxy load balancing and forward proxy with http (including CONNECT), socks4, socks5, and transparent proxy modes.
pkg_surrogate_homepage = https://github.com/skruger/Surrogate
pkg_surrogate_fetch = git
pkg_surrogate_repo = https://github.com/skruger/Surrogate
pkg_surrogate_commit = master

PACKAGES += swab
pkg_swab_name = swab
pkg_swab_description = General purpose buffer handling module
pkg_swab_homepage = https://github.com/crownedgrouse/swab
pkg_swab_fetch = git
pkg_swab_repo = https://github.com/crownedgrouse/swab
pkg_swab_commit = master

PACKAGES += swarm
pkg_swarm_name = swarm
pkg_swarm_description = Fast and simple acceptor pool for Erlang
pkg_swarm_homepage = https://github.com/jeremey/swarm
pkg_swarm_fetch = git
pkg_swarm_repo = https://github.com/jeremey/swarm
pkg_swarm_commit = master

PACKAGES += switchboard
pkg_switchboard_name = switchboard
pkg_switchboard_description = A framework for processing email using worker plugins.
pkg_switchboard_homepage = https://github.com/thusfresh/switchboard
pkg_switchboard_fetch = git
pkg_switchboard_repo = https://github.com/thusfresh/switchboard
pkg_switchboard_commit = master

PACKAGES += sync
pkg_sync_name = sync
pkg_sync_description = On-the-fly recompiling and reloading in Erlang.
pkg_sync_homepage = https://github.com/rustyio/sync
pkg_sync_fetch = git
pkg_sync_repo = https://github.com/rustyio/sync
pkg_sync_commit = master

PACKAGES += syn
pkg_syn_name = syn
pkg_syn_description = A global process registry for Erlang.
pkg_syn_homepage = https://github.com/ostinelli/syn
pkg_syn_fetch = git
pkg_syn_repo = https://github.com/ostinelli/syn
pkg_syn_commit = master

PACKAGES += syntaxerl
pkg_syntaxerl_name = syntaxerl
pkg_syntaxerl_description = Syntax checker for Erlang
pkg_syntaxerl_homepage = https://github.com/ten0s/syntaxerl
pkg_syntaxerl_fetch = git
pkg_syntaxerl_repo = https://github.com/ten0s/syntaxerl
pkg_syntaxerl_commit = master

PACKAGES += syslog
pkg_syslog_name = syslog
pkg_syslog_description = Erlang port driver for interacting with syslog via syslog(3)
pkg_syslog_homepage = https://github.com/Vagabond/erlang-syslog
pkg_syslog_fetch = git
pkg_syslog_repo = https://github.com/Vagabond/erlang-syslog
pkg_syslog_commit = master

PACKAGES += taskforce
pkg_taskforce_name = taskforce
pkg_taskforce_description = Erlang worker pools for controlled parallelisation of arbitrary tasks.
pkg_taskforce_homepage = https://github.com/g-andrade/taskforce
pkg_taskforce_fetch = git
pkg_taskforce_repo = https://github.com/g-andrade/taskforce
pkg_taskforce_commit = master

PACKAGES += tddreloader
pkg_tddreloader_name = tddreloader
pkg_tddreloader_description = Shell utility for recompiling, reloading, and testing code as it changes
pkg_tddreloader_homepage = https://github.com/version2beta/tddreloader
pkg_tddreloader_fetch = git
pkg_tddreloader_repo = https://github.com/version2beta/tddreloader
pkg_tddreloader_commit = master

PACKAGES += tempo
pkg_tempo_name = tempo
pkg_tempo_description = NIF-based date and time parsing and formatting for Erlang.
pkg_tempo_homepage = https://github.com/selectel/tempo
pkg_tempo_fetch = git
pkg_tempo_repo = https://github.com/selectel/tempo
pkg_tempo_commit = master

PACKAGES += ticktick
pkg_ticktick_name = ticktick
pkg_ticktick_description = Ticktick is an id generator for message service.
pkg_ticktick_homepage = https://github.com/ericliang/ticktick
pkg_ticktick_fetch = git
pkg_ticktick_repo = https://github.com/ericliang/ticktick
pkg_ticktick_commit = master

PACKAGES += tinymq
pkg_tinymq_name = tinymq
pkg_tinymq_description = TinyMQ - a diminutive, in-memory message queue
pkg_tinymq_homepage = https://github.com/ChicagoBoss/tinymq
pkg_tinymq_fetch = git
pkg_tinymq_repo = https://github.com/ChicagoBoss/tinymq
pkg_tinymq_commit = master

PACKAGES += tinymt
pkg_tinymt_name = tinymt
pkg_tinymt_description = TinyMT pseudo random number generator for Erlang.
pkg_tinymt_homepage = https://github.com/jj1bdx/tinymt-erlang
pkg_tinymt_fetch = git
pkg_tinymt_repo = https://github.com/jj1bdx/tinymt-erlang
pkg_tinymt_commit = master

PACKAGES += tirerl
pkg_tirerl_name = tirerl
pkg_tirerl_description = Erlang interface to Elastic Search
pkg_tirerl_homepage = https://github.com/inaka/tirerl
pkg_tirerl_fetch = git
pkg_tirerl_repo = https://github.com/inaka/tirerl
pkg_tirerl_commit = master

PACKAGES += traffic_tools
pkg_traffic_tools_name = traffic_tools
pkg_traffic_tools_description = Simple traffic limiting library
pkg_traffic_tools_homepage = https://github.com/systra/traffic_tools
pkg_traffic_tools_fetch = git
pkg_traffic_tools_repo = https://github.com/systra/traffic_tools
pkg_traffic_tools_commit = master

PACKAGES += trails
pkg_trails_name = trails
pkg_trails_description = A couple of improvements over Cowboy Routes
pkg_trails_homepage = http://inaka.github.io/cowboy-trails/
pkg_trails_fetch = git
pkg_trails_repo = https://github.com/inaka/cowboy-trails
pkg_trails_commit = master

PACKAGES += trane
pkg_trane_name = trane
pkg_trane_description = SAX style broken HTML parser in Erlang
pkg_trane_homepage = https://github.com/massemanet/trane
pkg_trane_fetch = git
pkg_trane_repo = https://github.com/massemanet/trane
pkg_trane_commit = master

PACKAGES += transit
pkg_transit_name = transit
pkg_transit_description = transit format for erlang
pkg_transit_homepage = https://github.com/isaiah/transit-erlang
pkg_transit_fetch = git
pkg_transit_repo = https://github.com/isaiah/transit-erlang
pkg_transit_commit = master

PACKAGES += trie
pkg_trie_name = trie
pkg_trie_description = Erlang Trie Implementation
pkg_trie_homepage = https://github.com/okeuday/trie
pkg_trie_fetch = git
pkg_trie_repo = https://github.com/okeuday/trie
pkg_trie_commit = master

PACKAGES += triq
pkg_triq_name = triq
pkg_triq_description = Trifork QuickCheck
pkg_triq_homepage = https://github.com/krestenkrab/triq
pkg_triq_fetch = git
pkg_triq_repo = https://github.com/krestenkrab/triq
pkg_triq_commit = master

PACKAGES += tunctl
pkg_tunctl_name = tunctl
pkg_tunctl_description = Erlang TUN/TAP interface
pkg_tunctl_homepage = https://github.com/msantos/tunctl
pkg_tunctl_fetch = git
pkg_tunctl_repo = https://github.com/msantos/tunctl
pkg_tunctl_commit = master

PACKAGES += twerl
pkg_twerl_name = twerl
pkg_twerl_description = Erlang client for the Twitter Streaming API
pkg_twerl_homepage = https://github.com/lucaspiller/twerl
pkg_twerl_fetch = git
pkg_twerl_repo = https://github.com/lucaspiller/twerl
pkg_twerl_commit = oauth

PACKAGES += twitter_erlang
pkg_twitter_erlang_name = twitter_erlang
pkg_twitter_erlang_description = An Erlang twitter client
pkg_twitter_erlang_homepage = https://github.com/ngerakines/erlang_twitter
pkg_twitter_erlang_fetch = git
pkg_twitter_erlang_repo = https://github.com/ngerakines/erlang_twitter
pkg_twitter_erlang_commit = master

PACKAGES += ucol_nif
pkg_ucol_nif_name = ucol_nif
pkg_ucol_nif_description = ICU based collation Erlang module
pkg_ucol_nif_homepage = https://github.com/refuge/ucol_nif
pkg_ucol_nif_fetch = git
pkg_ucol_nif_repo = https://github.com/refuge/ucol_nif
pkg_ucol_nif_commit = master

PACKAGES += unicorn
pkg_unicorn_name = unicorn
pkg_unicorn_description = Generic configuration server
pkg_unicorn_homepage = https://github.com/shizzard/unicorn
pkg_unicorn_fetch = git
pkg_unicorn_repo = https://github.com/shizzard/unicorn
pkg_unicorn_commit = 0.3.0

PACKAGES += unsplit
pkg_unsplit_name = unsplit
pkg_unsplit_description = Resolves conflicts in Mnesia after network splits
pkg_unsplit_homepage = https://github.com/uwiger/unsplit
pkg_unsplit_fetch = git
pkg_unsplit_repo = https://github.com/uwiger/unsplit
pkg_unsplit_commit = master

PACKAGES += uuid
pkg_uuid_name = uuid
pkg_uuid_description = Erlang UUID Implementation
pkg_uuid_homepage = https://github.com/okeuday/uuid
pkg_uuid_fetch = git
pkg_uuid_repo = https://github.com/okeuday/uuid
pkg_uuid_commit = v1.4.0

PACKAGES += ux
pkg_ux_name = ux
pkg_ux_description = Unicode eXtention for Erlang (Strings, Collation)
pkg_ux_homepage = https://github.com/erlang-unicode/ux
pkg_ux_fetch = git
pkg_ux_repo = https://github.com/erlang-unicode/ux
pkg_ux_commit = master

PACKAGES += vert
pkg_vert_name = vert
pkg_vert_description = Erlang binding to libvirt virtualization API
pkg_vert_homepage = https://github.com/msantos/erlang-libvirt
pkg_vert_fetch = git
pkg_vert_repo = https://github.com/msantos/erlang-libvirt
pkg_vert_commit = master

PACKAGES += verx
pkg_verx_name = verx
pkg_verx_description = Erlang implementation of the libvirtd remote protocol
pkg_verx_homepage = https://github.com/msantos/verx
pkg_verx_fetch = git
pkg_verx_repo = https://github.com/msantos/verx
pkg_verx_commit = master

PACKAGES += vmq_acl
pkg_vmq_acl_name = vmq_acl
pkg_vmq_acl_description = Component of VerneMQ: A distributed MQTT message broker
pkg_vmq_acl_homepage = https://verne.mq/
pkg_vmq_acl_fetch = git
pkg_vmq_acl_repo = https://github.com/erlio/vmq_acl
pkg_vmq_acl_commit = master

PACKAGES += vmq_bridge
pkg_vmq_bridge_name = vmq_bridge
pkg_vmq_bridge_description = Component of VerneMQ: A distributed MQTT message broker
pkg_vmq_bridge_homepage = https://verne.mq/
pkg_vmq_bridge_fetch = git
pkg_vmq_bridge_repo = https://github.com/erlio/vmq_bridge
pkg_vmq_bridge_commit = master

PACKAGES += vmq_graphite
pkg_vmq_graphite_name = vmq_graphite
pkg_vmq_graphite_description = Component of VerneMQ: A distributed MQTT message broker
pkg_vmq_graphite_homepage = https://verne.mq/
pkg_vmq_graphite_fetch = git
pkg_vmq_graphite_repo = https://github.com/erlio/vmq_graphite
pkg_vmq_graphite_commit = master

PACKAGES += vmq_passwd
pkg_vmq_passwd_name = vmq_passwd
pkg_vmq_passwd_description = Component of VerneMQ: A distributed MQTT message broker
pkg_vmq_passwd_homepage = https://verne.mq/
pkg_vmq_passwd_fetch = git
pkg_vmq_passwd_repo = https://github.com/erlio/vmq_passwd
pkg_vmq_passwd_commit = master

PACKAGES += vmq_server
pkg_vmq_server_name = vmq_server
pkg_vmq_server_description = Component of VerneMQ: A distributed MQTT message broker
pkg_vmq_server_homepage = https://verne.mq/
pkg_vmq_server_fetch = git
pkg_vmq_server_repo = https://github.com/erlio/vmq_server
pkg_vmq_server_commit = master

PACKAGES += vmq_snmp
pkg_vmq_snmp_name = vmq_snmp
pkg_vmq_snmp_description = Component of VerneMQ: A distributed MQTT message broker
pkg_vmq_snmp_homepage = https://verne.mq/
pkg_vmq_snmp_fetch = git
pkg_vmq_snmp_repo = https://github.com/erlio/vmq_snmp
pkg_vmq_snmp_commit = master

PACKAGES += vmq_systree
pkg_vmq_systree_name = vmq_systree
pkg_vmq_systree_description = Component of VerneMQ: A distributed MQTT message broker
pkg_vmq_systree_homepage = https://verne.mq/
pkg_vmq_systree_fetch = git
pkg_vmq_systree_repo = https://github.com/erlio/vmq_systree
pkg_vmq_systree_commit = master

PACKAGES += vmstats
pkg_vmstats_name = vmstats
pkg_vmstats_description = tiny Erlang app that works in conjunction with statsderl in order to generate information on the Erlang VM for graphite logs.
pkg_vmstats_homepage = https://github.com/ferd/vmstats
pkg_vmstats_fetch = git
pkg_vmstats_repo = https://github.com/ferd/vmstats
pkg_vmstats_commit = master

PACKAGES += walrus
pkg_walrus_name = walrus
pkg_walrus_description = Walrus - Mustache-like Templating
pkg_walrus_homepage = https://github.com/devinus/walrus
pkg_walrus_fetch = git
pkg_walrus_repo = https://github.com/devinus/walrus
pkg_walrus_commit = master

PACKAGES += webmachine
pkg_webmachine_name = webmachine
pkg_webmachine_description = A REST-based system for building web applications.
pkg_webmachine_homepage = https://github.com/basho/webmachine
pkg_webmachine_fetch = git
pkg_webmachine_repo = https://github.com/basho/webmachine
pkg_webmachine_commit = master

PACKAGES += websocket_client
pkg_websocket_client_name = websocket_client
pkg_websocket_client_description = Erlang websocket client (ws and wss supported)
pkg_websocket_client_homepage = https://github.com/jeremyong/websocket_client
pkg_websocket_client_fetch = git
pkg_websocket_client_repo = https://github.com/jeremyong/websocket_client
pkg_websocket_client_commit = master

PACKAGES += worker_pool
pkg_worker_pool_name = worker_pool
pkg_worker_pool_description = a simple erlang worker pool
pkg_worker_pool_homepage = https://github.com/inaka/worker_pool
pkg_worker_pool_fetch = git
pkg_worker_pool_repo = https://github.com/inaka/worker_pool
pkg_worker_pool_commit = 1.0.2

PACKAGES += wrangler
pkg_wrangler_name = wrangler
pkg_wrangler_description = Import of the Wrangler svn repository.
pkg_wrangler_homepage = http://www.cs.kent.ac.uk/projects/wrangler/Home.html
pkg_wrangler_fetch = git
pkg_wrangler_repo = https://github.com/RefactoringTools/wrangler
pkg_wrangler_commit = master

PACKAGES += wsock
pkg_wsock_name = wsock
pkg_wsock_description = Erlang library to build WebSocket clients and servers
pkg_wsock_homepage = https://github.com/madtrick/wsock
pkg_wsock_fetch = git
pkg_wsock_repo = https://github.com/madtrick/wsock
pkg_wsock_commit = master

PACKAGES += xhttpc
pkg_xhttpc_name = xhttpc
pkg_xhttpc_description = Extensible HTTP Client for Erlang
pkg_xhttpc_homepage = https://github.com/seriyps/xhttpc
pkg_xhttpc_fetch = git
pkg_xhttpc_repo = https://github.com/seriyps/xhttpc
pkg_xhttpc_commit = master

PACKAGES += xref_runner
pkg_xref_runner_name = xref_runner
pkg_xref_runner_description = Erlang Xref Runner (inspired in rebar xref)
pkg_xref_runner_homepage = https://github.com/inaka/xref_runner
pkg_xref_runner_fetch = git
pkg_xref_runner_repo = https://github.com/inaka/xref_runner
pkg_xref_runner_commit = 0.2.0

PACKAGES += yamerl
pkg_yamerl_name = yamerl
pkg_yamerl_description = YAML 1.2 parser in pure Erlang
pkg_yamerl_homepage = https://github.com/yakaz/yamerl
pkg_yamerl_fetch = git
pkg_yamerl_repo = https://github.com/yakaz/yamerl
pkg_yamerl_commit = master

PACKAGES += yamler
pkg_yamler_name = yamler
pkg_yamler_description = libyaml-based yaml loader for Erlang
pkg_yamler_homepage = https://github.com/goertzenator/yamler
pkg_yamler_fetch = git
pkg_yamler_repo = https://github.com/goertzenator/yamler
pkg_yamler_commit = master

PACKAGES += yaws
pkg_yaws_name = yaws
pkg_yaws_description = Yaws webserver
pkg_yaws_homepage = http://yaws.hyber.org
pkg_yaws_fetch = git
pkg_yaws_repo = https://github.com/klacke/yaws
pkg_yaws_commit = master

PACKAGES += zab_engine
pkg_zab_engine_name = zab_engine
pkg_zab_engine_description = zab propotocol implement by erlang
pkg_zab_engine_homepage = https://github.com/xinmingyao/zab_engine
pkg_zab_engine_fetch = git
pkg_zab_engine_repo = https://github.com/xinmingyao/zab_engine
pkg_zab_engine_commit = master

PACKAGES += zeta
pkg_zeta_name = zeta
pkg_zeta_description = HTTP access log parser in Erlang
pkg_zeta_homepage = https://github.com/s1n4/zeta
pkg_zeta_fetch = git
pkg_zeta_repo = https://github.com/s1n4/zeta
pkg_zeta_commit =  

PACKAGES += zippers
pkg_zippers_name = zippers
pkg_zippers_description = A library for functional zipper data structures in Erlang. Read more on zippers
pkg_zippers_homepage = https://github.com/ferd/zippers
pkg_zippers_fetch = git
pkg_zippers_repo = https://github.com/ferd/zippers
pkg_zippers_commit = master

PACKAGES += zlists
pkg_zlists_name = zlists
pkg_zlists_description = Erlang lazy lists library.
pkg_zlists_homepage = https://github.com/vjache/erlang-zlists
pkg_zlists_fetch = git
pkg_zlists_repo = https://github.com/vjache/erlang-zlists
pkg_zlists_commit = master

PACKAGES += zraft_lib
pkg_zraft_lib_name = zraft_lib
pkg_zraft_lib_description = Erlang raft consensus protocol implementation
pkg_zraft_lib_homepage = https://github.com/dreyk/zraft_lib
pkg_zraft_lib_fetch = git
pkg_zraft_lib_repo = https://github.com/dreyk/zraft_lib
pkg_zraft_lib_commit = master

PACKAGES += zucchini
pkg_zucchini_name = zucchini
pkg_zucchini_description = An Erlang INI parser
pkg_zucchini_homepage = https://github.com/devinus/zucchini
pkg_zucchini_fetch = git
pkg_zucchini_repo = https://github.com/devinus/zucchini
pkg_zucchini_commit = master

# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: search

define pkg_print
	$(verbose) printf "%s\n" \
		$(if $(call core_eq,$(1),$(pkg_$(1)_name)),,"Pkg name:    $(1)") \
		"App name:    $(pkg_$(1)_name)" \
		"Description: $(pkg_$(1)_description)" \
		"Home page:   $(pkg_$(1)_homepage)" \
		"Fetch with:  $(pkg_$(1)_fetch)" \
		"Repository:  $(pkg_$(1)_repo)" \
		"Commit:      $(pkg_$(1)_commit)" \
		""

endef

search:
ifdef q
	$(foreach p,$(PACKAGES), \
		$(if $(findstring $(call core_lc,$(q)),$(call core_lc,$(pkg_$(p)_name) $(pkg_$(p)_description))), \
			$(call pkg_print,$(p))))
else
	$(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
endif

# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: distclean-deps distclean-pkg

# Configuration.

IGNORE_DEPS ?=

DEPS_DIR ?= $(CURDIR)/deps
export DEPS_DIR

REBAR_DEPS_DIR = $(DEPS_DIR)
export REBAR_DEPS_DIR

ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(filter-out $(IGNORE_DEPS),$(DEPS)))

ifeq ($(filter $(DEPS_DIR),$(subst :, ,$(ERL_LIBS))),)
ifeq ($(ERL_LIBS),)
	ERL_LIBS = $(DEPS_DIR)
else
	ERL_LIBS := $(ERL_LIBS):$(DEPS_DIR)
endif
endif
export ERL_LIBS

# Verbosity.

dep_verbose_0 = @echo " DEP   " $(1);
dep_verbose = $(dep_verbose_$(V))

# Core targets.

ifneq ($(SKIP_DEPS),)
deps::
else
deps:: $(ALL_DEPS_DIRS)
ifneq ($(IS_DEP),1)
	$(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
endif
	$(verbose) mkdir -p $(ERLANG_MK_TMP)
	$(verbose) for dep in $(ALL_DEPS_DIRS) ; do \
		if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
			echo -n; \
		else \
			echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
			if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
				$(MAKE) -C $$dep IS_DEP=1 || exit $$?; \
			else \
				echo "ERROR: No Makefile to build dependency $$dep."; \
				exit 1; \
			fi \
		fi \
	done
endif

distclean:: distclean-deps distclean-pkg

# Deps related targets.

# @todo rename GNUmakefile and makefile into Makefile first, if they exist
# While Makefile file could be GNUmakefile or makefile,
# in practice only Makefile is needed so far.
define dep_autopatch
	if [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
		if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
			$(call dep_autopatch2,$(1)); \
		elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \
			$(call dep_autopatch2,$(1)); \
		elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i rebar '{}' \;`" ]; then \
			$(call dep_autopatch2,$(1)); \
		else \
			if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
				$(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
				$(call dep_autopatch_erlang_mk,$(1)); \
			else \
				$(call erlang,$(call dep_autopatch_app.erl,$(1))); \
			fi \
		fi \
	else \
		if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
			$(call dep_autopatch_noop,$(1)); \
		else \
			$(call dep_autopatch2,$(1)); \
		fi \
	fi
endef

define dep_autopatch2
	$(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
	if [ -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
		$(call dep_autopatch_fetch_rebar); \
		$(call dep_autopatch_rebar,$(1)); \
	else \
		$(call dep_autopatch_gen,$(1)); \
	fi
endef

define dep_autopatch_noop
	printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
endef

# Overwrite erlang.mk with the current file by default.
ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
define dep_autopatch_erlang_mk
	echo "include $(ERLANG_MK_FILENAME)" > $(DEPS_DIR)/$(1)/erlang.mk
endef
else
define dep_autopatch_erlang_mk
	echo -n
endef
endif

define dep_autopatch_gen
	printf "%s\n" \
		"ERLC_OPTS = +debug_info" \
		"include ../../erlang.mk" > $(DEPS_DIR)/$(1)/Makefile
endef

define dep_autopatch_fetch_rebar
	mkdir -p $(ERLANG_MK_TMP); \
	if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
		git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
		cd $(ERLANG_MK_TMP)/rebar; \
		git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
		$(MAKE); \
		cd -; \
	fi
endef

define dep_autopatch_rebar
	if [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
		mv $(DEPS_DIR)/$(1)/Makefile $(DEPS_DIR)/$(1)/Makefile.orig.mk; \
	fi; \
	$(call erlang,$(call dep_autopatch_rebar.erl,$(1))); \
	rm -f $(DEPS_DIR)/$(1)/ebin/$(1).app
endef

define dep_autopatch_rebar.erl
	application:set_env(rebar, log_level, debug),
	Conf1 = case file:consult("$(DEPS_DIR)/$(1)/rebar.config") of
		{ok, Conf0} -> Conf0;
		_ -> []
	end,
	{Conf, OsEnv} = fun() ->
		case filelib:is_file("$(DEPS_DIR)/$(1)/rebar.config.script") of
			false -> {Conf1, []};
			true ->
				Bindings0 = erl_eval:new_bindings(),
				Bindings1 = erl_eval:add_binding('CONFIG', Conf1, Bindings0),
				Bindings = erl_eval:add_binding('SCRIPT', "$(DEPS_DIR)/$(1)/rebar.config.script", Bindings1),
				Before = os:getenv(),
				{ok, Conf2} = file:script("$(DEPS_DIR)/$(1)/rebar.config.script", Bindings),
				{Conf2, lists:foldl(fun(E, Acc) -> lists:delete(E, Acc) end, os:getenv(), Before)}
		end
	end(),
	Write = fun (Text) ->
		file:write_file("$(DEPS_DIR)/$(1)/Makefile", Text, [append])
	end,
	Escape = fun (Text) ->
		re:replace(Text, "\\\\$$$$", "\$$$$$$$$", [global, {return, list}])
	end,
	Write("IGNORE_DEPS = edown eper eunit_formatters meck node_package "
		"rebar_lock_deps_plugin rebar_vsn_plugin reltool_util\n"),
	Write("C_SRC_DIR = /path/do/not/exist\n"),
	Write("DRV_CFLAGS = -fPIC\nexport DRV_CFLAGS\n"),
	Write(["ERLANG_ARCH = ", rebar_utils:wordsize(), "\nexport ERLANG_ARCH\n"]),
	fun() ->
		Write("ERLC_OPTS = +debug_info\nexport ERLC_OPTS\n"),
		case lists:keyfind(erl_opts, 1, Conf) of
			false -> ok;
			{_, ErlOpts} ->
				lists:foreach(fun
					({d, D}) ->
						Write("ERLC_OPTS += -D" ++ atom_to_list(D) ++ "=1\n");
					({i, I}) ->
						Write(["ERLC_OPTS += -I ", I, "\n"]);
					({platform_define, Regex, D}) ->
						case rebar_utils:is_arch(Regex) of
							true -> Write("ERLC_OPTS += -D" ++ atom_to_list(D) ++ "=1\n");
							false -> ok
						end;
					({parse_transform, PT}) ->
						Write("ERLC_OPTS += +'{parse_transform, " ++ atom_to_list(PT) ++ "}'\n");
					(_) -> ok
				end, ErlOpts)
		end,
		Write("\n")
	end(),
	fun() ->
		File = case lists:keyfind(deps, 1, Conf) of
			false -> [];
			{_, Deps} ->
				[begin case case Dep of
							{N, S} when is_atom(N), is_list(S) -> {N, {hex, S}};
							{N, S} when is_tuple(S) -> {N, S};
							{N, _, S} -> {N, S};
							{N, _, S, _} -> {N, S};
							_ -> false
						end of
					false -> ok;
					{Name, Source} ->
						{Method, Repo, Commit} = case Source of
							{hex, V} -> {hex, V, undefined};
							{git, R} -> {git, R, master};
							{M, R, {branch, C}} -> {M, R, C};
							{M, R, {ref, C}} -> {M, R, C};
							{M, R, {tag, C}} -> {M, R, C};
							{M, R, C} -> {M, R, C}
						end,
						Write(io_lib:format("DEPS += ~s\ndep_~s = ~s ~s ~s~n", [Name, Name, Method, Repo, Commit]))
				end end || Dep <- Deps]
		end
	end(),
	fun() ->
		case lists:keyfind(erl_first_files, 1, Conf) of
			false -> ok;
			{_, Files} ->
				Names = [[" ", case lists:reverse(F) of
					"lre." ++ Elif -> lists:reverse(Elif);
					Elif -> lists:reverse(Elif)
				end] || "src/" ++ F <- Files],
				Write(io_lib:format("COMPILE_FIRST +=~s\n", [Names]))
		end
	end(),
	FindFirst = fun(F, Fd) ->
		case io:parse_erl_form(Fd, undefined) of
			{ok, {attribute, _, compile, {parse_transform, PT}}, _} ->
				[PT, F(F, Fd)];
			{ok, {attribute, _, compile, CompileOpts}, _} when is_list(CompileOpts) ->
				case proplists:get_value(parse_transform, CompileOpts) of
					undefined -> [F(F, Fd)];
					PT -> [PT, F(F, Fd)]
				end;
			{ok, {attribute, _, include, Hrl}, _} ->
				case file:open("$(DEPS_DIR)/$(1)/include/" ++ Hrl, [read]) of
					{ok, HrlFd} -> [F(F, HrlFd), F(F, Fd)];
					_ ->
						case file:open("$(DEPS_DIR)/$(1)/src/" ++ Hrl, [read]) of
							{ok, HrlFd} -> [F(F, HrlFd), F(F, Fd)];
							_ -> [F(F, Fd)]
						end
				end;
			{ok, {attribute, _, include_lib, "$(1)/include/" ++ Hrl}, _} ->
				{ok, HrlFd} = file:open("$(DEPS_DIR)/$(1)/include/" ++ Hrl, [read]),
				[F(F, HrlFd), F(F, Fd)];
			{ok, {attribute, _, include_lib, Hrl}, _} ->
				case file:open("$(DEPS_DIR)/$(1)/include/" ++ Hrl, [read]) of
					{ok, HrlFd} -> [F(F, HrlFd), F(F, Fd)];
					_ -> [F(F, Fd)]
				end;
			{ok, {attribute, _, import, {Imp, _}}, _} ->
				case file:open("$(DEPS_DIR)/$(1)/src/" ++ atom_to_list(Imp) ++ ".erl", [read]) of
					{ok, ImpFd} -> [Imp, F(F, ImpFd), F(F, Fd)];
					_ -> [F(F, Fd)]
				end;
			{eof, _} ->
				file:close(Fd),
				[];
			_ ->
				F(F, Fd)
		end
	end,
	fun() ->
		ErlFiles = filelib:wildcard("$(DEPS_DIR)/$(1)/src/*.erl"),
		First0 = lists:usort(lists:flatten([begin
			{ok, Fd} = file:open(F, [read]),
			FindFirst(FindFirst, Fd)
		end || F <- ErlFiles])),
		First = lists:flatten([begin
			{ok, Fd} = file:open("$(DEPS_DIR)/$(1)/src/" ++ atom_to_list(M) ++ ".erl", [read]),
			FindFirst(FindFirst, Fd)
		end || M <- First0, lists:member("$(DEPS_DIR)/$(1)/src/" ++ atom_to_list(M) ++ ".erl", ErlFiles)]) ++ First0,
		Write(["COMPILE_FIRST +=", [[" ", atom_to_list(M)] || M <- First,
			lists:member("$(DEPS_DIR)/$(1)/src/" ++ atom_to_list(M) ++ ".erl", ErlFiles)], "\n"])
	end(),
	Write("\n\nrebar_dep: preprocess pre-deps deps pre-app app\n"),
	Write("\npreprocess::\n"),
	Write("\npre-deps::\n"),
	Write("\npre-app::\n"),
	PatchHook = fun(Cmd) ->
		case Cmd of
			"make -C" ++ Cmd1 -> "$$$$\(MAKE) -C" ++ Escape(Cmd1);
			"gmake -C" ++ Cmd1 -> "$$$$\(MAKE) -C" ++ Escape(Cmd1);
			"make " ++ Cmd1 -> "$$$$\(MAKE) -f Makefile.orig.mk " ++ Escape(Cmd1);
			"gmake " ++ Cmd1 -> "$$$$\(MAKE) -f Makefile.orig.mk " ++ Escape(Cmd1);
			_ -> Escape(Cmd)
		end
	end,
	fun() ->
		case lists:keyfind(pre_hooks, 1, Conf) of
			false -> ok;
			{_, Hooks} ->
				[case H of
					{'get-deps', Cmd} ->
						Write("\npre-deps::\n\t" ++ PatchHook(Cmd) ++ "\n");
					{compile, Cmd} ->
						Write("\npre-app::\n\tCC=$$$$\(CC) " ++ PatchHook(Cmd) ++ "\n");
					{Regex, compile, Cmd} ->
						case rebar_utils:is_arch(Regex) of
							true -> Write("\npre-app::\n\tCC=$$$$\(CC) " ++ PatchHook(Cmd) ++ "\n");
							false -> ok
						end;
					_ -> ok
				end || H <- Hooks]
		end
	end(),
	ShellToMk = fun(V) ->
		re:replace(re:replace(V, "(\\\\$$$$)(\\\\w*)", "\\\\1(\\\\2)", [global]),
			"-Werror\\\\b", "", [{return, list}, global])
	end,
	PortSpecs = fun() ->
		case lists:keyfind(port_specs, 1, Conf) of
			false ->
				case filelib:is_dir("$(DEPS_DIR)/$(1)/c_src") of
					false -> [];
					true ->
						[{"priv/" ++ proplists:get_value(so_name, Conf, "$(1)_drv.so"),
							proplists:get_value(port_sources, Conf, ["c_src/*.c"]), []}]
				end;
			{_, Specs} ->
				lists:flatten([case S of
					{Output, Input} -> {ShellToMk(Output), Input, []};
					{Regex, Output, Input} ->
						case rebar_utils:is_arch(Regex) of
							true -> {ShellToMk(Output), Input, []};
							false -> []
						end;
					{Regex, Output, Input, [{env, Env}]} ->
						case rebar_utils:is_arch(Regex) of
							true -> {ShellToMk(Output), Input, Env};
							false -> []
						end
				end || S <- Specs])
		end
	end(),
	PortSpecWrite = fun (Text) ->
		file:write_file("$(DEPS_DIR)/$(1)/c_src/Makefile.erlang.mk", Text, [append])
	end,
	case PortSpecs of
		[] -> ok;
		_ ->
			Write("\npre-app::\n\t$$$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
			PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I ~s/erts-~s/include -I ~s\n",
				[code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
			PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L ~s -lerl_interface -lei\n",
				[code:lib_dir(erl_interface, lib)])),
			[PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
			FilterEnv = fun(Env) ->
				lists:flatten([case E of
					{_, _} -> E;
					{Regex, K, V} ->
						case rebar_utils:is_arch(Regex) of
							true -> {K, V};
							false -> []
						end
				end || E <- Env])
			end,
			MergeEnv = fun(Env) ->
				lists:foldl(fun ({K, V}, Acc) ->
					case lists:keyfind(K, 1, Acc) of
						false -> [{K, rebar_utils:expand_env_variable(V, K, "")}|Acc];
						{_, V0} -> [{K, rebar_utils:expand_env_variable(V, K, V0)}|Acc]
					end
				end, [], Env)
			end,
			PortEnv = case lists:keyfind(port_env, 1, Conf) of
				false -> [];
				{_, PortEnv0} -> FilterEnv(PortEnv0)
			end,
			PortSpec = fun ({Output, Input0, Env}) ->
				filelib:ensure_dir("$(DEPS_DIR)/$(1)/" ++ Output),
				Input = [[" ", I] || I <- Input0],
				PortSpecWrite([
					[["\n", K, " = ", ShellToMk(V)] || {K, V} <- lists:reverse(MergeEnv(PortEnv))],
					case $(PLATFORM) of
						darwin -> "\n\nLDFLAGS += -flat_namespace -undefined suppress";
						_ -> ""
					end,
					"\n\nall:: ", Output, "\n\n",
					"%.o: %.c\n\t$$$$\(CC) -c -o $$$$\@ $$$$\< $$$$\(CFLAGS) $$$$\(ERL_CFLAGS) $$$$\(DRV_CFLAGS) $$$$\(EXE_CFLAGS)\n\n",
					"%.o: %.C\n\t$$$$\(CXX) -c -o $$$$\@ $$$$\< $$$$\(CXXFLAGS) $$$$\(ERL_CFLAGS) $$$$\(DRV_CFLAGS) $$$$\(EXE_CFLAGS)\n\n",
					"%.o: %.cc\n\t$$$$\(CXX) -c -o $$$$\@ $$$$\< $$$$\(CXXFLAGS) $$$$\(ERL_CFLAGS) $$$$\(DRV_CFLAGS) $$$$\(EXE_CFLAGS)\n\n",
					"%.o: %.cpp\n\t$$$$\(CXX) -c -o $$$$\@ $$$$\< $$$$\(CXXFLAGS) $$$$\(ERL_CFLAGS) $$$$\(DRV_CFLAGS) $$$$\(EXE_CFLAGS)\n\n",
					[[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
					Output, ": $$$$\(foreach ext,.c .C .cc .cpp,",
						"$$$$\(patsubst %$$$$\(ext),%.o,$$$$\(filter %$$$$\(ext),$$$$\(wildcard", Input, "))))\n",
					"\t$$$$\(CC) -o $$$$\@ $$$$\? $$$$\(LDFLAGS) $$$$\(ERL_LDFLAGS) $$$$\(DRV_LDFLAGS) $$$$\(EXE_LDFLAGS)",
					case filename:extension(Output) of
						[] -> "\n";
						_ -> " -shared\n"
					end])
			end,
			[PortSpec(S) || S <- PortSpecs]
	end,
	Write("\ninclude $(ERLANG_MK_FILENAME)"),
	RunPlugin = fun(Plugin, Step) ->
		case erlang:function_exported(Plugin, Step, 2) of
			false -> ok;
			true ->
				c:cd("$(DEPS_DIR)/$(1)/"),
				Ret = Plugin:Step({config, "", Conf, dict:new(), dict:new(), dict:new(),
					dict:store(base_dir, "", dict:new())}, undefined),
				io:format("rebar plugin ~p step ~p ret ~p~n", [Plugin, Step, Ret])
		end
	end,
	fun() ->
		case lists:keyfind(plugins, 1, Conf) of
			false -> ok;
			{_, Plugins} ->
				[begin
					case lists:keyfind(deps, 1, Conf) of
						false -> ok;
						{_, Deps} ->
							case lists:keyfind(P, 1, Deps) of
								false -> ok;
								_ ->
									Path = "$(DEPS_DIR)/" ++ atom_to_list(P),
									io:format("~s", [os:cmd("$(MAKE) -C $(DEPS_DIR)/$(1) " ++ Path)]),
									io:format("~s", [os:cmd("$(MAKE) -C " ++ Path ++ " IS_DEP=1")]),
									code:add_patha(Path ++ "/ebin")
							end
					end
				end || P <- Plugins],
				[case code:load_file(P) of
					{module, P} -> ok;
					_ ->
						case lists:keyfind(plugin_dir, 1, Conf) of
							false -> ok;
							{_, PluginsDir} ->
								ErlFile = "$(DEPS_DIR)/$(1)/" ++ PluginsDir ++ "/" ++ atom_to_list(P) ++ ".erl",
								{ok, P, Bin} = compile:file(ErlFile, [binary]),
								{module, P} = code:load_binary(P, ErlFile, Bin)
						end
				end || P <- Plugins],
				[RunPlugin(P, preprocess) || P <- Plugins],
				[RunPlugin(P, pre_compile) || P <- Plugins]
		end
	end(),
	halt()
endef

define dep_autopatch_app.erl
	UpdateModules = fun(App) ->
		case filelib:is_regular(App) of
			false -> ok;
			true ->
				{ok, [{application, $(1), L0}]} = file:consult(App),
				Mods = filelib:fold_files("$(DEPS_DIR)/$(1)/src", "\\\\.erl$$$$", true,
					fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
				L = lists:keystore(modules, 1, L0, {modules, Mods}),
				ok = file:write_file(App, io_lib:format("~p.~n", [{application, $(1), L}]))
		end
	end,
	UpdateModules("$(DEPS_DIR)/$(1)/ebin/$(1).app"),
	halt()
endef

define dep_autopatch_appsrc.erl
	AppSrcOut = "$(DEPS_DIR)/$(1)/src/$(1).app.src",
	AppSrcIn = case filelib:is_regular(AppSrcOut) of false -> "$(DEPS_DIR)/$(1)/ebin/$(1).app"; true -> AppSrcOut end,
	case filelib:is_regular(AppSrcIn) of
		false -> ok;
		true ->
			{ok, [{application, $(1), L0}]} = file:consult(AppSrcIn),
			L1 = lists:keystore(modules, 1, L0, {modules, []}),
			L2 = case lists:keyfind(vsn, 1, L1) of {_, git} -> lists:keyreplace(vsn, 1, L1, {vsn, "git"}); _ -> L1 end,
			L3 = case lists:keyfind(registered, 1, L2) of false -> [{registered, []}|L2]; _ -> L2 end,
			ok = file:write_file(AppSrcOut, io_lib:format("~p.~n", [{application, $(1), L3}])),
			case AppSrcOut of AppSrcIn -> ok; _ -> ok = file:delete(AppSrcIn) end
	end,
	halt()
endef

define dep_fetch_git
	git clone -q -n -- $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1)); \
	cd $(DEPS_DIR)/$(call dep_name,$(1)) && git checkout -q $(call dep_commit,$(1));
endef

define dep_fetch_hg
	hg clone -q -U $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1)); \
	cd $(DEPS_DIR)/$(call dep_name,$(1)) && hg update -q $(call dep_commit,$(1));
endef

define dep_fetch_svn
	svn checkout -q $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
endef

define dep_fetch_cp
	cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
endef

define dep_fetch_hex.erl
	ssl:start(),
	inets:start(),
	{ok, {{_, 200, _}, _, Body}} = httpc:request(get,
		{"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
		[], [{body_format, binary}]),
	{ok, Files} = erl_tar:extract({binary, Body}, [memory]),
	{_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
	ok = erl_tar:extract({binary, Source}, [{cwd, "$(DEPS_DIR)/$(1)"}, compressed]),
	halt()
endef

# Hex only has a package version. No need to look in the Erlang.mk packages.
define dep_fetch_hex
	$(call erlang,$(call dep_fetch_hex.erl,$(1),$(strip $(word 2,$(dep_$(1))))));
endef

define dep_fetch_fail
	echo "Unknown or invalid dependency: $(1). Please consult the erlang.mk README for instructions." >&2; \
	exit 78;
endef

# Kept for compatibility purposes with older Erlang.mk configuration.
define dep_fetch_legacy
	$(warning WARNING: '$(1)' dependency configuration uses deprecated format.) \
	git clone -q -n -- $(word 1,$(dep_$(1))) $(DEPS_DIR)/$(1); \
	cd $(DEPS_DIR)/$(1) && git checkout -q $(if $(word 2,$(dep_$(1))),$(word 2,$(dep_$(1))),master);
endef

define dep_fetch
	$(if $(dep_$(1)), \
		$(if $(dep_fetch_$(word 1,$(dep_$(1)))), \
			$(word 1,$(dep_$(1))), \
			legacy), \
		$(if $(filter $(1),$(PACKAGES)), \
			$(pkg_$(1)_fetch), \
			fail))
endef

dep_name = $(if $(dep_$(1)),$(1),$(pkg_$(1)_name))
dep_repo = $(patsubst git://github.com/%,https://github.com/%, \
	$(if $(dep_$(1)),$(word 2,$(dep_$(1))),$(pkg_$(1)_repo)))
dep_commit = $(if $(dep_$(1)),$(word 3,$(dep_$(1))),$(pkg_$(1)_commit))

define dep_target
$(DEPS_DIR)/$(1):
	$(verbose) mkdir -p $(DEPS_DIR)
	$(dep_verbose) $(call dep_fetch_$(strip $(call dep_fetch,$(1))),$(1))
	$(verbose) if [ -f $(DEPS_DIR)/$(1)/configure.ac -o -f $(DEPS_DIR)/$(1)/configure.in ]; then \
		echo " AUTO  " $(1); \
		cd $(DEPS_DIR)/$(1) && autoreconf -Wall -vif -I m4; \
	fi
	- $(verbose) if [ -f $(DEPS_DIR)/$(1)/configure ]; then \
		echo " CONF  " $(1); \
		cd $(DEPS_DIR)/$(1) && ./configure; \
	fi
ifeq ($(filter $(1),$(NO_AUTOPATCH)),)
	$(verbose) if [ "$(1)" = "amqp_client" -a "$(RABBITMQ_CLIENT_PATCH)" ]; then \
		if [ ! -d $(DEPS_DIR)/rabbitmq-codegen ]; then \
			echo " PATCH  Downloading rabbitmq-codegen"; \
			git clone https://github.com/rabbitmq/rabbitmq-codegen.git $(DEPS_DIR)/rabbitmq-codegen; \
		fi; \
		if [ ! -d $(DEPS_DIR)/rabbitmq-server ]; then \
			echo " PATCH  Downloading rabbitmq-server"; \
			git clone https://github.com/rabbitmq/rabbitmq-server.git $(DEPS_DIR)/rabbitmq-server; \
		fi; \
		ln -s $(DEPS_DIR)/amqp_client/deps/rabbit_common-0.0.0 $(DEPS_DIR)/rabbit_common; \
	elif [ "$(1)" = "rabbit" -a "$(RABBITMQ_SERVER_PATCH)" ]; then \
		if [ ! -d $(DEPS_DIR)/rabbitmq-codegen ]; then \
			echo " PATCH  Downloading rabbitmq-codegen"; \
			git clone https://github.com/rabbitmq/rabbitmq-codegen.git $(DEPS_DIR)/rabbitmq-codegen; \
		fi \
	else \
		$(call dep_autopatch,$(1)) \
	fi
endif
endef

$(foreach dep,$(DEPS),$(eval $(call dep_target,$(dep))))

distclean-deps:
	$(gen_verbose) rm -rf $(DEPS_DIR)

# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

# Verbosity.

proto_verbose_0 = @echo " PROTO " $(filter %.proto,$(?F));
proto_verbose = $(proto_verbose_$(V))

# Core targets.

define compile_proto
	$(verbose) mkdir -p ebin/ include/
	$(proto_verbose) $(call erlang,$(call compile_proto.erl,$(1)))
	$(proto_verbose) erlc +debug_info -o ebin/ ebin/*.erl
	$(verbose) rm ebin/*.erl
endef

define compile_proto.erl
	[begin
		Dir = filename:dirname(filename:dirname(F)),
		protobuffs_compile:generate_source(F,
			[{output_include_dir, Dir ++ "/include"},
				{output_src_dir, Dir ++ "/ebin"}])
	end || F <- string:tokens("$(1)", " ")],
	halt().
endef

ifneq ($(wildcard src/),)
ebin/$(PROJECT).app:: $(sort $(call core_find,src/,*.proto))
	$(if $(strip $?),$(call compile_proto,$?))
endif

# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: clean-app

# Configuration.

ERLC_OPTS ?= -Werror +debug_info +warn_export_vars +warn_shadow_vars \
	+warn_obsolete_guard # +bin_opt_info +warn_export_all +warn_missing_spec
COMPILE_FIRST ?=
COMPILE_FIRST_PATHS = $(addprefix src/,$(addsuffix .erl,$(COMPILE_FIRST)))
ERLC_EXCLUDE ?=
ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))

ERLC_MIB_OPTS ?=
COMPILE_MIB_FIRST ?=
COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))

# Verbosity.

app_verbose_0 = @echo " APP   " $(PROJECT);
app_verbose = $(app_verbose_$(V))

appsrc_verbose_0 = @echo " APP   " $(PROJECT).app.src;
appsrc_verbose = $(appsrc_verbose_$(V))

erlc_verbose_0 = @echo " ERLC  " $(filter-out $(patsubst %,%.erl,$(ERLC_EXCLUDE)),\
	$(filter %.erl %.core,$(?F)));
erlc_verbose = $(erlc_verbose_$(V))

xyrl_verbose_0 = @echo " XYRL  " $(filter %.xrl %.yrl,$(?F));
xyrl_verbose = $(xyrl_verbose_$(V))

asn1_verbose_0 = @echo " ASN1  " $(filter %.asn1,$(?F));
asn1_verbose = $(asn1_verbose_$(V))

mib_verbose_0 = @echo " MIB   " $(filter %.bin %.mib,$(?F));
mib_verbose = $(mib_verbose_$(V))

# Targets.

ifeq ($(wildcard ebin/test),)
app:: app-build
else
app:: clean app-build
endif

ifeq ($(wildcard src/$(PROJECT)_app.erl),)
define app_file
{application, $(PROJECT), [
	{description, "$(PROJECT_DESCRIPTION)"},
	{vsn, "$(PROJECT_VERSION)"},
	{id, "$(1)"},
	{modules, [$(call comma_list,$(2))]},
	{registered, []},
	{applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(DEPS))]}
]}.
endef
else
define app_file
{application, $(PROJECT), [
	{description, "$(PROJECT_DESCRIPTION)"},
	{vsn, "$(PROJECT_VERSION)"},
	{id, "$(1)"},
	{modules, [$(call comma_list,$(2))]},
	{registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
	{applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(DEPS))]},
	{mod, {$(PROJECT)_app, []}}
]}.
endef
endif

app-build: erlc-include ebin/$(PROJECT).app
	$(eval GITDESCRIBE := $(shell git describe --dirty --abbrev=7 --tags --always --first-parent 2>/dev/null || true))
	$(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename $(shell find ebin -type f -name *.beam))))))
ifeq ($(wildcard src/$(PROJECT).app.src),)
	$(app_verbose) echo $(subst $(newline),,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES)))) \
		> ebin/$(PROJECT).app
else
	$(verbose) if [ -z "$$(grep -E '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
		echo "Empty modules entry not found in $(PROJECT).app.src. Please consult the erlang.mk README for instructions." >&2; \
		exit 1; \
	fi
	$(appsrc_verbose) cat src/$(PROJECT).app.src \
		| sed "s/{[[:space:]]*modules[[:space:]]*,[[:space:]]*\[\]}/{modules, \[$(call comma_list,$(MODULES))\]}/" \
		| sed "s/{id,[[:space:]]*\"git\"}/{id, \"$(GITDESCRIBE)\"}/" \
		> ebin/$(PROJECT).app
endif

erlc-include:
	- $(verbose) if [ -d ebin/ ]; then \
		find include/ src/ -type f -name \*.hrl -newer ebin -exec touch $(shell find src/ -type f -name "*.erl") \; 2>/dev/null || printf ''; \
	fi

define compile_erl
	$(erlc_verbose) erlc -v $(if $(IS_DEP),$(filter-out -Werror,$(ERLC_OPTS)),$(ERLC_OPTS)) -o ebin/ \
		-pa ebin/ -I include/ $(filter-out $(ERLC_EXCLUDE_PATHS),\
		$(COMPILE_FIRST_PATHS) $(1))
endef

define compile_xyrl
	$(xyrl_verbose) erlc -v -o ebin/ $(1)
	$(xyrl_verbose) erlc $(ERLC_OPTS) -o ebin/ ebin/*.erl
	$(verbose) rm ebin/*.erl
endef

define compile_asn1
	$(asn1_verbose) erlc -v -I include/ -o ebin/ $(1)
	$(verbose) mv ebin/*.hrl include/
	$(verbose) mv ebin/*.asn1db include/
	$(verbose) rm ebin/*.erl
endef

define compile_mib
	$(mib_verbose) erlc -v $(ERLC_MIB_OPTS) -o priv/mibs/ \
		-I priv/mibs/ $(COMPILE_MIB_FIRST_PATHS) $(1)
	$(mib_verbose) erlc -o include/ -- priv/mibs/*.bin
endef

ifneq ($(wildcard src/),)
ebin/$(PROJECT).app::
	$(verbose) mkdir -p ebin/

ifneq ($(wildcard asn1/),)
ebin/$(PROJECT).app:: $(sort $(call core_find,asn1/,*.asn1))
	$(verbose) mkdir -p include
	$(if $(strip $?),$(call compile_asn1,$?))
endif

ifneq ($(wildcard mibs/),)
ebin/$(PROJECT).app:: $(sort $(call core_find,mibs/,*.mib))
	$(verbose) mkdir -p priv/mibs/ include
	$(if $(strip $?),$(call compile_mib,$?))
endif

ebin/$(PROJECT).app:: $(sort $(call core_find,src/,*.erl *.core))
	$(if $(strip $?),$(call compile_erl,$?))

ebin/$(PROJECT).app:: $(sort $(call core_find,src/,*.xrl *.yrl))
	$(if $(strip $?),$(call compile_xyrl,$?))
endif

clean:: clean-app

clean-app:
	$(gen_verbose) rm -rf ebin/ priv/mibs/ \
		$(addprefix include/,$(addsuffix .hrl,$(notdir $(basename $(call core_find,mibs/,*.mib)))))

# Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: docs-deps

# Configuration.

ALL_DOC_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(DOC_DEPS))

# Targets.

$(foreach dep,$(DOC_DEPS),$(eval $(call dep_target,$(dep))))

ifneq ($(SKIP_DEPS),)
doc-deps:
else
doc-deps: $(ALL_DOC_DEPS_DIRS)
	$(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
endif

# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: test-deps test-dir test-build clean-test-dir

# Configuration.

TEST_DIR ?= $(CURDIR)/test

ALL_TEST_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(TEST_DEPS))

TEST_ERLC_OPTS ?= +debug_info +warn_export_vars +warn_shadow_vars +warn_obsolete_guard
TEST_ERLC_OPTS += -DTEST=1

# Targets.

$(foreach dep,$(TEST_DEPS),$(eval $(call dep_target,$(dep))))

ifneq ($(SKIP_DEPS),)
test-deps:
else
test-deps: $(ALL_TEST_DEPS_DIRS)
	$(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
endif

ifneq ($(wildcard $(TEST_DIR)),)
test-dir:
	$(gen_verbose) erlc -v $(TEST_ERLC_OPTS) -I include/ -o $(TEST_DIR) \
		$(call core_find,$(TEST_DIR)/,*.erl) -pa ebin/
endif

ifeq ($(wildcard ebin/test),)
test-build:: ERLC_OPTS=$(TEST_ERLC_OPTS)
test-build:: clean deps test-deps
	$(verbose) $(MAKE) --no-print-directory app-build test-dir ERLC_OPTS="$(TEST_ERLC_OPTS)"
	$(gen_verbose) touch ebin/test
else
test-build:: ERLC_OPTS=$(TEST_ERLC_OPTS)
test-build:: deps test-deps
	$(verbose) $(MAKE) --no-print-directory app-build test-dir ERLC_OPTS="$(TEST_ERLC_OPTS)"
endif

clean:: clean-test-dir

clean-test-dir:
ifneq ($(wildcard $(TEST_DIR)/*.beam),)
	$(gen_verbose) rm -f $(TEST_DIR)/*.beam
endif

# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc

MAN_INSTALL_PATH ?= /usr/local/share/man
MAN_SECTIONS ?= 3 7

docs:: asciidoc

asciidoc: distclean-asciidoc doc-deps asciidoc-guide asciidoc-manual

ifeq ($(wildcard doc/src/guide/book.asciidoc),)
asciidoc-guide:
else
asciidoc-guide:
	a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
	a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
endif

ifeq ($(wildcard doc/src/manual/*.asciidoc),)
asciidoc-manual:
else
asciidoc-manual:
	for f in doc/src/manual/*.asciidoc ; do \
		a2x -v -f manpage $$f ; \
	done
	for s in $(MAN_SECTIONS); do \
		mkdir -p doc/man$$s/ ; \
		mv doc/src/manual/*.$$s doc/man$$s/ ; \
		gzip doc/man$$s/*.$$s ; \
	done

install-docs:: install-asciidoc

install-asciidoc: asciidoc-manual
	for s in $(MAN_SECTIONS); do \
		mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
		install -g 0 -o 0 -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
	done
endif

distclean:: distclean-asciidoc

distclean-asciidoc:
	$(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/

# Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates

# Core targets.

help::
	$(verbose) printf "%s\n" "" \
		"Bootstrap targets:" \
		"  bootstrap          Generate a skeleton of an OTP application" \
		"  bootstrap-lib      Generate a skeleton of an OTP library" \
		"  bootstrap-rel      Generate the files needed to build a release" \
		"  new t=TPL n=NAME   Generate a module NAME based on the template TPL" \
		"  list-templates     List available templates"

# Bootstrap templates.

define bs_appsrc
{application, $(PROJECT), [
	{description, ""},
	{vsn, "0.1.0"},
	{id, "git"},
	{modules, []},
	{registered, []},
	{applications, [
		kernel,
		stdlib
	]},
	{mod, {$(PROJECT)_app, []}},
	{env, []}
]}.
endef

define bs_appsrc_lib
{application, $(PROJECT), [
	{description, ""},
	{vsn, "0.1.0"},
	{id, "git"},
	{modules, []},
	{registered, []},
	{applications, [
		kernel,
		stdlib
	]}
]}.
endef

ifdef SP
define bs_Makefile
PROJECT = $(PROJECT)

# Whitespace to be used when creating files from templates.
SP = $(SP)

include erlang.mk
endef
else
define bs_Makefile
PROJECT = $(PROJECT)
include erlang.mk
endef
endif

define bs_app
-module($(PROJECT)_app).
-behaviour(application).

-export([start/2]).
-export([stop/1]).

start(_Type, _Args) ->
	$(PROJECT)_sup:start_link().

stop(_State) ->
	ok.
endef

define bs_relx_config
{release, {$(PROJECT)_release, "1"}, [$(PROJECT)]}.
{extended_start_script, true}.
{sys_config, "rel/sys.config"}.
{vm_args, "rel/vm.args"}.
endef

define bs_sys_config
[
].
endef

define bs_vm_args
-name $(PROJECT)@127.0.0.1
-setcookie $(PROJECT)
-heart
endef

# Normal templates.

define tpl_supervisor
-module($(n)).
-behaviour(supervisor).

-export([start_link/0]).
-export([init/1]).

start_link() ->
	supervisor:start_link({local, ?MODULE}, ?MODULE, []).

init([]) ->
	Procs = [],
	{ok, {{one_for_one, 1, 5}, Procs}}.
endef

define tpl_gen_server
-module($(n)).
-behaviour(gen_server).

%% API.
-export([start_link/0]).

%% gen_server.
-export([init/1]).
-export([handle_call/3]).
-export([handle_cast/2]).
-export([handle_info/2]).
-export([terminate/2]).
-export([code_change/3]).

-record(state, {
}).

%% API.

-spec start_link() -> {ok, pid()}.
start_link() ->
	gen_server:start_link(?MODULE, [], []).

%% gen_server.

init([]) ->
	{ok, #state{}}.

handle_call(_Request, _From, State) ->
	{reply, ignored, State}.

handle_cast(_Msg, State) ->
	{noreply, State}.

handle_info(_Info, State) ->
	{noreply, State}.

terminate(_Reason, _State) ->
	ok.

code_change(_OldVsn, State, _Extra) ->
	{ok, State}.
endef

define tpl_cowboy_http
-module($(n)).
-behaviour(cowboy_http_handler).

-export([init/3]).
-export([handle/2]).
-export([terminate/3]).

-record(state, {
}).

init(_, Req, _Opts) ->
	{ok, Req, #state{}}.

handle(Req, State=#state{}) ->
	{ok, Req2} = cowboy_req:reply(200, Req),
	{ok, Req2, State}.

terminate(_Reason, _Req, _State) ->
	ok.
endef

define tpl_gen_fsm
-module($(n)).
-behaviour(gen_fsm).

%% API.
-export([start_link/0]).

%% gen_fsm.
-export([init/1]).
-export([state_name/2]).
-export([handle_event/3]).
-export([state_name/3]).
-export([handle_sync_event/4]).
-export([handle_info/3]).
-export([terminate/3]).
-export([code_change/4]).

-record(state, {
}).

%% API.

-spec start_link() -> {ok, pid()}.
start_link() ->
	gen_fsm:start_link(?MODULE, [], []).

%% gen_fsm.

init([]) ->
	{ok, state_name, #state{}}.

state_name(_Event, StateData) ->
	{next_state, state_name, StateData}.

handle_event(_Event, StateName, StateData) ->
	{next_state, StateName, StateData}.

state_name(_Event, _From, StateData) ->
	{reply, ignored, state_name, StateData}.

handle_sync_event(_Event, _From, StateName, StateData) ->
	{reply, ignored, StateName, StateData}.

handle_info(_Info, StateName, StateData) ->
	{next_state, StateName, StateData}.

terminate(_Reason, _StateName, _StateData) ->
	ok.

code_change(_OldVsn, StateName, StateData, _Extra) ->
	{ok, StateName, StateData}.
endef

define tpl_cowboy_loop
-module($(n)).
-behaviour(cowboy_loop_handler).

-export([init/3]).
-export([info/3]).
-export([terminate/3]).

-record(state, {
}).

init(_, Req, _Opts) ->
	{loop, Req, #state{}, 5000, hibernate}.

info(_Info, Req, State) ->
	{loop, Req, State, hibernate}.

terminate(_Reason, _Req, _State) ->
	ok.
endef

define tpl_cowboy_rest
-module($(n)).

-export([init/3]).
-export([content_types_provided/2]).
-export([get_html/2]).

init(_, _Req, _Opts) ->
	{upgrade, protocol, cowboy_rest}.

content_types_provided(Req, State) ->
	{[{{<<"text">>, <<"html">>, '*'}, get_html}], Req, State}.

get_html(Req, State) ->
	{<<"<html><body>This is REST!</body></html>">>, Req, State}.
endef

define tpl_cowboy_ws
-module($(n)).
-behaviour(cowboy_websocket_handler).

-export([init/3]).
-export([websocket_init/3]).
-export([websocket_handle/3]).
-export([websocket_info/3]).
-export([websocket_terminate/3]).

-record(state, {
}).

init(_, _, _) ->
	{upgrade, protocol, cowboy_websocket}.

websocket_init(_, Req, _Opts) ->
	Req2 = cowboy_req:compact(Req),
	{ok, Req2, #state{}}.

websocket_handle({text, Data}, Req, State) ->
	{reply, {text, Data}, Req, State};
websocket_handle({binary, Data}, Req, State) ->
	{reply, {binary, Data}, Req, State};
websocket_handle(_Frame, Req, State) ->
	{ok, Req, State}.

websocket_info(_Info, Req, State) ->
	{ok, Req, State}.

websocket_terminate(_Reason, _Req, _State) ->
	ok.
endef

define tpl_ranch_protocol
-module($(n)).
-behaviour(ranch_protocol).

-export([start_link/4]).
-export([init/4]).

-type opts() :: [].
-export_type([opts/0]).

-record(state, {
	socket :: inet:socket(),
	transport :: module()
}).

start_link(Ref, Socket, Transport, Opts) ->
	Pid = spawn_link(?MODULE, init, [Ref, Socket, Transport, Opts]),
	{ok, Pid}.

-spec init(ranch:ref(), inet:socket(), module(), opts()) -> ok.
init(Ref, Socket, Transport, _Opts) ->
	ok = ranch:accept_ack(Ref),
	loop(#state{socket=Socket, transport=Transport}).

loop(State) ->
	loop(State).
endef

# Plugin-specific targets.

define render_template
	$(verbose) echo "$${_$(1)}" > $(2)
endef

ifndef WS
ifdef SP
WS = $(subst a,,a $(wordlist 1,$(SP),a a a a a a a a a a a a a a a a a a a a))
else
WS = $(tab)
endif
endif

$(foreach template,$(filter bs_% tpl_%,$(.VARIABLES)), \
	$(eval _$(template) = $$(subst $$(tab),$$(WS),$$($(template)))) \
	$(eval export _$(template)))

bootstrap:
ifneq ($(wildcard src/),)
	$(error Error: src/ directory already exists)
endif
	$(call render_template,bs_Makefile,Makefile)
	$(verbose) mkdir src/
	$(call render_template,bs_appsrc,src/$(PROJECT).app.src)
	$(call render_template,bs_app,src/$(PROJECT)_app.erl)
	$(eval n := $(PROJECT)_sup)
	$(call render_template,tpl_supervisor,src/$(PROJECT)_sup.erl)

bootstrap-lib:
ifneq ($(wildcard src/),)
	$(error Error: src/ directory already exists)
endif
	$(call render_template,bs_Makefile,Makefile)
	$(verbose) mkdir src/
	$(call render_template,bs_appsrc_lib,src/$(PROJECT).app.src)

bootstrap-rel:
ifneq ($(wildcard relx.config),)
	$(error Error: relx.config already exists)
endif
ifneq ($(wildcard rel/),)
	$(error Error: rel/ directory already exists)
endif
	$(call render_template,bs_relx_config,relx.config)
	$(verbose) mkdir rel/
	$(call render_template,bs_sys_config,rel/sys.config)
	$(call render_template,bs_vm_args,rel/vm.args)

new:
ifeq ($(wildcard src/),)
	$(error Error: src/ directory does not exist)
endif
ifndef t
	$(error Usage: $(MAKE) new t=TEMPLATE n=NAME)
endif
ifndef tpl_$(t)
	$(error Unknown template)
endif
ifndef n
	$(error Usage: $(MAKE) new t=TEMPLATE n=NAME)
endif
	$(call render_template,tpl_$(t),src/$(n).erl)

list-templates:
	$(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))

# Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: clean-c_src distclean-c_src-env

# Configuration.

C_SRC_DIR ?= $(CURDIR)/c_src
C_SRC_ENV ?= $(C_SRC_DIR)/env.mk
C_SRC_OUTPUT ?= $(CURDIR)/priv/$(PROJECT).so
C_SRC_TYPE ?= shared

# System type and C compiler/flags.

ifeq ($(PLATFORM),darwin)
	CC ?= cc
	CFLAGS ?= -O3 -std=c99 -arch x86_64 -finline-functions -Wall -Wmissing-prototypes
	CXXFLAGS ?= -O3 -arch x86_64 -finline-functions -Wall
	LDFLAGS ?= -arch x86_64 -flat_namespace -undefined suppress
else ifeq ($(PLATFORM),freebsd)
	CC ?= cc
	CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes
	CXXFLAGS ?= -O3 -finline-functions -Wall
else ifeq ($(PLATFORM),linux)
	CC ?= gcc
	CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes
	CXXFLAGS ?= -O3 -finline-functions -Wall
endif

CFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR)
CXXFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR)

LDLIBS += -L $(ERL_INTERFACE_LIB_DIR) -lerl_interface -lei

ifeq ($(C_SRC_TYPE),shared)
LDFLAGS += -shared
endif

# Verbosity.

c_verbose_0 = @echo " C     " $(?F);
c_verbose = $(c_verbose_$(V))

cpp_verbose_0 = @echo " CPP   " $(?F);
cpp_verbose = $(cpp_verbose_$(V))

link_verbose_0 = @echo " LD    " $(@F);
link_verbose = $(link_verbose_$(V))

# Targets.

ifeq ($(wildcard $(C_SRC_DIR)),)
else ifneq ($(wildcard $(C_SRC_DIR)/Makefile),)
app:: app-c_src

test-build:: app-c_src

app-c_src:
	$(MAKE) -C $(C_SRC_DIR)

clean::
	$(MAKE) -C $(C_SRC_DIR) clean

else

ifeq ($(SOURCES),)
SOURCES := $(sort $(call core_find,$(C_SRC_DIR)/,*.c *.C *.cc *.cpp))
endif
OBJECTS = $(addsuffix .o, $(basename $(SOURCES)))

COMPILE_C = $(c_verbose) $(CC) $(CFLAGS) $(CPPFLAGS) -c
COMPILE_CPP = $(cpp_verbose) $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c

app:: $(C_SRC_ENV) $(C_SRC_OUTPUT)

test-build:: $(C_SRC_ENV) $(C_SRC_OUTPUT)

$(C_SRC_OUTPUT): $(OBJECTS)
	$(verbose) mkdir -p priv/
	$(link_verbose) $(CC) $(OBJECTS) $(LDFLAGS) $(LDLIBS) -o $(C_SRC_OUTPUT)

%.o: %.c
	$(COMPILE_C) $(OUTPUT_OPTION) $<

%.o: %.cc
	$(COMPILE_CPP) $(OUTPUT_OPTION) $<

%.o: %.C
	$(COMPILE_CPP) $(OUTPUT_OPTION) $<

%.o: %.cpp
	$(COMPILE_CPP) $(OUTPUT_OPTION) $<

clean:: clean-c_src

clean-c_src:
	$(gen_verbose) rm -f $(C_SRC_OUTPUT) $(OBJECTS)

endif

ifneq ($(wildcard $(C_SRC_DIR)),)
$(C_SRC_ENV):
	$(verbose) $(ERL) -eval "file:write_file(\"$(C_SRC_ENV)\", \
		io_lib:format( \
			\"ERTS_INCLUDE_DIR ?= ~s/erts-~s/include/~n\" \
			\"ERL_INTERFACE_INCLUDE_DIR ?= ~s~n\" \
			\"ERL_INTERFACE_LIB_DIR ?= ~s~n\", \
			[code:root_dir(), erlang:system_info(version), \
			code:lib_dir(erl_interface, include), \
			code:lib_dir(erl_interface, lib)])), \
		halt()."

distclean:: distclean-c_src-env

distclean-c_src-env:
	$(gen_verbose) rm -f $(C_SRC_ENV)

-include $(C_SRC_ENV)
endif

# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: ci ci-setup distclean-kerl

KERL ?= $(CURDIR)/kerl
export KERL

KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl

OTP_GIT ?= https://github.com/erlang/otp

CI_INSTALL_DIR ?= $(HOME)/erlang
CI_OTP ?=

ifeq ($(strip $(CI_OTP)),)
ci::
else
ci:: $(addprefix ci-,$(CI_OTP))

ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))

ci-setup::

ci_verbose_0 = @echo " CI    " $(1);
ci_verbose = $(ci_verbose_$(V))

define ci_target
ci-$(1): $(CI_INSTALL_DIR)/$(1)
	$(ci_verbose) \
		PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
		CI_OTP_RELEASE="$(1)" \
		CT_OPTS="-label $(1)" \
		$(MAKE) clean ci-setup tests
endef

$(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))

define ci_otp_target
ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
$(CI_INSTALL_DIR)/$(1): $(KERL)
	$(KERL) build git $(OTP_GIT) $(1) $(1)
	$(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
endif
endef

$(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))

$(KERL):
	$(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL))
	$(verbose) chmod +x $(KERL)

help::
	$(verbose) printf "%s\n" "" \
		"Continuous Integration targets:" \
		"  ci          Run '$(MAKE) tests' on all configured Erlang versions." \
		"" \
		"The CI_OTP variable must be defined with the Erlang versions" \
		"that must be tested. For example: CI_OTP = OTP-17.3.4 OTP-17.5.3"

distclean:: distclean-kerl

distclean-kerl:
	$(gen_verbose) rm -rf $(KERL)
endif

# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: ct distclean-ct

# Configuration.

CT_OPTS ?=
ifneq ($(wildcard $(TEST_DIR)),)
	CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
else
	CT_SUITES ?=
endif

# Core targets.

tests:: ct

distclean:: distclean-ct

help::
	$(verbose) printf "%s\n" "" \
		"Common_test targets:" \
		"  ct          Run all the common_test suites for this project" \
		"" \
		"All your common_test suites have their associated targets." \
		"A suite named http_SUITE can be ran using the ct-http target."

# Plugin-specific targets.

CT_RUN = ct_run \
	-no_auto_compile \
	-noinput \
	-pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(TEST_DIR) \
	-dir $(TEST_DIR) \
	-logdir $(CURDIR)/logs

ifeq ($(CT_SUITES),)
ct:
else
ct: test-build
	$(verbose) mkdir -p $(CURDIR)/logs/
	$(gen_verbose) $(CT_RUN) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
endif

define ct_suite_target
ct-$(1): test-build
	$(verbose) mkdir -p $(CURDIR)/logs/
	$(gen_verbose) $(CT_RUN) -suite $(addsuffix _SUITE,$(1)) $(CT_OPTS)
endef

$(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))

distclean-ct:
	$(gen_verbose) rm -rf $(CURDIR)/logs/

# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: plt distclean-plt dialyze

# Configuration.

DIALYZER_PLT ?= $(CURDIR)/.$(PROJECT).plt
export DIALYZER_PLT

PLT_APPS ?=
DIALYZER_DIRS ?= --src -r src
DIALYZER_OPTS ?= -Werror_handling -Wrace_conditions \
	-Wunmatched_returns # -Wunderspecs

# Core targets.

check:: dialyze

distclean:: distclean-plt

help::
	$(verbose) printf "%s\n" "" \
		"Dialyzer targets:" \
		"  plt         Build a PLT file for this project" \
		"  dialyze     Analyze the project using Dialyzer"

# Plugin-specific targets.

$(DIALYZER_PLT): deps app
	$(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(ALL_DEPS_DIRS)

plt: $(DIALYZER_PLT)

distclean-plt:
	$(gen_verbose) rm -f $(DIALYZER_PLT)

ifneq ($(wildcard $(DIALYZER_PLT)),)
dialyze:
else
dialyze: $(DIALYZER_PLT)
endif
	$(verbose) dialyzer --no_native $(DIALYZER_DIRS) $(DIALYZER_OPTS)

# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: distclean-edoc edoc

# Configuration.

EDOC_OPTS ?=

# Core targets.

docs:: distclean-edoc edoc

distclean:: distclean-edoc

# Plugin-specific targets.

edoc: doc-deps
	$(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'

distclean-edoc:
	$(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info

# Copyright (c) 2015, Erlang Solutions Ltd.
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: elvis distclean-elvis

# Configuration.

ELVIS_CONFIG ?= $(CURDIR)/elvis.config

ELVIS ?= $(CURDIR)/elvis
export ELVIS

ELVIS_URL ?= https://github.com/inaka/elvis/releases/download/0.2.5-beta2/elvis
ELVIS_CONFIG_URL ?= https://github.com/inaka/elvis/releases/download/0.2.5-beta2/elvis.config
ELVIS_OPTS ?=

# Core targets.

help::
	$(verbose) printf "%s\n" "" \
		"Elvis targets:" \
		"  elvis       Run Elvis using the local elvis.config or download the default otherwise"

distclean:: distclean-elvis

# Plugin-specific targets.

$(ELVIS):
	$(gen_verbose) $(call core_http_get,$(ELVIS),$(ELVIS_URL))
	$(verbose) chmod +x $(ELVIS)

$(ELVIS_CONFIG):
	$(verbose) $(call core_http_get,$(ELVIS_CONFIG),$(ELVIS_CONFIG_URL))

elvis: $(ELVIS) $(ELVIS_CONFIG)
	$(verbose) $(ELVIS) rock -c $(ELVIS_CONFIG) $(ELVIS_OPTS)

distclean-elvis:
	$(gen_verbose) rm -rf $(ELVIS)

# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

# Configuration.

DTL_FULL_PATH ?= 0
DTL_PATH ?= templates/
DTL_SUFFIX ?= _dtl

# Verbosity.

dtl_verbose_0 = @echo " DTL   " $(filter %.dtl,$(?F));
dtl_verbose = $(dtl_verbose_$(V))

# Core targets.

define erlydtl_compile.erl
	[begin
		Module0 = case $(DTL_FULL_PATH) of
			0 ->
				filename:basename(F, ".dtl");
			1 ->
				"$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"),
				re:replace(F2, "/",  "_",  [{return, list}, global])
		end,
		Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
		case erlydtl:compile(F, Module, [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of
			ok -> ok;
			{ok, _} -> ok
		end
	end || F <- string:tokens("$(1)", " ")],
	halt().
endef

ifneq ($(wildcard src/),)
ebin/$(PROJECT).app:: $(sort $(call core_find,$(DTL_PATH),*.dtl))
	$(if $(strip $?),\
		$(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?,-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/)))
endif

# Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: distclean-escript escript

# Configuration.

ESCRIPT_NAME ?= $(PROJECT)
ESCRIPT_COMMENT ?= This is an -*- erlang -*- file

ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
ESCRIPT_EMU_ARGS ?= -pa . \
	-sasl errlog_type error \
	-escript main $(ESCRIPT_NAME)
ESCRIPT_SHEBANG ?= /usr/bin/env escript
ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"

# Core targets.

distclean:: distclean-escript

help::
	$(verbose) printf "%s\n" "" \
		"Escript targets:" \
		"  escript     Build an executable escript archive" \

# Plugin-specific targets.

# Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
# Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
# Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
# Software may only be used for the great good and the true happiness of all
# sentient beings.

define ESCRIPT_RAW
'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
'  [F || F <- A, not filelib:is_dir(F) ] end,'\
'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
'Ez = fun(Escript) ->'\
'  Static = Files([$(ESCRIPT_STATIC)]),'\
'  Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
'  Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
'  escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
'    {archive, Archive, [memory]},'\
'    {shebang, "$(ESCRIPT_SHEBANG)"},'\
'    {comment, "$(ESCRIPT_COMMENT)"},'\
'    {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
'  ]),'\
'  file:change_mode(Escript, 8#755)'\
'end,'\
'Ez("$(ESCRIPT_NAME)"),'\
'halt().'
endef

ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))

escript:: distclean-escript deps app
	$(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)

distclean-escript:
	$(gen_verbose) rm -f $(ESCRIPT_NAME)

# Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
# This file is contributed to erlang.mk and subject to the terms of the ISC License.

.PHONY: eunit

# Configuration

EUNIT_OPTS ?=

# Core targets.

tests:: eunit

help::
	$(verbose) printf "%s\n" "" \
		"EUnit targets:" \
		"  eunit       Run all the EUnit tests for this project"

# Plugin-specific targets.

define eunit.erl
	case "$(COVER)" of
		"" -> ok;
		_ ->
			case cover:compile_beam_directory("ebin") of
				{error, _} -> halt(1);
				_ -> ok
			end
	end,
	case eunit:test([$(call comma_list,$(1))], [$(EUNIT_OPTS)]) of
		ok -> ok;
		error -> halt(2)
	end,
	case "$(COVER)" of
		"" -> ok;
		_ ->
			cover:export("eunit.coverdata")
	end,
	halt()
endef

EUNIT_EBIN_MODS = $(notdir $(basename $(call core_find,ebin/,*.beam)))
EUNIT_TEST_MODS = $(notdir $(basename $(call core_find,$(TEST_DIR)/,*.beam)))
EUNIT_MODS = $(foreach mod,$(EUNIT_EBIN_MODS) $(filter-out \
	$(patsubst %,%_tests,$(EUNIT_EBIN_MODS)),$(EUNIT_TEST_MODS)),{module,'$(mod)'})

eunit: test-build
	$(gen_verbose) $(ERL) -pa $(TEST_DIR) $(DEPS_DIR)/*/ebin ebin \
		-eval "$(subst $(newline),,$(subst ",\",$(call eunit.erl,$(EUNIT_MODS))))"

# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: relx-rel distclean-relx-rel distclean-relx run

# Configuration.

RELX_CONFIG ?= $(CURDIR)/relx.config

RELX ?= $(CURDIR)/relx
export RELX

RELX_URL ?= https://github.com/erlware/relx/releases/download/v2.0.0/relx
RELX_OPTS ?=
RELX_OUTPUT_DIR ?= _rel

ifeq ($(firstword $(RELX_OPTS)),-o)
	RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
else
	RELX_OPTS += -o $(RELX_OUTPUT_DIR)
endif

# Core targets.

ifeq ($(IS_DEP),)
ifneq ($(wildcard $(RELX_CONFIG)),)
rel:: distclean-relx-rel relx-rel
endif
endif

distclean:: distclean-relx-rel distclean-relx

# Plugin-specific targets.

$(RELX):
	$(gen_verbose) $(call core_http_get,$(RELX),$(RELX_URL))
	$(verbose) chmod +x $(RELX)

relx-rel: $(RELX)
	$(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)

distclean-relx-rel:
	$(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)

distclean-relx:
	$(gen_verbose) rm -rf $(RELX)

# Run target.

ifeq ($(wildcard $(RELX_CONFIG)),)
run:
else

define get_relx_release.erl
	{ok, Config} = file:consult("$(RELX_CONFIG)"),
	{release, {Name, _}, _} = lists:keyfind(release, 1, Config),
	io:format("~s", [Name]),
	halt(0).
endef

RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`

run: all
	$(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console

help::
	$(verbose) printf "%s\n" "" \
		"Relx targets:" \
		"  run         Compile the project, build the release and run it"

endif

# Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
# This file is contributed to erlang.mk and subject to the terms of the ISC License.

.PHONY: shell

# Configuration.

SHELL_PATH ?= -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin
SHELL_OPTS ?=

ALL_SHELL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(SHELL_DEPS))

# Core targets

help::
	$(verbose) printf "%s\n" "" \
		"Shell targets:" \
		"  shell       Run an erlang shell with SHELL_OPTS or reasonable default"

# Plugin-specific targets.

$(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))

build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
	$(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done

shell: build-shell-deps
	$(gen_verbose) erl $(SHELL_PATH) $(SHELL_OPTS)

# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
.PHONY: triq

# Targets.

tests:: triq

define triq_check.erl
	code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
	try
		case $(1) of
			all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
			module -> triq:check($(2));
			function -> triq:check($(2))
		end
	of
		true -> halt(0);
		_ -> halt(1)
	catch error:undef ->
		io:format("Undefined property or module~n"),
		halt(0)
	end.
endef

ifdef t
ifeq (,$(findstring :,$(t)))
triq: test-build
	$(verbose) $(call erlang,$(call triq_check.erl,module,$(t)))
else
triq: test-build
	$(verbose) echo Testing $(t)/0
	$(verbose) $(call erlang,$(call triq_check.erl,function,$(t)()))
endif
else
triq: test-build
	$(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename $(wildcard ebin/*.beam))))))
	$(gen_verbose) $(call erlang,$(call triq_check.erl,all,undefined,$(MODULES)))
endif
endif

# Copyright (c) 2015, Erlang Solutions Ltd.
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: xref distclean-xref

# Configuration.

ifeq ($(XREF_CONFIG),)
	XREF_ARGS :=
else
	XREF_ARGS := -c $(XREF_CONFIG)
endif

XREFR ?= $(CURDIR)/xrefr
export XREFR

XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr

# Core targets.

help::
	$(verbose) printf "%s\n" "" \
		"Xref targets:" \
		"  xref        Run Xrefr using $XREF_CONFIG as config file if defined"

distclean:: distclean-xref

# Plugin-specific targets.

$(XREFR):
	$(gen_verbose) $(call core_http_get,$(XREFR),$(XREFR_URL))
	$(verbose) chmod +x $(XREFR)

xref: deps app $(XREFR)
	$(gen_verbose) $(XREFR) $(XREFR_ARGS)

distclean-xref:
	$(gen_verbose) rm -rf $(XREFR)

# Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
# This file is part of erlang.mk and subject to the terms of the ISC License.

COVER_REPORT_DIR = cover

# Hook in coverage to ct

ifdef COVER
ifdef CT_RUN
# All modules in 'ebin'
COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))

test-build:: $(TEST_DIR)/ct.cover.spec

$(TEST_DIR)/ct.cover.spec:
	$(verbose) echo Cover mods: $(COVER_MODS)
	$(gen_verbose) printf "%s\n" \
		'{incl_mods,[$(subst $(space),$(comma),$(COVER_MODS))]}.' \
		'{export,"$(CURDIR)/ct.coverdata"}.' > $@

CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
endif
endif

# Core targets

ifdef COVER
ifneq ($(COVER_REPORT_DIR),)
tests::
	$(verbose) $(MAKE) --no-print-directory cover-report
endif
endif

clean:: coverdata-clean

ifneq ($(COVER_REPORT_DIR),)
distclean:: cover-report-clean
endif

help::
	$(verbose) printf "%s\n" "" \
		"Cover targets:" \
		"  cover-report  Generate a HTML coverage report from previously collected" \
		"                cover data." \
		"  all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
		"" \
		"If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
		"target tests additionally generates a HTML coverage report from the combined" \
		"coverdata files from each of these testing tools. HTML reports can be disabled" \
		"by setting COVER_REPORT_DIR to empty."

# Plugin specific targets

COVERDATA = $(filter-out all.coverdata,$(wildcard *.coverdata))

.PHONY: coverdata-clean
coverdata-clean:
	$(gen_verbose) rm -f *.coverdata ct.cover.spec

# Merge all coverdata files into one.
all.coverdata: $(COVERDATA)
	$(gen_verbose) $(ERL) -eval ' \
		$(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
		cover:export("$@"), halt(0).'

# These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
# empty if you want the coverdata files but not the HTML report.
ifneq ($(COVER_REPORT_DIR),)

.PHONY: cover-report-clean cover-report

cover-report-clean:
	$(gen_verbose) rm -rf $(COVER_REPORT_DIR)

ifeq ($(COVERDATA),)
cover-report:
else

# Modules which include eunit.hrl always contain one line without coverage
# because eunit defines test/0 which is never called. We compensate for this.
EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
	grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
	| sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))

define cover_report.erl
	$(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
	Ms = cover:imported_modules(),
	[cover:analyse_to_file(M, "$(COVER_REPORT_DIR)/" ++ atom_to_list(M)
		++ ".COVER.html", [html])  || M <- Ms],
	Report = [begin {ok, R} = cover:analyse(M, module), R end || M <- Ms],
	EunitHrlMods = [$(EUNIT_HRL_MODS)],
	Report1 = [{M, {Y, case lists:member(M, EunitHrlMods) of
		true -> N - 1; false -> N end}} || {M, {Y, N}} <- Report],
	TotalY = lists:sum([Y || {_, {Y, _}} <- Report1]),
	TotalN = lists:sum([N || {_, {_, N}} <- Report1]),
	TotalPerc = round(100 * TotalY / (TotalY + TotalN)),
	{ok, F} = file:open("$(COVER_REPORT_DIR)/index.html", [write]),
	io:format(F, "<!DOCTYPE html><html>~n"
		"<head><meta charset=\"UTF-8\">~n"
		"<title>Coverage report</title></head>~n"
		"<body>~n", []),
	io:format(F, "<h1>Coverage</h1>~n<p>Total: ~p%</p>~n", [TotalPerc]),
	io:format(F, "<table><tr><th>Module</th><th>Coverage</th></tr>~n", []),
	[io:format(F, "<tr><td><a href=\"~p.COVER.html\">~p</a></td>"
		"<td>~p%</td></tr>~n",
		[M, M, round(100 * Y / (Y + N))]) || {M, {Y, N}} <- Report1],
	How = "$(subst $(space),$(comma)$(space),$(basename $(COVERDATA)))",
	Date = "$(shell date -u "+%Y-%m-%dT%H:%M:%SZ")",
	io:format(F, "</table>~n"
		"<p>Generated using ~s and erlang.mk on ~s.</p>~n"
		"</body></html>", [How, Date]),
	halt().
endef

cover-report:
	$(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
	$(gen_verbose) $(call erlang,$(cover_report.erl))

endif
endif # ifneq ($(COVER_REPORT_DIR),)
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted deps/cowlib/include/cow_inline.hrl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
%% Copyright (c) 2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-ifndef(COW_INLINE_HRL).
-define(COW_INLINE_HRL, 1).

%% INLINE_LOWERCASE(Function, Rest, Acc, ...)
%%
%% To be included at the end of a case block.
%% Defined for up to 10 extra arguments.

-define(INLINE_LOWERCASE(Function, Rest, Acc),
	$A -> Function(Rest, << Acc/binary, $a >>);
	$B -> Function(Rest, << Acc/binary, $b >>);
	$C -> Function(Rest, << Acc/binary, $c >>);
	$D -> Function(Rest, << Acc/binary, $d >>);
	$E -> Function(Rest, << Acc/binary, $e >>);
	$F -> Function(Rest, << Acc/binary, $f >>);
	$G -> Function(Rest, << Acc/binary, $g >>);
	$H -> Function(Rest, << Acc/binary, $h >>);
	$I -> Function(Rest, << Acc/binary, $i >>);
	$J -> Function(Rest, << Acc/binary, $j >>);
	$K -> Function(Rest, << Acc/binary, $k >>);
	$L -> Function(Rest, << Acc/binary, $l >>);
	$M -> Function(Rest, << Acc/binary, $m >>);
	$N -> Function(Rest, << Acc/binary, $n >>);
	$O -> Function(Rest, << Acc/binary, $o >>);
	$P -> Function(Rest, << Acc/binary, $p >>);
	$Q -> Function(Rest, << Acc/binary, $q >>);
	$R -> Function(Rest, << Acc/binary, $r >>);
	$S -> Function(Rest, << Acc/binary, $s >>);
	$T -> Function(Rest, << Acc/binary, $t >>);
	$U -> Function(Rest, << Acc/binary, $u >>);
	$V -> Function(Rest, << Acc/binary, $v >>);
	$W -> Function(Rest, << Acc/binary, $w >>);
	$X -> Function(Rest, << Acc/binary, $x >>);
	$Y -> Function(Rest, << Acc/binary, $y >>);
	$Z -> Function(Rest, << Acc/binary, $z >>);
	C -> Function(Rest, << Acc/binary, C >>)
).

-define(INLINE_LOWERCASE(Function, Rest, A0, Acc),
	$A -> Function(Rest, A0, << Acc/binary, $a >>);
	$B -> Function(Rest, A0, << Acc/binary, $b >>);
	$C -> Function(Rest, A0, << Acc/binary, $c >>);
	$D -> Function(Rest, A0, << Acc/binary, $d >>);
	$E -> Function(Rest, A0, << Acc/binary, $e >>);
	$F -> Function(Rest, A0, << Acc/binary, $f >>);
	$G -> Function(Rest, A0, << Acc/binary, $g >>);
	$H -> Function(Rest, A0, << Acc/binary, $h >>);
	$I -> Function(Rest, A0, << Acc/binary, $i >>);
	$J -> Function(Rest, A0, << Acc/binary, $j >>);
	$K -> Function(Rest, A0, << Acc/binary, $k >>);
	$L -> Function(Rest, A0, << Acc/binary, $l >>);
	$M -> Function(Rest, A0, << Acc/binary, $m >>);
	$N -> Function(Rest, A0, << Acc/binary, $n >>);
	$O -> Function(Rest, A0, << Acc/binary, $o >>);
	$P -> Function(Rest, A0, << Acc/binary, $p >>);
	$Q -> Function(Rest, A0, << Acc/binary, $q >>);
	$R -> Function(Rest, A0, << Acc/binary, $r >>);
	$S -> Function(Rest, A0, << Acc/binary, $s >>);
	$T -> Function(Rest, A0, << Acc/binary, $t >>);
	$U -> Function(Rest, A0, << Acc/binary, $u >>);
	$V -> Function(Rest, A0, << Acc/binary, $v >>);
	$W -> Function(Rest, A0, << Acc/binary, $w >>);
	$X -> Function(Rest, A0, << Acc/binary, $x >>);
	$Y -> Function(Rest, A0, << Acc/binary, $y >>);
	$Z -> Function(Rest, A0, << Acc/binary, $z >>);
	C -> Function(Rest, A0, << Acc/binary, C >>)
).

-define(INLINE_LOWERCASE(Function, Rest, A0, A1, Acc),
	$A -> Function(Rest, A0, A1, << Acc/binary, $a >>);
	$B -> Function(Rest, A0, A1, << Acc/binary, $b >>);
	$C -> Function(Rest, A0, A1, << Acc/binary, $c >>);
	$D -> Function(Rest, A0, A1, << Acc/binary, $d >>);
	$E -> Function(Rest, A0, A1, << Acc/binary, $e >>);
	$F -> Function(Rest, A0, A1, << Acc/binary, $f >>);
	$G -> Function(Rest, A0, A1, << Acc/binary, $g >>);
	$H -> Function(Rest, A0, A1, << Acc/binary, $h >>);
	$I -> Function(Rest, A0, A1, << Acc/binary, $i >>);
	$J -> Function(Rest, A0, A1, << Acc/binary, $j >>);
	$K -> Function(Rest, A0, A1, << Acc/binary, $k >>);
	$L -> Function(Rest, A0, A1, << Acc/binary, $l >>);
	$M -> Function(Rest, A0, A1, << Acc/binary, $m >>);
	$N -> Function(Rest, A0, A1, << Acc/binary, $n >>);
	$O -> Function(Rest, A0, A1, << Acc/binary, $o >>);
	$P -> Function(Rest, A0, A1, << Acc/binary, $p >>);
	$Q -> Function(Rest, A0, A1, << Acc/binary, $q >>);
	$R -> Function(Rest, A0, A1, << Acc/binary, $r >>);
	$S -> Function(Rest, A0, A1, << Acc/binary, $s >>);
	$T -> Function(Rest, A0, A1, << Acc/binary, $t >>);
	$U -> Function(Rest, A0, A1, << Acc/binary, $u >>);
	$V -> Function(Rest, A0, A1, << Acc/binary, $v >>);
	$W -> Function(Rest, A0, A1, << Acc/binary, $w >>);
	$X -> Function(Rest, A0, A1, << Acc/binary, $x >>);
	$Y -> Function(Rest, A0, A1, << Acc/binary, $y >>);
	$Z -> Function(Rest, A0, A1, << Acc/binary, $z >>);
	C -> Function(Rest, A0, A1, << Acc/binary, C >>)
).

-define(INLINE_LOWERCASE(Function, Rest, A0, A1, A2, Acc),
	$A -> Function(Rest, A0, A1, A2, << Acc/binary, $a >>);
	$B -> Function(Rest, A0, A1, A2, << Acc/binary, $b >>);
	$C -> Function(Rest, A0, A1, A2, << Acc/binary, $c >>);
	$D -> Function(Rest, A0, A1, A2, << Acc/binary, $d >>);
	$E -> Function(Rest, A0, A1, A2, << Acc/binary, $e >>);
	$F -> Function(Rest, A0, A1, A2, << Acc/binary, $f >>);
	$G -> Function(Rest, A0, A1, A2, << Acc/binary, $g >>);
	$H -> Function(Rest, A0, A1, A2, << Acc/binary, $h >>);
	$I -> Function(Rest, A0, A1, A2, << Acc/binary, $i >>);
	$J -> Function(Rest, A0, A1, A2, << Acc/binary, $j >>);
	$K -> Function(Rest, A0, A1, A2, << Acc/binary, $k >>);
	$L -> Function(Rest, A0, A1, A2, << Acc/binary, $l >>);
	$M -> Function(Rest, A0, A1, A2, << Acc/binary, $m >>);
	$N -> Function(Rest, A0, A1, A2, << Acc/binary, $n >>);
	$O -> Function(Rest, A0, A1, A2, << Acc/binary, $o >>);
	$P -> Function(Rest, A0, A1, A2, << Acc/binary, $p >>);
	$Q -> Function(Rest, A0, A1, A2, << Acc/binary, $q >>);
	$R -> Function(Rest, A0, A1, A2, << Acc/binary, $r >>);
	$S -> Function(Rest, A0, A1, A2, << Acc/binary, $s >>);
	$T -> Function(Rest, A0, A1, A2, << Acc/binary, $t >>);
	$U -> Function(Rest, A0, A1, A2, << Acc/binary, $u >>);
	$V -> Function(Rest, A0, A1, A2, << Acc/binary, $v >>);
	$W -> Function(Rest, A0, A1, A2, << Acc/binary, $w >>);
	$X -> Function(Rest, A0, A1, A2, << Acc/binary, $x >>);
	$Y -> Function(Rest, A0, A1, A2, << Acc/binary, $y >>);
	$Z -> Function(Rest, A0, A1, A2, << Acc/binary, $z >>);
	C -> Function(Rest, A0, A1, A2, << Acc/binary, C >>)
).

-define(INLINE_LOWERCASE(Function, Rest, A0, A1, A2, A3, Acc),
	$A -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $a >>);
	$B -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $b >>);
	$C -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $c >>);
	$D -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $d >>);
	$E -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $e >>);
	$F -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $f >>);
	$G -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $g >>);
	$H -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $h >>);
	$I -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $i >>);
	$J -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $j >>);
	$K -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $k >>);
	$L -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $l >>);
	$M -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $m >>);
	$N -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $n >>);
	$O -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $o >>);
	$P -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $p >>);
	$Q -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $q >>);
	$R -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $r >>);
	$S -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $s >>);
	$T -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $t >>);
	$U -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $u >>);
	$V -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $v >>);
	$W -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $w >>);
	$X -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $x >>);
	$Y -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $y >>);
	$Z -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $z >>);
	C -> Function(Rest, A0, A1, A2, A3, << Acc/binary, C >>)
).

-define(INLINE_LOWERCASE(Function, Rest, A0, A1, A2, A3, A4, Acc),
	$A -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $a >>);
	$B -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $b >>);
	$C -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $c >>);
	$D -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $d >>);
	$E -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $e >>);
	$F -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $f >>);
	$G -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $g >>);
	$H -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $h >>);
	$I -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $i >>);
	$J -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $j >>);
	$K -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $k >>);
	$L -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $l >>);
	$M -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $m >>);
	$N -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $n >>);
	$O -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $o >>);
	$P -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $p >>);
	$Q -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $q >>);
	$R -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $r >>);
	$S -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $s >>);
	$T -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $t >>);
	$U -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $u >>);
	$V -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $v >>);
	$W -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $w >>);
	$X -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $x >>);
	$Y -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $y >>);
	$Z -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $z >>);
	C -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, C >>)
).

-define(INLINE_LOWERCASE(Function, Rest, A0, A1, A2, A3, A4, A5, Acc),
	$A -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $a >>);
	$B -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $b >>);
	$C -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $c >>);
	$D -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $d >>);
	$E -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $e >>);
	$F -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $f >>);
	$G -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $g >>);
	$H -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $h >>);
	$I -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $i >>);
	$J -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $j >>);
	$K -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $k >>);
	$L -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $l >>);
	$M -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $m >>);
	$N -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $n >>);
	$O -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $o >>);
	$P -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $p >>);
	$Q -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $q >>);
	$R -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $r >>);
	$S -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $s >>);
	$T -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $t >>);
	$U -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $u >>);
	$V -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $v >>);
	$W -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $w >>);
	$X -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $x >>);
	$Y -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $y >>);
	$Z -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $z >>);
	C -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, C >>)
).

-define(INLINE_LOWERCASE(Function, Rest, A0, A1, A2, A3, A4, A5, A6, Acc),
	$A -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $a >>);
	$B -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $b >>);
	$C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $c >>);
	$D -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $d >>);
	$E -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $e >>);
	$F -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $f >>);
	$G -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $g >>);
	$H -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $h >>);
	$I -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $i >>);
	$J -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $j >>);
	$K -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $k >>);
	$L -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $l >>);
	$M -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $m >>);
	$N -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $n >>);
	$O -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $o >>);
	$P -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $p >>);
	$Q -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $q >>);
	$R -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $r >>);
	$S -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $s >>);
	$T -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $t >>);
	$U -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $u >>);
	$V -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $v >>);
	$W -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $w >>);
	$X -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $x >>);
	$Y -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $y >>);
	$Z -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $z >>);
	C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, C >>)
).

-define(INLINE_LOWERCASE(Function, Rest, A0, A1, A2, A3, A4, A5, A6, A7, Acc),
	$A -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $a >>);
	$B -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $b >>);
	$C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $c >>);
	$D -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $d >>);
	$E -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $e >>);
	$F -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $f >>);
	$G -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $g >>);
	$H -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $h >>);
	$I -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $i >>);
	$J -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $j >>);
	$K -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $k >>);
	$L -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $l >>);
	$M -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $m >>);
	$N -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $n >>);
	$O -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $o >>);
	$P -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $p >>);
	$Q -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $q >>);
	$R -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $r >>);
	$S -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $s >>);
	$T -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $t >>);
	$U -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $u >>);
	$V -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $v >>);
	$W -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $w >>);
	$X -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $x >>);
	$Y -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $y >>);
	$Z -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $z >>);
	C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, C >>)
).

-define(INLINE_LOWERCASE(Function, Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, Acc),
	$A -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $a >>);
	$B -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $b >>);
	$C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $c >>);
	$D -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $d >>);
	$E -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $e >>);
	$F -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $f >>);
	$G -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $g >>);
	$H -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $h >>);
	$I -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $i >>);
	$J -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $j >>);
	$K -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $k >>);
	$L -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $l >>);
	$M -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $m >>);
	$N -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $n >>);
	$O -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $o >>);
	$P -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $p >>);
	$Q -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $q >>);
	$R -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $r >>);
	$S -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $s >>);
	$T -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $t >>);
	$U -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $u >>);
	$V -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $v >>);
	$W -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $w >>);
	$X -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $x >>);
	$Y -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $y >>);
	$Z -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $z >>);
	C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, C >>)
).

-define(INLINE_LOWERCASE(Function, Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, Acc),
	$A -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $a >>);
	$B -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $b >>);
	$C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $c >>);
	$D -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $d >>);
	$E -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $e >>);
	$F -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $f >>);
	$G -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $g >>);
	$H -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $h >>);
	$I -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $i >>);
	$J -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $j >>);
	$K -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $k >>);
	$L -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $l >>);
	$M -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $m >>);
	$N -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $n >>);
	$O -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $o >>);
	$P -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $p >>);
	$Q -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $q >>);
	$R -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $r >>);
	$S -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $s >>);
	$T -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $t >>);
	$U -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $u >>);
	$V -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $v >>);
	$W -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $w >>);
	$X -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $x >>);
	$Y -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $y >>);
	$Z -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $z >>);
	C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, C >>)
).

%% INLINE_LOWERCASE_BC(Bin)
%%
%% Lowercase the entire binary string in a binary comprehension.

-define(INLINE_LOWERCASE_BC(Bin),
	<< << case C of
		$A -> $a;
		$B -> $b;
		$C -> $c;
		$D -> $d;
		$E -> $e;
		$F -> $f;
		$G -> $g;
		$H -> $h;
		$I -> $i;
		$J -> $j;
		$K -> $k;
		$L -> $l;
		$M -> $m;
		$N -> $n;
		$O -> $o;
		$P -> $p;
		$Q -> $q;
		$R -> $r;
		$S -> $s;
		$T -> $t;
		$U -> $u;
		$V -> $v;
		$W -> $w;
		$X -> $x;
		$Y -> $y;
		$Z -> $z;
		C -> C
	end >> || << C >> <= Bin >>).

-endif.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<








































































































































































































































































































































































































































































































































































































































































































































































































Deleted deps/cowlib/src/cow_cookie.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
%% Copyright (c) 2013-2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(cow_cookie).

-export([parse_cookie/1]).
-export([setcookie/3]).

-type cookie_option() :: {max_age, non_neg_integer()}
	| {domain, binary()} | {path, binary()}
	| {secure, boolean()} | {http_only, boolean()}.
-type cookie_opts() :: [cookie_option()].
-export_type([cookie_opts/0]).

%% @doc Parse a cookie header string and return a list of key/values.

-spec parse_cookie(binary()) -> [{binary(), binary()}] | {error, badarg}.
parse_cookie(Cookie) ->
	parse_cookie(Cookie, []).

parse_cookie(<<>>, Acc) ->
	lists:reverse(Acc);
parse_cookie(<< $\s, Rest/binary >>, Acc) ->
	parse_cookie(Rest, Acc);
parse_cookie(<< $\t, Rest/binary >>, Acc) ->
	parse_cookie(Rest, Acc);
parse_cookie(<< $,, Rest/binary >>, Acc) ->
	parse_cookie(Rest, Acc);
parse_cookie(<< $;, Rest/binary >>, Acc) ->
	parse_cookie(Rest, Acc);
parse_cookie(<< $$, Rest/binary >>, Acc) ->
	skip_cookie(Rest, Acc);
parse_cookie(Cookie, Acc) ->
	parse_cookie_name(Cookie, Acc, <<>>).

skip_cookie(<<>>, Acc) ->
	lists:reverse(Acc);
skip_cookie(<< $,, Rest/binary >>, Acc) ->
	parse_cookie(Rest, Acc);
skip_cookie(<< $;, Rest/binary >>, Acc) ->
	parse_cookie(Rest, Acc);
skip_cookie(<< _, Rest/binary >>, Acc) ->
	skip_cookie(Rest, Acc).

parse_cookie_name(<<>>, _, _) ->
	{error, badarg};
parse_cookie_name(<< $=, _/binary >>, _, <<>>) ->
	{error, badarg};
parse_cookie_name(<< $=, Rest/binary >>, Acc, Name) ->
	parse_cookie_value(Rest, Acc, Name, <<>>);
parse_cookie_name(<< $,, _/binary >>, _, _) ->
	{error, badarg};
parse_cookie_name(<< $;, _/binary >>, _, _) ->
	{error, badarg};
parse_cookie_name(<< $\s, _/binary >>, _, _) ->
	{error, badarg};
parse_cookie_name(<< $\t, _/binary >>, _, _) ->
	{error, badarg};
parse_cookie_name(<< $\r, _/binary >>, _, _) ->
	{error, badarg};
parse_cookie_name(<< $\n, _/binary >>, _, _) ->
	{error, badarg};
parse_cookie_name(<< $\013, _/binary >>, _, _) ->
	{error, badarg};
parse_cookie_name(<< $\014, _/binary >>, _, _) ->
	{error, badarg};
parse_cookie_name(<< C, Rest/binary >>, Acc, Name) ->
	parse_cookie_name(Rest, Acc, << Name/binary, C >>).

parse_cookie_value(<<>>, Acc, Name, Value) ->
	lists:reverse([{Name, parse_cookie_trim(Value)}|Acc]);
parse_cookie_value(<< $;, Rest/binary >>, Acc, Name, Value) ->
	parse_cookie(Rest, [{Name, parse_cookie_trim(Value)}|Acc]);
parse_cookie_value(<< $\t, _/binary >>, _, _, _) ->
	{error, badarg};
parse_cookie_value(<< $\r, _/binary >>, _, _, _) ->
	{error, badarg};
parse_cookie_value(<< $\n, _/binary >>, _, _, _) ->
	{error, badarg};
parse_cookie_value(<< $\013, _/binary >>, _, _, _) ->
	{error, badarg};
parse_cookie_value(<< $\014, _/binary >>, _, _, _) ->
	{error, badarg};
parse_cookie_value(<< C, Rest/binary >>, Acc, Name, Value) ->
	parse_cookie_value(Rest, Acc, Name, << Value/binary, C >>).

parse_cookie_trim(Value = <<>>) ->
	Value;
parse_cookie_trim(Value) ->
	case binary:last(Value) of
		$\s ->
			Size = byte_size(Value) - 1,
			<< Value2:Size/binary, _ >> = Value,
			parse_cookie_trim(Value2);
		_ ->
			Value
	end.

-ifdef(TEST).
parse_cookie_test_() ->
	%% {Value, Result}.
	Tests = [
		{<<"name=value; name2=value2">>, [
			{<<"name">>, <<"value">>},
			{<<"name2">>, <<"value2">>}
		]},
		{<<"$Version=1; Customer=WILE_E_COYOTE; $Path=/acme">>, [
			{<<"Customer">>, <<"WILE_E_COYOTE">>}
		]},
		{<<"$Version=1; Customer=WILE_E_COYOTE; $Path=/acme; "
			"Part_Number=Rocket_Launcher_0001; $Path=/acme; "
			"Shipping=FedEx; $Path=/acme">>, [
			{<<"Customer">>, <<"WILE_E_COYOTE">>},
			{<<"Part_Number">>, <<"Rocket_Launcher_0001">>},
			{<<"Shipping">>, <<"FedEx">>}
		]},
		%% Space in value.
		{<<"foo=Thu Jul 11 2013 15:38:43 GMT+0400 (MSK)">>,
			[{<<"foo">>, <<"Thu Jul 11 2013 15:38:43 GMT+0400 (MSK)">>}]},
		%% Comma in value. Google Analytics sets that kind of cookies.
		{<<"refk=sOUZDzq2w2; sk=B602064E0139D842D620C7569640DBB4C81C45080651"
			"9CC124EF794863E10E80; __utma=64249653.825741573.1380181332.1400"
			"015657.1400019557.703; __utmb=64249653.1.10.1400019557; __utmc="
			"64249653; __utmz=64249653.1400019557.703.13.utmcsr=bluesky.chic"
			"agotribune.com|utmccn=(referral)|utmcmd=referral|utmcct=/origin"
			"als/chi-12-indispensable-digital-tools-bsi,0,0.storygallery">>, [
				{<<"refk">>, <<"sOUZDzq2w2">>},
				{<<"sk">>, <<"B602064E0139D842D620C7569640DBB4C81C45080651"
					"9CC124EF794863E10E80">>},
				{<<"__utma">>, <<"64249653.825741573.1380181332.1400"
					"015657.1400019557.703">>},
				{<<"__utmb">>, <<"64249653.1.10.1400019557">>},
				{<<"__utmc">>, <<"64249653">>},
				{<<"__utmz">>, <<"64249653.1400019557.703.13.utmcsr=bluesky.chic"
					"agotribune.com|utmccn=(referral)|utmcmd=referral|utmcct=/origin"
					"als/chi-12-indispensable-digital-tools-bsi,0,0.storygallery">>}
		]},
		%% Potential edge cases (initially from Mochiweb).
		{<<"foo=\\x">>, [{<<"foo">>, <<"\\x">>}]},
		{<<"=">>, {error, badarg}},
		{<<"  foo ; bar  ">>, {error, badarg}},
		{<<"foo=;bar=">>, [{<<"foo">>, <<>>}, {<<"bar">>, <<>>}]},
		{<<"foo=\\\";;bar ">>, {error, badarg}},
		{<<"foo=\\\";;bar=good ">>,
			[{<<"foo">>, <<"\\\"">>}, {<<"bar">>, <<"good">>}]},
		{<<"foo=\"\\\";bar">>, {error, badarg}},
		{<<>>, []},
		{<<"foo=bar , baz=wibble ">>, [{<<"foo">>, <<"bar , baz=wibble">>}]}
	],
	[{V, fun() -> R = parse_cookie(V) end} || {V, R} <- Tests].
-endif.

%% @doc Convert a cookie name, value and options to its iodata form.
%% @end
%%
%% Initially from Mochiweb:
%%   * Copyright 2007 Mochi Media, Inc.
%% Initial binary implementation:
%%   * Copyright 2011 Thomas Burdick <thomas.burdick@gmail.com>

-spec setcookie(iodata(), iodata(), cookie_opts()) -> iodata().
setcookie(Name, Value, Opts) ->
	nomatch = binary:match(iolist_to_binary(Name), [<<$=>>, <<$,>>, <<$;>>,
			<<$\s>>, <<$\t>>, <<$\r>>, <<$\n>>, <<$\013>>, <<$\014>>]),
	nomatch = binary:match(iolist_to_binary(Value), [<<$,>>, <<$;>>,
			<<$\s>>, <<$\t>>, <<$\r>>, <<$\n>>, <<$\013>>, <<$\014>>]),
	MaxAgeBin = case lists:keyfind(max_age, 1, Opts) of
		false -> <<>>;
		{_, 0} ->
			%% MSIE requires an Expires date in the past to delete a cookie.
			<<"; Expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0">>;
		{_, MaxAge} when is_integer(MaxAge), MaxAge > 0 ->
			UTC = calendar:universal_time(),
			Secs = calendar:datetime_to_gregorian_seconds(UTC),
			Expires = calendar:gregorian_seconds_to_datetime(Secs + MaxAge),
			[<<"; Expires=">>, cow_date:rfc2109(Expires),
				<<"; Max-Age=">>, integer_to_list(MaxAge)]
	end,
	DomainBin = case lists:keyfind(domain, 1, Opts) of
		false -> <<>>;
		{_, Domain} -> [<<"; Domain=">>, Domain]
	end,
	PathBin = case lists:keyfind(path, 1, Opts) of
		false -> <<>>;
		{_, Path} -> [<<"; Path=">>, Path]
	end,
	SecureBin = case lists:keyfind(secure, 1, Opts) of
		false -> <<>>;
		{_, false} -> <<>>;
		{_, true} -> <<"; Secure">>
	end,
	HttpOnlyBin = case lists:keyfind(http_only, 1, Opts) of
		false -> <<>>;
		{_, false} -> <<>>;
		{_, true} -> <<"; HttpOnly">>
	end,
	[Name, <<"=">>, Value, <<"; Version=1">>,
		MaxAgeBin, DomainBin, PathBin, SecureBin, HttpOnlyBin].

-ifdef(TEST).
setcookie_test_() ->
	%% {Name, Value, Opts, Result}
	Tests = [
		{<<"Customer">>, <<"WILE_E_COYOTE">>,
			[{http_only, true}, {domain, <<"acme.com">>}],
			<<"Customer=WILE_E_COYOTE; Version=1; "
				"Domain=acme.com; HttpOnly">>},
		{<<"Customer">>, <<"WILE_E_COYOTE">>,
			[{path, <<"/acme">>}],
			<<"Customer=WILE_E_COYOTE; Version=1; Path=/acme">>},
		{<<"Customer">>, <<"WILE_E_COYOTE">>,
			[{secure, true}],
			<<"Customer=WILE_E_COYOTE; Version=1; Secure">>},
		{<<"Customer">>, <<"WILE_E_COYOTE">>,
			[{secure, false}, {http_only, false}],
			<<"Customer=WILE_E_COYOTE; Version=1">>},
		{<<"Customer">>, <<"WILE_E_COYOTE">>,
			[{path, <<"/acme">>}, {badoption, <<"negatory">>}],
			<<"Customer=WILE_E_COYOTE; Version=1; Path=/acme">>}
	],
	[{R, fun() -> R = iolist_to_binary(setcookie(N, V, O)) end}
		|| {N, V, O, R} <- Tests].

setcookie_max_age_test() ->
	F = fun(N, V, O) ->
		binary:split(iolist_to_binary(
			setcookie(N, V, O)), <<";">>, [global])
	end,
	[<<"Customer=WILE_E_COYOTE">>,
		<<" Version=1">>,
		<<" Expires=", _/binary>>,
		<<" Max-Age=111">>,
		<<" Secure">>] = F(<<"Customer">>, <<"WILE_E_COYOTE">>,
			[{max_age, 111}, {secure, true}]),
	case catch F(<<"Customer">>, <<"WILE_E_COYOTE">>, [{max_age, -111}]) of
		{'EXIT', {{case_clause, {max_age, -111}}, _}} -> ok
	end,
	[<<"Customer=WILE_E_COYOTE">>,
		<<" Version=1">>,
		<<" Expires=", _/binary>>,
		<<" Max-Age=86417">>] = F(<<"Customer">>, <<"WILE_E_COYOTE">>,
			 [{max_age, 86417}]),
	ok.

setcookie_failures_test_() ->
	F = fun(N, V) ->
		try setcookie(N, V, []) of
			_ ->
				false
		catch _:_ ->
			true
		end
	end,
	Tests = [
		{<<"Na=me">>, <<"Value">>},
		{<<"Name;">>, <<"Value">>},
		{<<"\r\name">>, <<"Value">>},
		{<<"Name">>, <<"Value;">>},
		{<<"Name">>, <<"\value">>}
	],
	[{iolist_to_binary(io_lib:format("{~p, ~p} failure", [N, V])),
		fun() -> true = F(N, V) end}
		|| {N, V} <- Tests].
-endif.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






































































































































































































































































































































































































































































































































































Deleted deps/cowlib/src/cow_date.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
%% Copyright (c) 2013-2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(cow_date).

-export([rfc2109/1]).

%% @doc Return the date formatted according to RFC2109.

-spec rfc2109(calendar:datetime()) -> binary().
rfc2109({Date = {Y, Mo, D}, {H, Mi, S}}) ->
	Wday = calendar:day_of_the_week(Date),
	<<	(weekday(Wday))/binary, ", ",
		(pad_int(D))/binary, "-",
		(month(Mo))/binary, "-",
		(year(Y))/binary, " ",
		(pad_int(H))/binary, ":",
		(pad_int(Mi))/binary, ":",
		(pad_int(S))/binary, " GMT" >>.

-ifdef(TEST).
rfc2109_test_() ->
	Tests = [
		{<<"Sat, 14-May-2011 14:25:33 GMT">>, {{2011, 5, 14}, {14, 25, 33}}},
		{<<"Sun, 01-Jan-2012 00:00:00 GMT">>, {{2012, 1,  1}, { 0,  0,  0}}}
	],
	[{R, fun() -> R = rfc2109(D) end} || {R, D} <- Tests].
-endif.

-ifdef(PERF).
horse_rfc2019_20130101_000000() ->
	horse:repeat(100000,
		rfc2109({{2013, 1, 1}, {0, 0, 0}})
	).

horse_rfc2019_20131231_235959() ->
	horse:repeat(100000,
		rfc2109({{2013, 12, 31}, {23, 59, 59}})
	).

horse_rfc2019_12340506_070809() ->
	horse:repeat(100000,
		rfc2109({{1234, 5, 6}, {7, 8, 9}})
	).
-endif.

%% Internal.

-spec pad_int(0..59) -> <<_:16>>.
pad_int( 0) -> <<"00">>;
pad_int( 1) -> <<"01">>;
pad_int( 2) -> <<"02">>;
pad_int( 3) -> <<"03">>;
pad_int( 4) -> <<"04">>;
pad_int( 5) -> <<"05">>;
pad_int( 6) -> <<"06">>;
pad_int( 7) -> <<"07">>;
pad_int( 8) -> <<"08">>;
pad_int( 9) -> <<"09">>;
pad_int(10) -> <<"10">>;
pad_int(11) -> <<"11">>;
pad_int(12) -> <<"12">>;
pad_int(13) -> <<"13">>;
pad_int(14) -> <<"14">>;
pad_int(15) -> <<"15">>;
pad_int(16) -> <<"16">>;
pad_int(17) -> <<"17">>;
pad_int(18) -> <<"18">>;
pad_int(19) -> <<"19">>;
pad_int(20) -> <<"20">>;
pad_int(21) -> <<"21">>;
pad_int(22) -> <<"22">>;
pad_int(23) -> <<"23">>;
pad_int(24) -> <<"24">>;
pad_int(25) -> <<"25">>;
pad_int(26) -> <<"26">>;
pad_int(27) -> <<"27">>;
pad_int(28) -> <<"28">>;
pad_int(29) -> <<"29">>;
pad_int(30) -> <<"30">>;
pad_int(31) -> <<"31">>;
pad_int(32) -> <<"32">>;
pad_int(33) -> <<"33">>;
pad_int(34) -> <<"34">>;
pad_int(35) -> <<"35">>;
pad_int(36) -> <<"36">>;
pad_int(37) -> <<"37">>;
pad_int(38) -> <<"38">>;
pad_int(39) -> <<"39">>;
pad_int(40) -> <<"40">>;
pad_int(41) -> <<"41">>;
pad_int(42) -> <<"42">>;
pad_int(43) -> <<"43">>;
pad_int(44) -> <<"44">>;
pad_int(45) -> <<"45">>;
pad_int(46) -> <<"46">>;
pad_int(47) -> <<"47">>;
pad_int(48) -> <<"48">>;
pad_int(49) -> <<"49">>;
pad_int(50) -> <<"50">>;
pad_int(51) -> <<"51">>;
pad_int(52) -> <<"52">>;
pad_int(53) -> <<"53">>;
pad_int(54) -> <<"54">>;
pad_int(55) -> <<"55">>;
pad_int(56) -> <<"56">>;
pad_int(57) -> <<"57">>;
pad_int(58) -> <<"58">>;
pad_int(59) -> <<"59">>.

-spec weekday(1..7) -> <<_:24>>.
weekday(1) -> <<"Mon">>;
weekday(2) -> <<"Tue">>;
weekday(3) -> <<"Wed">>;
weekday(4) -> <<"Thu">>;
weekday(5) -> <<"Fri">>;
weekday(6) -> <<"Sat">>;
weekday(7) -> <<"Sun">>.

-spec month(1..12) -> <<_:24>>.
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">>.

-spec year(pos_integer()) -> <<_:32>>.
year(1970) -> <<"1970">>;
year(1971) -> <<"1971">>;
year(1972) -> <<"1972">>;
year(1973) -> <<"1973">>;
year(1974) -> <<"1974">>;
year(1975) -> <<"1975">>;
year(1976) -> <<"1976">>;
year(1977) -> <<"1977">>;
year(1978) -> <<"1978">>;
year(1979) -> <<"1979">>;
year(1980) -> <<"1980">>;
year(1981) -> <<"1981">>;
year(1982) -> <<"1982">>;
year(1983) -> <<"1983">>;
year(1984) -> <<"1984">>;
year(1985) -> <<"1985">>;
year(1986) -> <<"1986">>;
year(1987) -> <<"1987">>;
year(1988) -> <<"1988">>;
year(1989) -> <<"1989">>;
year(1990) -> <<"1990">>;
year(1991) -> <<"1991">>;
year(1992) -> <<"1992">>;
year(1993) -> <<"1993">>;
year(1994) -> <<"1994">>;
year(1995) -> <<"1995">>;
year(1996) -> <<"1996">>;
year(1997) -> <<"1997">>;
year(1998) -> <<"1998">>;
year(1999) -> <<"1999">>;
year(2000) -> <<"2000">>;
year(2001) -> <<"2001">>;
year(2002) -> <<"2002">>;
year(2003) -> <<"2003">>;
year(2004) -> <<"2004">>;
year(2005) -> <<"2005">>;
year(2006) -> <<"2006">>;
year(2007) -> <<"2007">>;
year(2008) -> <<"2008">>;
year(2009) -> <<"2009">>;
year(2010) -> <<"2010">>;
year(2011) -> <<"2011">>;
year(2012) -> <<"2012">>;
year(2013) -> <<"2013">>;
year(2014) -> <<"2014">>;
year(2015) -> <<"2015">>;
year(2016) -> <<"2016">>;
year(2017) -> <<"2017">>;
year(2018) -> <<"2018">>;
year(2019) -> <<"2019">>;
year(2020) -> <<"2020">>;
year(2021) -> <<"2021">>;
year(2022) -> <<"2022">>;
year(2023) -> <<"2023">>;
year(2024) -> <<"2024">>;
year(2025) -> <<"2025">>;
year(2026) -> <<"2026">>;
year(2027) -> <<"2027">>;
year(2028) -> <<"2028">>;
year(2029) -> <<"2029">>;
year(Year) -> list_to_binary(integer_to_list(Year)).
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




























































































































































































































































































































































































































Deleted deps/cowlib/src/cow_http.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
%% Copyright (c) 2013-2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(cow_http).

%% @todo parse_request_line
-export([parse_status_line/1]).
-export([parse_headers/1]).

-export([parse_fullhost/1]).
-export([parse_fullpath/1]).
-export([parse_version/1]).

-export([request/4]).
-export([version/1]).

-type version() :: 'HTTP/1.0' | 'HTTP/1.1'.
-type status() :: 100..999.
-type headers() :: [{binary(), iodata()}].

-include("cow_inline.hrl").

%% @doc Parse the status line.

-spec parse_status_line(binary()) -> {version(), status(), binary(), binary()}.
parse_status_line(<< "HTTP/1.1 200 OK\r\n", Rest/bits >>) ->
	{'HTTP/1.1', 200, <<"OK">>, Rest};
parse_status_line(<< "HTTP/1.1 404 Not Found\r\n", Rest/bits >>) ->
	{'HTTP/1.1', 404, <<"Not Found">>, Rest};
parse_status_line(<< "HTTP/1.1 500 Internal Server Error\r\n", Rest/bits >>) ->
	{'HTTP/1.1', 500, <<"Internal Server Error">>, Rest};
parse_status_line(<< "HTTP/1.1 ", Status/bits >>) ->
	parse_status_line(Status, 'HTTP/1.1');
parse_status_line(<< "HTTP/1.0 ", Status/bits >>) ->
	parse_status_line(Status, 'HTTP/1.0').

parse_status_line(<< H, T, U, " ", Rest/bits >>, Version)
		when $0 =< H, H =< $9, $0 =< T, T =< $9, $0 =< U, U =< $9 ->
	Status = (H - $0) * 100 + (T - $0) * 10 + (U - $0),
	{Pos, _} = binary:match(Rest, <<"\r">>),
	<< StatusStr:Pos/binary, "\r\n", Rest2/bits >> = Rest,
	{Version, Status, StatusStr, Rest2}.

-ifdef(TEST).
parse_status_line_test_() ->
	Tests = [
		{<<"HTTP/1.1 200 OK\r\nRest">>,
			{'HTTP/1.1', 200, <<"OK">>, <<"Rest">>}},
		{<<"HTTP/1.0 404 Not Found\r\nRest">>,
			{'HTTP/1.0', 404, <<"Not Found">>, <<"Rest">>}},
		{<<"HTTP/1.1 500 Something very funny here\r\nRest">>,
			{'HTTP/1.1', 500, <<"Something very funny here">>, <<"Rest">>}},
		{<<"HTTP/1.1 200 \r\nRest">>,
			{'HTTP/1.1', 200, <<>>, <<"Rest">>}}
	],
	[{V, fun() -> R = parse_status_line(V) end}
		|| {V, R} <- Tests].

parse_status_line_error_test_() ->
	Tests = [
		<<>>,
		<<"HTTP/1.1">>,
		<<"HTTP/1.1 200\r\n">>,
		<<"HTTP/1.1 200 OK">>,
		<<"HTTP/1.1 200 OK\r">>,
		<<"HTTP/1.1 200 OK\n">>,
		<<"HTTP/0.9 200 OK\r\n">>,
		<<"HTTP/1.1 42 Answer\r\n">>,
		<<"HTTP/1.1 999999999 More than OK\r\n">>,
		<<"content-type: text/plain\r\n">>,
		<<0:80, "\r\n">>
	],
	[{V, fun() -> {'EXIT', _} = (catch parse_status_line(V)) end}
		|| V <- Tests].
-endif.

-ifdef(PERF).
horse_parse_status_line_200() ->
	horse:repeat(200000,
		parse_status_line(<<"HTTP/1.1 200 OK\r\n">>)
	).

horse_parse_status_line_404() ->
	horse:repeat(200000,
		parse_status_line(<<"HTTP/1.1 404 Not Found\r\n">>)
	).

horse_parse_status_line_500() ->
	horse:repeat(200000,
		parse_status_line(<<"HTTP/1.1 500 Internal Server Error\r\n">>)
	).

horse_parse_status_line_other() ->
	horse:repeat(200000,
		parse_status_line(<<"HTTP/1.1 416 Requested range not satisfiable\r\n">>)
	).
-endif.

%% @doc Parse the list of headers.

-spec parse_headers(binary()) -> {[{binary(), binary()}], binary()}.
parse_headers(Data) ->
	parse_header(Data, []).

parse_header(<< $\r, $\n, Rest/bits >>, Acc) ->
	{lists:reverse(Acc), Rest};
parse_header(Data, Acc) ->
	parse_hd_name(Data, Acc, <<>>).

parse_hd_name(<< C, Rest/bits >>, Acc, SoFar) ->
	case C of
		$: -> parse_hd_before_value(Rest, Acc, SoFar);
		$\s -> parse_hd_name_ws(Rest, Acc, SoFar);
		$\t -> parse_hd_name_ws(Rest, Acc, SoFar);
		?INLINE_LOWERCASE(parse_hd_name, Rest, Acc, SoFar)
	end.

parse_hd_name_ws(<< C, Rest/bits >>, Acc, Name) ->
	case C of
		$: -> parse_hd_before_value(Rest, Acc, Name);
		$\s -> parse_hd_name_ws(Rest, Acc, Name);
		$\t -> parse_hd_name_ws(Rest, Acc, Name)
	end.

parse_hd_before_value(<< $\s, Rest/bits >>, Acc, Name) ->
	parse_hd_before_value(Rest, Acc, Name);
parse_hd_before_value(<< $\t, Rest/bits >>, Acc, Name) ->
	parse_hd_before_value(Rest, Acc, Name);
parse_hd_before_value(Data, Acc, Name) ->
	parse_hd_value(Data, Acc, Name, <<>>).

parse_hd_value(<< $\r, Rest/bits >>, Acc, Name, SoFar) ->
	case Rest of
		<< $\n, C, Rest2/bits >> when C =:= $\s; C =:= $\t ->
			parse_hd_value(Rest2, Acc, Name, << SoFar/binary, C >>);
		<< $\n, Rest2/bits >> ->
			parse_header(Rest2, [{Name, SoFar}|Acc])
	end;
parse_hd_value(<< C, Rest/bits >>, Acc, Name, SoFar) ->
	parse_hd_value(Rest, Acc, Name, << SoFar/binary, C >>).

-ifdef(TEST).
parse_headers_test_() ->
	Tests = [
		{<<"\r\nRest">>,
			{[], <<"Rest">>}},
		{<<"Server: Erlang/R17\r\n"
			"Date: Sun, 23 Feb 2014 09:30:39 GMT\r\n"
			"Multiline-Header: why hello!\r\n"
				" I didn't see you all the way over there!\r\n"
			"Content-Length: 12\r\n"
			"Content-Type: text/plain\r\n"
			"\r\nRest">>,
			{[{<<"server">>, <<"Erlang/R17">>},
				{<<"date">>, <<"Sun, 23 Feb 2014 09:30:39 GMT">>},
				{<<"multiline-header">>,
					<<"why hello! I didn't see you all the way over there!">>},
				{<<"content-length">>, <<"12">>},
				{<<"content-type">>, <<"text/plain">>}],
				<<"Rest">>}}
	],
	[{V, fun() -> R = parse_headers(V) end}
		|| {V, R} <- Tests].

parse_headers_error_test_() ->
	Tests = [
		<<>>,
		<<"\r">>,
		<<"Malformed\r\n\r\n">>,
		<<"content-type: text/plain\r\nMalformed\r\n\r\n">>,
		<<"HTTP/1.1 200 OK\r\n\r\n">>,
		<<0:80, "\r\n\r\n">>,
		<<"content-type: text/plain\r\ncontent-length: 12\r\n">>
	],
	[{V, fun() -> {'EXIT', _} = (catch parse_headers(V)) end}
		|| V <- Tests].
-endif.

-ifdef(PERF).
horse_parse_headers() ->
	horse:repeat(50000,
		parse_headers(<<"Server: Erlang/R17\r\n"
			"Date: Sun, 23 Feb 2014 09:30:39 GMT\r\n"
			"Multiline-Header: why hello!\r\n"
				" I didn't see you all the way over there!\r\n"
			"Content-Length: 12\r\n"
			"Content-Type: text/plain\r\n"
			"\r\nRest">>)
	).
-endif.

%% @doc Extract host and port from a binary.
%%
%% Because the hostname is case insensitive it is converted
%% to lowercase.

-spec parse_fullhost(binary()) -> {binary(), undefined | non_neg_integer()}.
parse_fullhost(Fullhost) ->
	parse_fullhost(Fullhost, false, <<>>).

parse_fullhost(<< $[, Rest/bits >>, false, <<>>) ->
	parse_fullhost(Rest, true, << $[ >>);
parse_fullhost(<<>>, false, Acc) ->
	{Acc, undefined};
%% @todo Optimize.
parse_fullhost(<< $:, Rest/bits >>, false, Acc) ->
	{Acc, list_to_integer(binary_to_list(Rest))};
parse_fullhost(<< $], Rest/bits >>, true, Acc) ->
	parse_fullhost(Rest, false, << Acc/binary, $] >>);
parse_fullhost(<< C, Rest/bits >>, E, Acc) ->
	case C of
		?INLINE_LOWERCASE(parse_fullhost, Rest, E, Acc)
	end.

-ifdef(TEST).
parse_fullhost_test() ->
	{<<"example.org">>, 8080} = parse_fullhost(<<"example.org:8080">>),
	{<<"example.org">>, undefined} = parse_fullhost(<<"example.org">>),
	{<<"192.0.2.1">>, 8080} = parse_fullhost(<<"192.0.2.1:8080">>),
	{<<"192.0.2.1">>, undefined} = parse_fullhost(<<"192.0.2.1">>),
	{<<"[2001:db8::1]">>, 8080} = parse_fullhost(<<"[2001:db8::1]:8080">>),
	{<<"[2001:db8::1]">>, undefined} = parse_fullhost(<<"[2001:db8::1]">>),
	{<<"[::ffff:192.0.2.1]">>, 8080}
		= parse_fullhost(<<"[::ffff:192.0.2.1]:8080">>),
	{<<"[::ffff:192.0.2.1]">>, undefined}
		= parse_fullhost(<<"[::ffff:192.0.2.1]">>),
	ok.
-endif.

%% @doc Extract path and query string from a binary.

-spec parse_fullpath(binary()) -> {binary(), binary()}.
parse_fullpath(Fullpath) ->
	parse_fullpath(Fullpath, <<>>).

parse_fullpath(<<>>, Path) ->
	{Path, <<>>};
parse_fullpath(<< $?, Qs/binary >>, Path) ->
	{Path, Qs};
parse_fullpath(<< C, Rest/binary >>, SoFar) ->
	parse_fullpath(Rest, << SoFar/binary, C >>).

-ifdef(TEST).
parse_fullpath_test() ->
	{<<"*">>, <<>>} = parse_fullpath(<<"*">>),
	{<<"/">>, <<>>} = parse_fullpath(<<"/">>),
	{<<"/path/to/resource">>, <<>>} = parse_fullpath(<<"/path/to/resource">>),
	{<<"/">>, <<>>} = parse_fullpath(<<"/?">>),
	{<<"/">>, <<"q=cowboy">>} = parse_fullpath(<<"/?q=cowboy">>),
	{<<"/path/to/resource">>, <<"q=cowboy">>}
		= parse_fullpath(<<"/path/to/resource?q=cowboy">>),
	ok.
-endif.

%% @doc Convert an HTTP version to atom.

-spec parse_version(binary()) -> version().
parse_version(<<"HTTP/1.1">>) -> 'HTTP/1.1';
parse_version(<<"HTTP/1.0">>) -> 'HTTP/1.0'.

-ifdef(TEST).
parse_version_test() ->
	'HTTP/1.1' = parse_version(<<"HTTP/1.1">>),
	'HTTP/1.0' = parse_version(<<"HTTP/1.0">>),
	{'EXIT', _} = (catch parse_version(<<"HTTP/1.2">>)),
	ok.
-endif.

%% @doc Return formatted request-line and headers.
%% @todo Add tests when the corresponding reverse functions are added.

-spec request(binary(), iodata(), version(), headers()) -> iodata().
request(Method, Path, Version, Headers) ->
	[Method, <<" ">>, Path, <<" ">>, version(Version), <<"\r\n">>,
		[[N, <<": ">>, V, <<"\r\n">>] || {N, V} <- Headers],
		<<"\r\n">>].

%% @doc Return the version as a binary.

-spec version(version()) -> binary().
version('HTTP/1.1') -> <<"HTTP/1.1">>;
version('HTTP/1.0') -> <<"HTTP/1.0">>.

-ifdef(TEST).
version_test() ->
	<<"HTTP/1.1">> = version('HTTP/1.1'),
	<<"HTTP/1.0">> = version('HTTP/1.0'),
	{'EXIT', _} = (catch version('HTTP/1.2')),
	ok.
-endif.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


























































































































































































































































































































































































































































































































































































































Deleted deps/cowlib/src/cow_http_hd.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
%% Copyright (c) 2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(cow_http_hd).

-export([parse_connection/1]).
-export([parse_content_length/1]).
-export([parse_transfer_encoding/1]).

-include("cow_inline.hrl").

%% @doc Parse the Connection header.

-spec parse_connection(binary()) -> [binary()].
parse_connection(<<"close">>) ->
	[<<"close">>];
parse_connection(<<"keep-alive">>) ->
	[<<"keep-alive">>];
parse_connection(Connection) ->
	nonempty(token_ci_list(Connection, [])).

-ifdef(TEST).
parse_connection_test_() ->
	Tests = [
		{<<"close">>, [<<"close">>]},
		{<<"ClOsE">>, [<<"close">>]},
		{<<"Keep-Alive">>, [<<"keep-alive">>]},
		{<<"keep-alive, Upgrade">>, [<<"keep-alive">>, <<"upgrade">>]}
	],
	[{V, fun() -> R = parse_connection(V) end} || {V, R} <- Tests].
-endif.

-ifdef(PERF).
horse_parse_connection_close() ->
	horse:repeat(200000,
		parse_connection(<<"close">>)
	).

horse_parse_connection_keepalive() ->
	horse:repeat(200000,
		parse_connection(<<"keep-alive">>)
	).

horse_parse_connection_keepalive_upgrade() ->
	horse:repeat(200000,
		parse_connection(<<"keep-alive, upgrade">>)
	).
-endif.

%% @doc Parse the Content-Length header.
%%
%% The value has at least one digit, and may be followed by whitespace.

-spec parse_content_length(binary()) -> non_neg_integer().
parse_content_length(<< $0 >>) -> 0;
parse_content_length(<< $0, R/bits >>) -> number(R, 0);
parse_content_length(<< $1, R/bits >>) -> number(R, 1);
parse_content_length(<< $2, R/bits >>) -> number(R, 2);
parse_content_length(<< $3, R/bits >>) -> number(R, 3);
parse_content_length(<< $4, R/bits >>) -> number(R, 4);
parse_content_length(<< $5, R/bits >>) -> number(R, 5);
parse_content_length(<< $6, R/bits >>) -> number(R, 6);
parse_content_length(<< $7, R/bits >>) -> number(R, 7);
parse_content_length(<< $8, R/bits >>) -> number(R, 8);
parse_content_length(<< $9, R/bits >>) -> number(R, 9).

-ifdef(TEST).
parse_content_length_test_() ->
	Tests = [
		{<<"0">>, 0},
		{<<"42    ">>, 42},
		{<<"69\t">>, 69},
		{<<"1337">>, 1337},
		{<<"1234567890">>, 1234567890},
		{<<"1234567890     ">>, 1234567890}
	],
	[{V, fun() -> R = parse_content_length(V) end} || {V, R} <- Tests].
-endif.

-ifdef(PERF).
horse_parse_content_length_zero() ->
	horse:repeat(100000,
		parse_content_length(<<"0">>)
	).

horse_parse_content_length_giga() ->
	horse:repeat(100000,
		parse_content_length(<<"1234567890">>)
	).
-endif.

%% @doc Parse the Transfer-Encoding header.
%%
%% @todo Extension parameters.

-spec parse_transfer_encoding(binary()) -> [binary()].
parse_transfer_encoding(<<"chunked">>) ->
	[<<"chunked">>];
parse_transfer_encoding(TransferEncoding) ->
	nonempty(token_ci_list(TransferEncoding, [])).

-ifdef(TEST).
parse_transfer_encoding_test_() ->
	Tests = [
		{<<"a , , , ">>, [<<"a">>]},
		{<<" , , , a">>, [<<"a">>]},
		{<<"a , , b">>, [<<"a">>, <<"b">>]},
		{<<"chunked">>, [<<"chunked">>]},
		{<<"chunked, something">>, [<<"chunked">>, <<"something">>]}
	],
	[{V, fun() -> R = parse_transfer_encoding(V) end} || {V, R} <- Tests].

parse_transfer_encoding_error_test_() ->
	Tests = [
		<<>>,
		<<" ">>,
		<<" , ">>,
		<<",,,">>,
		<<"a b">>
	],
	[{V, fun() -> {'EXIT', _} = (catch parse_transfer_encoding(V)) end}
		|| V <- Tests].
-endif.

-ifdef(PERF).
horse_parse_transfer_encoding_chunked() ->
	horse:repeat(200000,
		parse_transfer_encoding(<<"chunked">>)
	).

horse_parse_transfer_encoding_custom() ->
	horse:repeat(200000,
		parse_transfer_encoding(<<"chunked, something">>)
	).
-endif.

%% Internal.

%% Only return if the list is not empty.
nonempty(L) when L =/= [] -> L.

%% Parse a number optionally followed by whitespace.
number(<< $0, R/bits >>, Acc) -> number(R, Acc * 10);
number(<< $1, R/bits >>, Acc) -> number(R, Acc * 10 + 1);
number(<< $2, R/bits >>, Acc) -> number(R, Acc * 10 + 2);
number(<< $3, R/bits >>, Acc) -> number(R, Acc * 10 + 3);
number(<< $4, R/bits >>, Acc) -> number(R, Acc * 10 + 4);
number(<< $5, R/bits >>, Acc) -> number(R, Acc * 10 + 5);
number(<< $6, R/bits >>, Acc) -> number(R, Acc * 10 + 6);
number(<< $7, R/bits >>, Acc) -> number(R, Acc * 10 + 7);
number(<< $8, R/bits >>, Acc) -> number(R, Acc * 10 + 8);
number(<< $9, R/bits >>, Acc) -> number(R, Acc * 10 + 9);
number(<< $\s, R/bits >>, Acc) -> ws_end(R), Acc;
number(<< $\t, R/bits >>, Acc) -> ws_end(R), Acc;
number(<<>>, Acc) -> Acc.

ws_end(<< $\s, R/bits >>) -> ws_end(R);
ws_end(<< $\t, R/bits >>) -> ws_end(R);
ws_end(<<>>) -> ok.

%% Parse a list of case insensitive tokens.
token_ci_list(<<>>, Acc) -> lists:reverse(Acc);
token_ci_list(<< $\s, R/bits >>, Acc) -> token_ci_list(R, Acc);
token_ci_list(<< $\t, R/bits >>, Acc) -> token_ci_list(R, Acc);
token_ci_list(<< $,, R/bits >>, Acc) -> token_ci_list(R, Acc);
token_ci_list(<< C, R/bits >>, Acc) ->
	case C of
		?INLINE_LOWERCASE(token_ci_list, R, Acc, <<>>)
	end.

token_ci_list(<<>>, Acc, T) -> lists:reverse([T|Acc]);
token_ci_list(<< $\s, R/bits >>, Acc, T) -> token_ci_list_sep(R, Acc, T);
token_ci_list(<< $\t, R/bits >>, Acc, T) -> token_ci_list_sep(R, Acc, T);
token_ci_list(<< $,, R/bits >>, Acc, T) -> token_ci_list(R, [T|Acc]);
token_ci_list(<< C, R/bits >>, Acc, T) ->
	case C of
		?INLINE_LOWERCASE(token_ci_list, R, Acc, T)
	end.

token_ci_list_sep(<<>>, Acc, T) -> lists:reverse([T|Acc]);
token_ci_list_sep(<< $\s, R/bits >>, Acc, T) -> token_ci_list_sep(R, Acc, T);
token_ci_list_sep(<< $\t, R/bits >>, Acc, T) -> token_ci_list_sep(R, Acc, T);
token_ci_list_sep(<< $,, R/bits >>, Acc, T) -> token_ci_list(R, [T|Acc]).
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




































































































































































































































































































































































































Deleted deps/cowlib/src/cow_http_te.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
%% Copyright (c) 2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(cow_http_te).

%% Identity.
-export([stream_identity/2]).
-export([identity/1]).

%% Chunked.
-export([stream_chunked/2]).
-export([chunk/1]).
-export([last_chunk/0]).

%% The state type is the same for both identity and chunked.
-type state() :: {non_neg_integer(), non_neg_integer()}.

-type decode_ret() :: more
	| {more, Data::binary(), state()}
	| {more, Data::binary(), RemLen::non_neg_integer(), state()}
	| {more, Data::binary(), Rest::binary(), state()}
	| {done, TotalLen::non_neg_integer(), Rest::binary()}
	| {done, Data::binary(), TotalLen::non_neg_integer(), Rest::binary()}.
-export_type([decode_ret/0]).

-ifdef(EXTRA).
dripfeed(<< C, Rest/bits >>, Acc, State, F) ->
	case F(<< Acc/binary, C >>, State) of
		more ->
			dripfeed(Rest, << Acc/binary, C >>, State, F);
		{more, _, State2} ->
			dripfeed(Rest, <<>>, State2, F);
		{more, _, Length, State2} when is_integer(Length) ->
			dripfeed(Rest, <<>>, State2, F);
		{more, _, Acc2, State2} ->
			dripfeed(Rest, Acc2, State2, F);
		{done, _, <<>>} ->
			ok;
		{done, _, _, <<>>} ->
			ok
	end.
-endif.

%% Identity.

%% @doc Decode an identity stream.

-spec stream_identity(Data, State)
	-> {more, Data, Len, State} | {done, Data, Len, Data}
	when Data::binary(), State::state(), Len::non_neg_integer().
stream_identity(Data, {Streamed, Total}) ->
	Streamed2 = Streamed + byte_size(Data),
	if
		Streamed2 < Total ->
			{more, Data, Total - Streamed2, {Streamed2, Total}};
		true ->
			Size = Total - Streamed,
			<< Data2:Size/binary, Rest/bits >> = Data,
			{done, Data2, Total, Rest}
	end.

-spec identity(Data) -> Data when Data::iodata().
identity(Data) ->
	Data.

-ifdef(TEST).
stream_identity_test() ->
	{done, <<>>, 0, <<>>}
		= stream_identity(identity(<<>>), {0, 0}),
	{done, <<"\r\n">>, 2, <<>>}
		= stream_identity(identity(<<"\r\n">>), {0, 2}),
	{done, << 0:80000 >>, 10000, <<>>}
		= stream_identity(identity(<< 0:80000 >>), {0, 10000}),
	ok.

stream_identity_parts_test() ->
	{more, << 0:8000 >>, 1999, S1}
		= stream_identity(<< 0:8000 >>, {0, 2999}),
	{more, << 0:8000 >>, 999, S2}
		= stream_identity(<< 0:8000 >>, S1),
	{done, << 0:7992 >>, 2999, <<>>}
		= stream_identity(<< 0:7992 >>, S2),
	ok.
-endif.

-ifdef(PERF).
%% Using the same data as the chunked one for comparison.

horse_stream_identity() ->
	horse:repeat(10000,
		stream_identity(<<
			"4\r\n"
			"Wiki\r\n"
			"5\r\n"
			"pedia\r\n"
			"e\r\n"
			" in\r\n\r\nchunks.\r\n"
			"0\r\n"
			"\r\n">>, {0, 43})
	).

horse_stream_identity_dripfeed() ->
	horse:repeat(10000,
		dripfeed(<<
			"4\r\n"
			"Wiki\r\n"
			"5\r\n"
			"pedia\r\n"
			"e\r\n"
			" in\r\n\r\nchunks.\r\n"
			"0\r\n"
			"\r\n">>, <<>>, {0, 43}, fun stream_identity/2)
	).
-endif.

%% Chunked.

%% @doc Decode a chunked stream.

-spec stream_chunked(Data, State)
	-> more | {more, Data, State} | {more, Data, Len, State}
	| {more, Data, Data, State}
	| {done, Len, Data} | {done, Data, Len, Data}
	when Data::binary(), State::state(), Len::non_neg_integer().
stream_chunked(Data, State) ->
	stream_chunked(Data, State, <<>>).

%% New chunk.
stream_chunked(Data = << C, _/bits >>, {0, Streamed}, Acc) when C =/= $\r ->
	case chunked_len(Data, Streamed, Acc, 0) of
		{next, Rest, State, Acc2} ->
			stream_chunked(Rest, State, Acc2);
		{more, State, Acc2} ->
			{more, Acc2, Data, State};
		Ret ->
			Ret
	end;
%% Trailing \r\n before next chunk.
stream_chunked(<< "\r\n", Rest/bits >>, {2, Streamed}, Acc) ->
	stream_chunked(Rest, {0, Streamed}, Acc);
%% Trailing \r before next chunk.
stream_chunked(<< "\r" >>, {2, Streamed}, Acc) ->
	{more, Acc, {1, Streamed}};
%% Trailing \n before next chunk.
stream_chunked(<< "\n", Rest/bits >>, {1, Streamed}, Acc) ->
	stream_chunked(Rest, {0, Streamed}, Acc);
%% More data needed.
stream_chunked(<<>>, State = {Rem, _}, Acc) ->
	{more, Acc, Rem, State};
%% Chunk data.
stream_chunked(Data, {Rem, Streamed}, Acc) when Rem > 2 ->
	DataSize = byte_size(Data),
	RemSize = Rem - 2,
	case Data of
		<< Chunk:RemSize/binary, "\r\n", Rest/bits >> ->
			stream_chunked(Rest, {0, Streamed + RemSize}, << Acc/binary, Chunk/binary >>);
		<< Chunk:RemSize/binary, "\r" >> ->
			{more, << Acc/binary, Chunk/binary >>, {1, Streamed + RemSize}};
		%% Everything in Data is part of the chunk.
		_ ->
			Rem2 = Rem - DataSize,
			{more, << Acc/binary, Data/binary >>, Rem2, {Rem2, Streamed + DataSize}}
	end.

chunked_len(<< $0, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16);
chunked_len(<< $1, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 1);
chunked_len(<< $2, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 2);
chunked_len(<< $3, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 3);
chunked_len(<< $4, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 4);
chunked_len(<< $5, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 5);
chunked_len(<< $6, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 6);
chunked_len(<< $7, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 7);
chunked_len(<< $8, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 8);
chunked_len(<< $9, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 9);
chunked_len(<< $A, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 10);
chunked_len(<< $B, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 11);
chunked_len(<< $C, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 12);
chunked_len(<< $D, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 13);
chunked_len(<< $E, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 14);
chunked_len(<< $F, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 15);
chunked_len(<< $a, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 10);
chunked_len(<< $b, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 11);
chunked_len(<< $c, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 12);
chunked_len(<< $d, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 13);
chunked_len(<< $e, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 14);
chunked_len(<< $f, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 15);
%% Final chunk.
chunked_len(<< "\r\n\r\n", R/bits >>, S, <<>>, 0) -> {done, S, R};
chunked_len(<< "\r\n\r\n", R/bits >>, S, A, 0) -> {done, A, S, R};
chunked_len(_, _, _, 0) -> more;
%% Normal chunk. Add 2 to Len for the trailing \r\n.
chunked_len(<< "\r\n", R/bits >>, S, A, Len) -> {next, R, {Len + 2, S}, A};
chunked_len(<<"\r">>, _, <<>>, _) -> more;
chunked_len(<<"\r">>, S, A, _) -> {more, {0, S}, A};
chunked_len(<<>>, _, <<>>, _) -> more;
chunked_len(<<>>, S, A, _) -> {more, {0, S}, A}.

%% @doc Encode a chunk.

-spec chunk(D) -> D when D::iodata().
chunk(Data) ->
	[integer_to_list(iolist_size(Data), 16), <<"\r\n">>,
		Data, <<"\r\n">>].

%% @doc Encode the last chunk of a chunked stream.

-spec last_chunk() -> << _:40 >>.
last_chunk() ->
	<<"0\r\n\r\n">>.

-ifdef(TEST).
stream_chunked_identity_test() ->
	{done, <<"Wikipedia in\r\n\r\nchunks.">>, 23, <<>>}
		= stream_chunked(iolist_to_binary([
			chunk("Wiki"),
			chunk("pedia"),
			chunk(" in\r\n\r\nchunks."),
			last_chunk()
		]), {0, 0}),
	ok.

stream_chunked_one_pass_test() ->
	{done, 0, <<>>} = stream_chunked(<<"0\r\n\r\n">>, {0, 0}),
	{done, <<"Wikipedia in\r\n\r\nchunks.">>, 23, <<>>}
		= stream_chunked(<<
			"4\r\n"
			"Wiki\r\n"
			"5\r\n"
			"pedia\r\n"
			"e\r\n"
			" in\r\n\r\nchunks.\r\n"
			"0\r\n"
			"\r\n">>, {0, 0}),
	ok.

stream_chunked_n_passes_test() ->
	S0 = {0, 0},
	more = stream_chunked(<<"4\r">>, S0),
	{more, <<>>, 6, S1} = stream_chunked(<<"4\r\n">>, S0),
	{more, <<"Wiki">>, 0, S2} = stream_chunked(<<"Wiki\r\n">>, S1),
	{more, <<"pedia">>, <<"e\r">>, S3} = stream_chunked(<<"5\r\npedia\r\ne\r">>, S2),
	{more, <<" in\r\n\r\nchunks.">>, 2, S4} = stream_chunked(<<"e\r\n in\r\n\r\nchunks.">>, S3),
	{done, 23, <<>>} = stream_chunked(<<"\r\n0\r\n\r\n">>, S4),
	%% A few extra for coverage purposes.
	more = stream_chunked(<<"\n3">>, {1, 0}),
	{more, <<"abc">>, 2, {2, 3}} = stream_chunked(<<"\n3\r\nabc">>, {1, 0}),
	{more, <<"abc">>, {1, 3}} = stream_chunked(<<"3\r\nabc\r">>, {0, 0}),
	{more, <<"abc">>, <<"123">>, {0, 3}} = stream_chunked(<<"3\r\nabc\r\n123">>, {0, 0}),
	ok.

stream_chunked_dripfeed_test() ->
	dripfeed(<<
		"4\r\n"
		"Wiki\r\n"
		"5\r\n"
		"pedia\r\n"
		"e\r\n"
		" in\r\n\r\nchunks.\r\n"
		"0\r\n"
		"\r\n">>, <<>>, {0, 0}, fun stream_chunked/2).

do_body_to_chunks(_, <<>>, Acc) ->
	lists:reverse([<<"0\r\n\r\n">>|Acc]);
do_body_to_chunks(ChunkSize, Body, Acc) ->
	BodySize = byte_size(Body),
	ChunkSize2 = case BodySize < ChunkSize of
		true -> BodySize;
		false -> ChunkSize
	end,
	<< Chunk:ChunkSize2/binary, Rest/binary >> = Body,
	ChunkSizeBin = list_to_binary(integer_to_list(ChunkSize2, 16)),
	do_body_to_chunks(ChunkSize, Rest,
		[<< ChunkSizeBin/binary, "\r\n", Chunk/binary, "\r\n" >>|Acc]).

stream_chunked_dripfeed2_test() ->
	Body = list_to_binary(io_lib:format("~p", [lists:seq(1, 100)])),
	Body2 = iolist_to_binary(do_body_to_chunks(50, Body, [])),
	dripfeed(Body2, <<>>, {0, 0}, fun stream_chunked/2).

stream_chunked_error_test_() ->
	Tests = [
		{<<>>, undefined},
		{<<"\n\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">>, {2, 0}}
	],
	[{lists:flatten(io_lib:format("value ~p state ~p", [V, S])),
		fun() -> {'EXIT', _} = (catch stream_chunked(V, S)) end}
			|| {V, S} <- Tests].
-endif.

-ifdef(PERF).
horse_stream_chunked() ->
	horse:repeat(10000,
		stream_chunked(<<
			"4\r\n"
			"Wiki\r\n"
			"5\r\n"
			"pedia\r\n"
			"e\r\n"
			" in\r\n\r\nchunks.\r\n"
			"0\r\n"
			"\r\n">>, {0, 0})
	).

horse_stream_chunked_dripfeed() ->
	horse:repeat(10000,
		dripfeed(<<
			"4\r\n"
			"Wiki\r\n"
			"5\r\n"
			"pedia\r\n"
			"e\r\n"
			" in\r\n\r\nchunks.\r\n"
			"0\r\n"
			"\r\n">>, <<>>, {0, 43}, fun stream_chunked/2)
	).
-endif.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<














































































































































































































































































































































































































































































































































































































































































Deleted deps/cowlib/src/cow_mimetypes.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
%% Copyright (c) 2013-2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(cow_mimetypes).

-export([all/1]).
-export([web/1]).

%% @doc Return the mimetype for any file by looking at its extension.

-spec all(binary()) -> {binary(), binary(), []}.
all(Path) ->
	case filename:extension(Path) of
		<<>> -> {<<"application">>, <<"octet-stream">>, []};
		<< $., Ext/binary >> -> all_ext(Ext)
	end.

%% @doc Return the mimetype for a Web related file by looking at its extension.

-spec web(binary()) -> {binary(), binary(), []}.
web(Path) ->
	case filename:extension(Path) of
		<<>> -> {<<"application">>, <<"octet-stream">>, []};
		<< $., Ext/binary >> -> web_ext(Ext)
	end.

%% Internal.

%% GENERATED
all_ext(<<"123">>) -> {<<"application">>, <<"vnd.lotus-1-2-3">>, []};
all_ext(<<"3dml">>) -> {<<"text">>, <<"vnd.in3d.3dml">>, []};
all_ext(<<"3ds">>) -> {<<"image">>, <<"x-3ds">>, []};
all_ext(<<"3g2">>) -> {<<"video">>, <<"3gpp2">>, []};
all_ext(<<"3gp">>) -> {<<"video">>, <<"3gpp">>, []};
all_ext(<<"7z">>) -> {<<"application">>, <<"x-7z-compressed">>, []};
all_ext(<<"aab">>) -> {<<"application">>, <<"x-authorware-bin">>, []};
all_ext(<<"aac">>) -> {<<"audio">>, <<"x-aac">>, []};
all_ext(<<"aam">>) -> {<<"application">>, <<"x-authorware-map">>, []};
all_ext(<<"aas">>) -> {<<"application">>, <<"x-authorware-seg">>, []};
all_ext(<<"abw">>) -> {<<"application">>, <<"x-abiword">>, []};
all_ext(<<"ac">>) -> {<<"application">>, <<"pkix-attr-cert">>, []};
all_ext(<<"acc">>) -> {<<"application">>, <<"vnd.americandynamics.acc">>, []};
all_ext(<<"ace">>) -> {<<"application">>, <<"x-ace-compressed">>, []};
all_ext(<<"acu">>) -> {<<"application">>, <<"vnd.acucobol">>, []};
all_ext(<<"acutc">>) -> {<<"application">>, <<"vnd.acucorp">>, []};
all_ext(<<"adp">>) -> {<<"audio">>, <<"adpcm">>, []};
all_ext(<<"aep">>) -> {<<"application">>, <<"vnd.audiograph">>, []};
all_ext(<<"afm">>) -> {<<"application">>, <<"x-font-type1">>, []};
all_ext(<<"afp">>) -> {<<"application">>, <<"vnd.ibm.modcap">>, []};
all_ext(<<"ahead">>) -> {<<"application">>, <<"vnd.ahead.space">>, []};
all_ext(<<"ai">>) -> {<<"application">>, <<"postscript">>, []};
all_ext(<<"aif">>) -> {<<"audio">>, <<"x-aiff">>, []};
all_ext(<<"aifc">>) -> {<<"audio">>, <<"x-aiff">>, []};
all_ext(<<"aiff">>) -> {<<"audio">>, <<"x-aiff">>, []};
all_ext(<<"air">>) -> {<<"application">>, <<"vnd.adobe.air-application-installer-package+zip">>, []};
all_ext(<<"ait">>) -> {<<"application">>, <<"vnd.dvb.ait">>, []};
all_ext(<<"ami">>) -> {<<"application">>, <<"vnd.amiga.ami">>, []};
all_ext(<<"apk">>) -> {<<"application">>, <<"vnd.android.package-archive">>, []};
all_ext(<<"appcache">>) -> {<<"text">>, <<"cache-manifest">>, []};
all_ext(<<"application">>) -> {<<"application">>, <<"x-ms-application">>, []};
all_ext(<<"apr">>) -> {<<"application">>, <<"vnd.lotus-approach">>, []};
all_ext(<<"arc">>) -> {<<"application">>, <<"x-freearc">>, []};
all_ext(<<"asc">>) -> {<<"application">>, <<"pgp-signature">>, []};
all_ext(<<"asf">>) -> {<<"video">>, <<"x-ms-asf">>, []};
all_ext(<<"asm">>) -> {<<"text">>, <<"x-asm">>, []};
all_ext(<<"aso">>) -> {<<"application">>, <<"vnd.accpac.simply.aso">>, []};
all_ext(<<"asx">>) -> {<<"video">>, <<"x-ms-asf">>, []};
all_ext(<<"atc">>) -> {<<"application">>, <<"vnd.acucorp">>, []};
all_ext(<<"atom">>) -> {<<"application">>, <<"atom+xml">>, []};
all_ext(<<"atomcat">>) -> {<<"application">>, <<"atomcat+xml">>, []};
all_ext(<<"atomsvc">>) -> {<<"application">>, <<"atomsvc+xml">>, []};
all_ext(<<"atx">>) -> {<<"application">>, <<"vnd.antix.game-component">>, []};
all_ext(<<"au">>) -> {<<"audio">>, <<"basic">>, []};
all_ext(<<"avi">>) -> {<<"video">>, <<"x-msvideo">>, []};
all_ext(<<"aw">>) -> {<<"application">>, <<"applixware">>, []};
all_ext(<<"azf">>) -> {<<"application">>, <<"vnd.airzip.filesecure.azf">>, []};
all_ext(<<"azs">>) -> {<<"application">>, <<"vnd.airzip.filesecure.azs">>, []};
all_ext(<<"azw">>) -> {<<"application">>, <<"vnd.amazon.ebook">>, []};
all_ext(<<"bat">>) -> {<<"application">>, <<"x-msdownload">>, []};
all_ext(<<"bcpio">>) -> {<<"application">>, <<"x-bcpio">>, []};
all_ext(<<"bdf">>) -> {<<"application">>, <<"x-font-bdf">>, []};
all_ext(<<"bdm">>) -> {<<"application">>, <<"vnd.syncml.dm+wbxml">>, []};
all_ext(<<"bed">>) -> {<<"application">>, <<"vnd.realvnc.bed">>, []};
all_ext(<<"bh2">>) -> {<<"application">>, <<"vnd.fujitsu.oasysprs">>, []};
all_ext(<<"bin">>) -> {<<"application">>, <<"octet-stream">>, []};
all_ext(<<"blb">>) -> {<<"application">>, <<"x-blorb">>, []};
all_ext(<<"blorb">>) -> {<<"application">>, <<"x-blorb">>, []};
all_ext(<<"bmi">>) -> {<<"application">>, <<"vnd.bmi">>, []};
all_ext(<<"bmp">>) -> {<<"image">>, <<"bmp">>, []};
all_ext(<<"book">>) -> {<<"application">>, <<"vnd.framemaker">>, []};
all_ext(<<"box">>) -> {<<"application">>, <<"vnd.previewsystems.box">>, []};
all_ext(<<"boz">>) -> {<<"application">>, <<"x-bzip2">>, []};
all_ext(<<"bpk">>) -> {<<"application">>, <<"octet-stream">>, []};
all_ext(<<"btif">>) -> {<<"image">>, <<"prs.btif">>, []};
all_ext(<<"bz2">>) -> {<<"application">>, <<"x-bzip2">>, []};
all_ext(<<"bz">>) -> {<<"application">>, <<"x-bzip">>, []};
all_ext(<<"c11amc">>) -> {<<"application">>, <<"vnd.cluetrust.cartomobile-config">>, []};
all_ext(<<"c11amz">>) -> {<<"application">>, <<"vnd.cluetrust.cartomobile-config-pkg">>, []};
all_ext(<<"c4d">>) -> {<<"application">>, <<"vnd.clonk.c4group">>, []};
all_ext(<<"c4f">>) -> {<<"application">>, <<"vnd.clonk.c4group">>, []};
all_ext(<<"c4g">>) -> {<<"application">>, <<"vnd.clonk.c4group">>, []};
all_ext(<<"c4p">>) -> {<<"application">>, <<"vnd.clonk.c4group">>, []};
all_ext(<<"c4u">>) -> {<<"application">>, <<"vnd.clonk.c4group">>, []};
all_ext(<<"cab">>) -> {<<"application">>, <<"vnd.ms-cab-compressed">>, []};
all_ext(<<"caf">>) -> {<<"audio">>, <<"x-caf">>, []};
all_ext(<<"cap">>) -> {<<"application">>, <<"vnd.tcpdump.pcap">>, []};
all_ext(<<"car">>) -> {<<"application">>, <<"vnd.curl.car">>, []};
all_ext(<<"cat">>) -> {<<"application">>, <<"vnd.ms-pki.seccat">>, []};
all_ext(<<"cb7">>) -> {<<"application">>, <<"x-cbr">>, []};
all_ext(<<"cba">>) -> {<<"application">>, <<"x-cbr">>, []};
all_ext(<<"cbr">>) -> {<<"application">>, <<"x-cbr">>, []};
all_ext(<<"cbt">>) -> {<<"application">>, <<"x-cbr">>, []};
all_ext(<<"cbz">>) -> {<<"application">>, <<"x-cbr">>, []};
all_ext(<<"cct">>) -> {<<"application">>, <<"x-director">>, []};
all_ext(<<"cc">>) -> {<<"text">>, <<"x-c">>, []};
all_ext(<<"ccxml">>) -> {<<"application">>, <<"ccxml+xml">>, []};
all_ext(<<"cdbcmsg">>) -> {<<"application">>, <<"vnd.contact.cmsg">>, []};
all_ext(<<"cdf">>) -> {<<"application">>, <<"x-netcdf">>, []};
all_ext(<<"cdkey">>) -> {<<"application">>, <<"vnd.mediastation.cdkey">>, []};
all_ext(<<"cdmia">>) -> {<<"application">>, <<"cdmi-capability">>, []};
all_ext(<<"cdmic">>) -> {<<"application">>, <<"cdmi-container">>, []};
all_ext(<<"cdmid">>) -> {<<"application">>, <<"cdmi-domain">>, []};
all_ext(<<"cdmio">>) -> {<<"application">>, <<"cdmi-object">>, []};
all_ext(<<"cdmiq">>) -> {<<"application">>, <<"cdmi-queue">>, []};
all_ext(<<"cdx">>) -> {<<"chemical">>, <<"x-cdx">>, []};
all_ext(<<"cdxml">>) -> {<<"application">>, <<"vnd.chemdraw+xml">>, []};
all_ext(<<"cdy">>) -> {<<"application">>, <<"vnd.cinderella">>, []};
all_ext(<<"cer">>) -> {<<"application">>, <<"pkix-cert">>, []};
all_ext(<<"cfs">>) -> {<<"application">>, <<"x-cfs-compressed">>, []};
all_ext(<<"cgm">>) -> {<<"image">>, <<"cgm">>, []};
all_ext(<<"chat">>) -> {<<"application">>, <<"x-chat">>, []};
all_ext(<<"chm">>) -> {<<"application">>, <<"vnd.ms-htmlhelp">>, []};
all_ext(<<"chrt">>) -> {<<"application">>, <<"vnd.kde.kchart">>, []};
all_ext(<<"cif">>) -> {<<"chemical">>, <<"x-cif">>, []};
all_ext(<<"cii">>) -> {<<"application">>, <<"vnd.anser-web-certificate-issue-initiation">>, []};
all_ext(<<"cil">>) -> {<<"application">>, <<"vnd.ms-artgalry">>, []};
all_ext(<<"cla">>) -> {<<"application">>, <<"vnd.claymore">>, []};
all_ext(<<"class">>) -> {<<"application">>, <<"java-vm">>, []};
all_ext(<<"clkk">>) -> {<<"application">>, <<"vnd.crick.clicker.keyboard">>, []};
all_ext(<<"clkp">>) -> {<<"application">>, <<"vnd.crick.clicker.palette">>, []};
all_ext(<<"clkt">>) -> {<<"application">>, <<"vnd.crick.clicker.template">>, []};
all_ext(<<"clkw">>) -> {<<"application">>, <<"vnd.crick.clicker.wordbank">>, []};
all_ext(<<"clkx">>) -> {<<"application">>, <<"vnd.crick.clicker">>, []};
all_ext(<<"clp">>) -> {<<"application">>, <<"x-msclip">>, []};
all_ext(<<"cmc">>) -> {<<"application">>, <<"vnd.cosmocaller">>, []};
all_ext(<<"cmdf">>) -> {<<"chemical">>, <<"x-cmdf">>, []};
all_ext(<<"cml">>) -> {<<"chemical">>, <<"x-cml">>, []};
all_ext(<<"cmp">>) -> {<<"application">>, <<"vnd.yellowriver-custom-menu">>, []};
all_ext(<<"cmx">>) -> {<<"image">>, <<"x-cmx">>, []};
all_ext(<<"cod">>) -> {<<"application">>, <<"vnd.rim.cod">>, []};
all_ext(<<"com">>) -> {<<"application">>, <<"x-msdownload">>, []};
all_ext(<<"conf">>) -> {<<"text">>, <<"plain">>, []};
all_ext(<<"cpio">>) -> {<<"application">>, <<"x-cpio">>, []};
all_ext(<<"cpp">>) -> {<<"text">>, <<"x-c">>, []};
all_ext(<<"cpt">>) -> {<<"application">>, <<"mac-compactpro">>, []};
all_ext(<<"crd">>) -> {<<"application">>, <<"x-mscardfile">>, []};
all_ext(<<"crl">>) -> {<<"application">>, <<"pkix-crl">>, []};
all_ext(<<"crt">>) -> {<<"application">>, <<"x-x509-ca-cert">>, []};
all_ext(<<"cryptonote">>) -> {<<"application">>, <<"vnd.rig.cryptonote">>, []};
all_ext(<<"csh">>) -> {<<"application">>, <<"x-csh">>, []};
all_ext(<<"csml">>) -> {<<"chemical">>, <<"x-csml">>, []};
all_ext(<<"csp">>) -> {<<"application">>, <<"vnd.commonspace">>, []};
all_ext(<<"css">>) -> {<<"text">>, <<"css">>, []};
all_ext(<<"cst">>) -> {<<"application">>, <<"x-director">>, []};
all_ext(<<"csv">>) -> {<<"text">>, <<"csv">>, []};
all_ext(<<"c">>) -> {<<"text">>, <<"x-c">>, []};
all_ext(<<"cu">>) -> {<<"application">>, <<"cu-seeme">>, []};
all_ext(<<"curl">>) -> {<<"text">>, <<"vnd.curl">>, []};
all_ext(<<"cww">>) -> {<<"application">>, <<"prs.cww">>, []};
all_ext(<<"cxt">>) -> {<<"application">>, <<"x-director">>, []};
all_ext(<<"cxx">>) -> {<<"text">>, <<"x-c">>, []};
all_ext(<<"dae">>) -> {<<"model">>, <<"vnd.collada+xml">>, []};
all_ext(<<"daf">>) -> {<<"application">>, <<"vnd.mobius.daf">>, []};
all_ext(<<"dart">>) -> {<<"application">>, <<"vnd.dart">>, []};
all_ext(<<"dataless">>) -> {<<"application">>, <<"vnd.fdsn.seed">>, []};
all_ext(<<"davmount">>) -> {<<"application">>, <<"davmount+xml">>, []};
all_ext(<<"dbk">>) -> {<<"application">>, <<"docbook+xml">>, []};
all_ext(<<"dcr">>) -> {<<"application">>, <<"x-director">>, []};
all_ext(<<"dcurl">>) -> {<<"text">>, <<"vnd.curl.dcurl">>, []};
all_ext(<<"dd2">>) -> {<<"application">>, <<"vnd.oma.dd2+xml">>, []};
all_ext(<<"ddd">>) -> {<<"application">>, <<"vnd.fujixerox.ddd">>, []};
all_ext(<<"deb">>) -> {<<"application">>, <<"x-debian-package">>, []};
all_ext(<<"def">>) -> {<<"text">>, <<"plain">>, []};
all_ext(<<"deploy">>) -> {<<"application">>, <<"octet-stream">>, []};
all_ext(<<"der">>) -> {<<"application">>, <<"x-x509-ca-cert">>, []};
all_ext(<<"dfac">>) -> {<<"application">>, <<"vnd.dreamfactory">>, []};
all_ext(<<"dgc">>) -> {<<"application">>, <<"x-dgc-compressed">>, []};
all_ext(<<"dic">>) -> {<<"text">>, <<"x-c">>, []};
all_ext(<<"dir">>) -> {<<"application">>, <<"x-director">>, []};
all_ext(<<"dis">>) -> {<<"application">>, <<"vnd.mobius.dis">>, []};
all_ext(<<"dist">>) -> {<<"application">>, <<"octet-stream">>, []};
all_ext(<<"distz">>) -> {<<"application">>, <<"octet-stream">>, []};
all_ext(<<"djv">>) -> {<<"image">>, <<"vnd.djvu">>, []};
all_ext(<<"djvu">>) -> {<<"image">>, <<"vnd.djvu">>, []};
all_ext(<<"dll">>) -> {<<"application">>, <<"x-msdownload">>, []};
all_ext(<<"dmg">>) -> {<<"application">>, <<"x-apple-diskimage">>, []};
all_ext(<<"dmp">>) -> {<<"application">>, <<"vnd.tcpdump.pcap">>, []};
all_ext(<<"dms">>) -> {<<"application">>, <<"octet-stream">>, []};
all_ext(<<"dna">>) -> {<<"application">>, <<"vnd.dna">>, []};
all_ext(<<"doc">>) -> {<<"application">>, <<"msword">>, []};
all_ext(<<"docm">>) -> {<<"application">>, <<"vnd.ms-word.document.macroenabled.12">>, []};
all_ext(<<"docx">>) -> {<<"application">>, <<"vnd.openxmlformats-officedocument.wordprocessingml.document">>, []};
all_ext(<<"dot">>) -> {<<"application">>, <<"msword">>, []};
all_ext(<<"dotm">>) -> {<<"application">>, <<"vnd.ms-word.template.macroenabled.12">>, []};
all_ext(<<"dotx">>) -> {<<"application">>, <<"vnd.openxmlformats-officedocument.wordprocessingml.template">>, []};
all_ext(<<"dp">>) -> {<<"application">>, <<"vnd.osgi.dp">>, []};
all_ext(<<"dpg">>) -> {<<"application">>, <<"vnd.dpgraph">>, []};
all_ext(<<"dra">>) -> {<<"audio">>, <<"vnd.dra">>, []};
all_ext(<<"dsc">>) -> {<<"text">>, <<"prs.lines.tag">>, []};
all_ext(<<"dssc">>) -> {<<"application">>, <<"dssc+der">>, []};
all_ext(<<"dtb">>) -> {<<"application">>, <<"x-dtbook+xml">>, []};
all_ext(<<"dtd">>) -> {<<"application">>, <<"xml-dtd">>, []};
all_ext(<<"dts">>) -> {<<"audio">>, <<"vnd.dts">>, []};
all_ext(<<"dtshd">>) -> {<<"audio">>, <<"vnd.dts.hd">>, []};
all_ext(<<"dump">>) -> {<<"application">>, <<"octet-stream">>, []};
all_ext(<<"dvb">>) -> {<<"video">>, <<"vnd.dvb.file">>, []};
all_ext(<<"dvi">>) -> {<<"application">>, <<"x-dvi">>, []};
all_ext(<<"dwf">>) -> {<<"model">>, <<"vnd.dwf">>, []};
all_ext(<<"dwg">>) -> {<<"image">>, <<"vnd.dwg">>, []};
all_ext(<<"dxf">>) -> {<<"image">>, <<"vnd.dxf">>, []};
all_ext(<<"dxp">>) -> {<<"application">>, <<"vnd.spotfire.dxp">>, []};
all_ext(<<"dxr">>) -> {<<"application">>, <<"x-director">>, []};
all_ext(<<"ecelp4800">>) -> {<<"audio">>, <<"vnd.nuera.ecelp4800">>, []};
all_ext(<<"ecelp7470">>) -> {<<"audio">>, <<"vnd.nuera.ecelp7470">>, []};
all_ext(<<"ecelp9600">>) -> {<<"audio">>, <<"vnd.nuera.ecelp9600">>, []};
all_ext(<<"ecma">>) -> {<<"application">>, <<"ecmascript">>, []};
all_ext(<<"edm">>) -> {<<"application">>, <<"vnd.novadigm.edm">>, []};
all_ext(<<"edx">>) -> {<<"application">>, <<"vnd.novadigm.edx">>, []};
all_ext(<<"efif">>) -> {<<"application">>, <<"vnd.picsel">>, []};
all_ext(<<"ei6">>) -> {<<"application">>, <<"vnd.pg.osasli">>, []};
all_ext(<<"elc">>) -> {<<"application">>, <<"octet-stream">>, []};
all_ext(<<"emf">>) -> {<<"application">>, <<"x-msmetafile">>, []};
all_ext(<<"eml">>) -> {<<"message">>, <<"rfc822">>, []};
all_ext(<<"emma">>) -> {<<"application">>, <<"emma+xml">>, []};
all_ext(<<"emz">>) -> {<<"application">>, <<"x-msmetafile">>, []};
all_ext(<<"eol">>) -> {<<"audio">>, <<"vnd.digital-winds">>, []};
all_ext(<<"eot">>) -> {<<"application">>, <<"vnd.ms-fontobject">>, []};
all_ext(<<"eps">>) -> {<<"application">>, <<"postscript">>, []};
all_ext(<<"epub">>) -> {<<"application">>, <<"epub+zip">>, []};
all_ext(<<"es3">>) -> {<<"application">>, <<"vnd.eszigno3+xml">>, []};
all_ext(<<"esa">>) -> {<<"application">>, <<"vnd.osgi.subsystem">>, []};
all_ext(<<"esf">>) -> {<<"application">>, <<"vnd.epson.esf">>, []};
all_ext(<<"et3">>) -> {<<"application">>, <<"vnd.eszigno3+xml">>, []};
all_ext(<<"etx">>) -> {<<"text">>, <<"x-setext">>, []};
all_ext(<<"eva">>) -> {<<"application">>, <<"x-eva">>, []};
all_ext(<<"evy">>) -> {<<"application">>, <<"x-envoy">>, []};
all_ext(<<"exe">>) -> {<<"application">>, <<"x-msdownload">>, []};
all_ext(<<"exi">>) -> {<<"application">>, <<"exi">>, []};
all_ext(<<"ext">>) -> {<<"application">>, <<"vnd.novadigm.ext">>, []};
all_ext(<<"ez2">>) -> {<<"application">>, <<"vnd.ezpix-album">>, []};
all_ext(<<"ez3">>) -> {<<"application">>, <<"vnd.ezpix-package">>, []};
all_ext(<<"ez">>) -> {<<"application">>, <<"andrew-inset">>, []};
all_ext(<<"f4v">>) -> {<<"video">>, <<"x-f4v">>, []};
all_ext(<<"f77">>) -> {<<"text">>, <<"x-fortran">>, []};
all_ext(<<"f90">>) -> {<<"text">>, <<"x-fortran">>, []};
all_ext(<<"fbs">>) -> {<<"image">>, <<"vnd.fastbidsheet">>, []};
all_ext(<<"fcdt">>) -> {<<"application">>, <<"vnd.adobe.formscentral.fcdt">>, []};
all_ext(<<"fcs">>) -> {<<"application">>, <<"vnd.isac.fcs">>, []};
all_ext(<<"fdf">>) -> {<<"application">>, <<"vnd.fdf">>, []};
all_ext(<<"fe_launch">>) -> {<<"application">>, <<"vnd.denovo.fcselayout-link">>, []};
all_ext(<<"fg5">>) -> {<<"application">>, <<"vnd.fujitsu.oasysgp">>, []};
all_ext(<<"fgd">>) -> {<<"application">>, <<"x-director">>, []};
all_ext(<<"fh4">>) -> {<<"image">>, <<"x-freehand">>, []};
all_ext(<<"fh5">>) -> {<<"image">>, <<"x-freehand">>, []};
all_ext(<<"fh7">>) -> {<<"image">>, <<"x-freehand">>, []};
all_ext(<<"fhc">>) -> {<<"image">>, <<"x-freehand">>, []};
all_ext(<<"fh">>) -> {<<"image">>, <<"x-freehand">>, []};
all_ext(<<"fig">>) -> {<<"application">>, <<"x-xfig">>, []};
all_ext(<<"flac">>) -> {<<"audio">>, <<"x-flac">>, []};
all_ext(<<"fli">>) -> {<<"video">>, <<"x-fli">>, []};
all_ext(<<"flo">>) -> {<<"application">>, <<"vnd.micrografx.flo">>, []};
all_ext(<<"flv">>) -> {<<"video">>, <<"x-flv">>, []};
all_ext(<<"flw">>) -> {<<"application">>, <<"vnd.kde.kivio">>, []};
all_ext(<<"flx">>) -> {<<"text">>, <<"vnd.fmi.flexstor">>, []};
all_ext(<<"fly">>) -> {<<"text">>, <<"vnd.fly">>, []};
all_ext(<<"fm">>) -> {<<"application">>, <<"vnd.framemaker">>, []};
all_ext(<<"fnc">>) -> {<<"application">>, <<"vnd.frogans.fnc">>, []};
all_ext(<<"for">>) -> {<<"text">>, <<"x-fortran">>, []};
all_ext(<<"fpx">>) -> {<<"image">>, <<"vnd.fpx">>, []};
all_ext(<<"frame">>) -> {<<"application">>, <<"vnd.framemaker">>, []};
all_ext(<<"fsc">>) -> {<<"application">>, <<"vnd.fsc.weblaunch">>, []};
all_ext(<<"fst">>) -> {<<"image">>, <<"vnd.fst">>, []};
all_ext(<<"ftc">>) -> {<<"application">>, <<"vnd.fluxtime.clip">>, []};
all_ext(<<"f">>) -> {<<"text">>, <<"x-fortran">>, []};
all_ext(<<"fti">>) -> {<<"application">>, <<"vnd.anser-web-funds-transfer-initiation">>, []};
all_ext(<<"fvt">>) -> {<<"video">>, <<"vnd.fvt">>, []};
all_ext(<<"fxp">>) -> {<<"application">>, <<"vnd.adobe.fxp">>, []};
all_ext(<<"fxpl">>) -> {<<"application">>, <<"vnd.adobe.fxp">>, []};
all_ext(<<"fzs">>) -> {<<"application">>, <<"vnd.fuzzysheet">>, []};
all_ext(<<"g2w">>) -> {<<"application">>, <<"vnd.geoplan">>, []};
all_ext(<<"g3">>) -> {<<"image">>, <<"g3fax">>, []};
all_ext(<<"g3w">>) -> {<<"application">>, <<"vnd.geospace">>, []};
all_ext(<<"gac">>) -> {<<"application">>, <<"vnd.groove-account">>, []};
all_ext(<<"gam">>) -> {<<"application">>, <<"x-tads">>, []};
all_ext(<<"gbr">>) -> {<<"application">>, <<"rpki-ghostbusters">>, []};
all_ext(<<"gca">>) -> {<<"application">>, <<"x-gca-compressed">>, []};
all_ext(<<"gdl">>) -> {<<"model">>, <<"vnd.gdl">>, []};
all_ext(<<"geo">>) -> {<<"application">>, <<"vnd.dynageo">>, []};
all_ext(<<"gex">>) -> {<<"application">>, <<"vnd.geometry-explorer">>, []};
all_ext(<<"ggb">>) -> {<<"application">>, <<"vnd.geogebra.file">>, []};
all_ext(<<"ggt">>) -> {<<"application">>, <<"vnd.geogebra.tool">>, []};
all_ext(<<"ghf">>) -> {<<"application">>, <<"vnd.groove-help">>, []};
all_ext(<<"gif">>) -> {<<"image">>, <<"gif">>, []};
all_ext(<<"gim">>) -> {<<"application">>, <<"vnd.groove-identity-message">>, []};
all_ext(<<"gml">>) -> {<<"application">>, <<"gml+xml">>, []};
all_ext(<<"gmx">>) -> {<<"application">>, <<"vnd.gmx">>, []};
all_ext(<<"gnumeric">>) -> {<<"application">>, <<"x-gnumeric">>, []};
all_ext(<<"gph">>) -> {<<"application">>, <<"vnd.flographit">>, []};
all_ext(<<"gpx">>) -> {<<"application">>, <<"gpx+xml">>, []};
all_ext(<<"gqf">>) -> {<<"application">>, <<"vnd.grafeq">>, []};
all_ext(<<"gqs">>) -> {<<"application">>, <<"vnd.grafeq">>, []};
all_ext(<<"gram">>) -> {<<"application">>, <<"srgs">>, []};
all_ext(<<"gramps">>) -> {<<"application">>, <<"x-gramps-xml">>, []};
all_ext(<<"gre">>) -> {<<"application">>, <<"vnd.geometry-explorer">>, []};
all_ext(<<"grv">>) -> {<<"application">>, <<"vnd.groove-injector">>, []};
all_ext(<<"grxml">>) -> {<<"application">>, <<"srgs+xml">>, []};
all_ext(<<"gsf">>) -> {<<"application">>, <<"x-font-ghostscript">>, []};
all_ext(<<"gtar">>) -> {<<"application">>, <<"x-gtar">>, []};
all_ext(<<"gtm">>) -> {<<"application">>, <<"vnd.groove-tool-message">>, []};
all_ext(<<"gtw">>) -> {<<"model">>, <<"vnd.gtw">>, []};
all_ext(<<"gv">>) -> {<<"text">>, <<"vnd.graphviz">>, []};
all_ext(<<"gxf">>) -> {<<"application">>, <<"gxf">>, []};
all_ext(<<"gxt">>) -> {<<"application">>, <<"vnd.geonext">>, []};
all_ext(<<"h261">>) -> {<<"video">>, <<"h261">>, []};
all_ext(<<"h263">>) -> {<<"video">>, <<"h263">>, []};
all_ext(<<"h264">>) -> {<<"video">>, <<"h264">>, []};
all_ext(<<"hal">>) -> {<<"application">>, <<"vnd.hal+xml">>, []};
all_ext(<<"hbci">>) -> {<<"application">>, <<"vnd.hbci">>, []};
all_ext(<<"hdf">>) -> {<<"application">>, <<"x-hdf">>, []};
all_ext(<<"hh">>) -> {<<"text">>, <<"x-c">>, []};
all_ext(<<"hlp">>) -> {<<"application">>, <<"winhlp">>, []};
all_ext(<<"hpgl">>) -> {<<"application">>, <<"vnd.hp-hpgl">>, []};
all_ext(<<"hpid">>) -> {<<"application">>, <<"vnd.hp-hpid">>, []};
all_ext(<<"hps">>) -> {<<"application">>, <<"vnd.hp-hps">>, []};
all_ext(<<"hqx">>) -> {<<"application">>, <<"mac-binhex40">>, []};
all_ext(<<"h">>) -> {<<"text">>, <<"x-c">>, []};
all_ext(<<"htke">>) -> {<<"application">>, <<"vnd.kenameaapp">>, []};
all_ext(<<"html">>) -> {<<"text">>, <<"html">>, []};
all_ext(<<"htm">>) -> {<<"text">>, <<"html">>, []};
all_ext(<<"hvd">>) -> {<<"application">>, <<"vnd.yamaha.hv-dic">>, []};
all_ext(<<"hvp">>) -> {<<"application">>, <<"vnd.yamaha.hv-voice">>, []};
all_ext(<<"hvs">>) -> {<<"application">>, <<"vnd.yamaha.hv-script">>, []};
all_ext(<<"i2g">>) -> {<<"application">>, <<"vnd.intergeo">>, []};
all_ext(<<"icc">>) -> {<<"application">>, <<"vnd.iccprofile">>, []};
all_ext(<<"ice">>) -> {<<"x-conference">>, <<"x-cooltalk">>, []};
all_ext(<<"icm">>) -> {<<"application">>, <<"vnd.iccprofile">>, []};
all_ext(<<"ico">>) -> {<<"image">>, <<"x-icon">>, []};
all_ext(<<"ics">>) -> {<<"text">>, <<"calendar">>, []};
all_ext(<<"ief">>) -> {<<"image">>, <<"ief">>, []};
all_ext(<<"ifb">>) -> {<<"text">>, <<"calendar">>, []};
all_ext(<<"ifm">>) -> {<<"application">>, <<"vnd.shana.informed.formdata">>, []};
all_ext(<<"iges">>) -> {<<"model">>, <<"iges">>, []};
all_ext(<<"igl">>) -> {<<"application">>, <<"vnd.igloader">>, []};
all_ext(<<"igm">>) -> {<<"application">>, <<"vnd.insors.igm">>, []};
all_ext(<<"igs">>) -> {<<"model">>, <<"iges">>, []};
all_ext(<<"igx">>) -> {<<"application">>, <<"vnd.micrografx.igx">>, []};
all_ext(<<"iif">>) -> {<<"application">>, <<"vnd.shana.informed.interchange">>, []};
all_ext(<<"imp">>) -> {<<"application">>, <<"vnd.accpac.simply.imp">>, []};
all_ext(<<"ims">>) -> {<<"application">>, <<"vnd.ms-ims">>, []};
all_ext(<<"ink">>) -> {<<"application">>, <<"inkml+xml">>, []};
all_ext(<<"inkml">>) -> {<<"application">>, <<"inkml+xml">>, []};
all_ext(<<"install">>) -> {<<"application">>, <<"x-install-instructions">>, []};
all_ext(<<"in">>) -> {<<"text">>, <<"plain">>, []};
all_ext(<<"iota">>) -> {<<"application">>, <<"vnd.astraea-software.iota">>, []};
all_ext(<<"ipfix">>) -> {<<"application">>, <<"ipfix">>, []};
all_ext(<<"ipk">>) -> {<<"application">>, <<"vnd.shana.informed.package">>, []};
all_ext(<<"irm">>) -> {<<"application">>, <<"vnd.ibm.rights-management">>, []};
all_ext(<<"irp">>) -> {<<"application">>, <<"vnd.irepository.package+xml">>, []};
all_ext(<<"iso">>) -> {<<"application">>, <<"x-iso9660-image">>, []};
all_ext(<<"itp">>) -> {<<"application">>, <<"vnd.shana.informed.formtemplate">>, []};
all_ext(<<"ivp">>) -> {<<"application">>, <<"vnd.immervision-ivp">>, []};
all_ext(<<"ivu">>) -> {<<"application">>, <<"vnd.immervision-ivu">>, []};
all_ext(<<"jad">>) -> {<<"text">>, <<"vnd.sun.j2me.app-descriptor">>, []};
all_ext(<<"jam">>) -> {<<"application">>, <<"vnd.jam">>, []};
all_ext(<<"jar">>) -> {<<"application">>, <<"java-archive">>, []};
all_ext(<<"java">>) -> {<<"text">>, <<"x-java-source">>, []};
all_ext(<<"jisp">>) -> {<<"application">>, <<"vnd.jisp">>, []};
all_ext(<<"jlt">>) -> {<<"application">>, <<"vnd.hp-jlyt">>, []};
all_ext(<<"jnlp">>) -> {<<"application">>, <<"x-java-jnlp-file">>, []};
all_ext(<<"joda">>) -> {<<"application">>, <<"vnd.joost.joda-archive">>, []};
all_ext(<<"jpeg">>) -> {<<"image">>, <<"jpeg">>, []};
all_ext(<<"jpe">>) -> {<<"image">>, <<"jpeg">>, []};
all_ext(<<"jpg">>) -> {<<"image">>, <<"jpeg">>, []};
all_ext(<<"jpgm">>) -> {<<"video">>, <<"jpm">>, []};
all_ext(<<"jpgv">>) -> {<<"video">>, <<"jpeg">>, []};
all_ext(<<"jpm">>) -> {<<"video">>, <<"jpm">>, []};
all_ext(<<"js">>) -> {<<"application">>, <<"javascript">>, []};
all_ext(<<"json">>) -> {<<"application">>, <<"json">>, []};
all_ext(<<"jsonml">>) -> {<<"application">>, <<"jsonml+json">>, []};
all_ext(<<"kar">>) -> {<<"audio">>, <<"midi">>, []};
all_ext(<<"karbon">>) -> {<<"application">>, <<"vnd.kde.karbon">>, []};
all_ext(<<"kfo">>) -> {<<"application">>, <<"vnd.kde.kformula">>, []};
all_ext(<<"kia">>) -> {<<"application">>, <<"vnd.kidspiration">>, []};
all_ext(<<"kml">>) -> {<<"application">>, <<"vnd.google-earth.kml+xml">>, []};
all_ext(<<"kmz">>) -> {<<"application">>, <<"vnd.google-earth.kmz">>, []};
all_ext(<<"kne">>) -> {<<"application">>, <<"vnd.kinar">>, []};
all_ext(<<"knp">>) -> {<<"application">>, <<"vnd.kinar">>, []};
all_ext(<<"kon">>) -> {<<"application">>, <<"vnd.kde.kontour">>, []};
all_ext(<<"kpr">>) -> {<<"application">>, <<"vnd.kde.kpresenter">>, []};
all_ext(<<"kpt">>) -> {<<"application">>, <<"vnd.kde.kpresenter">>, []};
all_ext(<<"kpxx">>) -> {<<"application">>, <<"vnd.ds-keypoint">>, []};
all_ext(<<"ksp">>) -> {<<"application">>, <<"vnd.kde.kspread">>, []};
all_ext(<<"ktr">>) -> {<<"application">>, <<"vnd.kahootz">>, []};
all_ext(<<"ktx">>) -> {<<"image">>, <<"ktx">>, []};
all_ext(<<"ktz">>) -> {<<"application">>, <<"vnd.kahootz">>, []};
all_ext(<<"kwd">>) -> {<<"application">>, <<"vnd.kde.kword">>, []};
all_ext(<<"kwt">>) -> {<<"application">>, <<"vnd.kde.kword">>, []};
all_ext(<<"lasxml">>) -> {<<"application">>, <<"vnd.las.las+xml">>, []};
all_ext(<<"latex">>) -> {<<"application">>, <<"x-latex">>, []};
all_ext(<<"lbd">>) -> {<<"application">>, <<"vnd.llamagraphics.life-balance.desktop">>, []};
all_ext(<<"lbe">>) -> {<<"application">>, <<"vnd.llamagraphics.life-balance.exchange+xml">>, []};
all_ext(<<"les">>) -> {<<"application">>, <<"vnd.hhe.lesson-player">>, []};
all_ext(<<"lha">>) -> {<<"application">>, <<"x-lzh-compressed">>, []};
all_ext(<<"link66">>) -> {<<"application">>, <<"vnd.route66.link66+xml">>, []};
all_ext(<<"list3820">>) -> {<<"application">>, <<"vnd.ibm.modcap">>, []};
all_ext(<<"listafp">>) -> {<<"application">>, <<"vnd.ibm.modcap">>, []};
all_ext(<<"list">>) -> {<<"text">>, <<"plain">>, []};
all_ext(<<"lnk">>) -> {<<"application">>, <<"x-ms-shortcut">>, []};
all_ext(<<"log">>) -> {<<"text">>, <<"plain">>, []};
all_ext(<<"lostxml">>) -> {<<"application">>, <<"lost+xml">>, []};
all_ext(<<"lrf">>) -> {<<"application">>, <<"octet-stream">>, []};
all_ext(<<"lrm">>) -> {<<"application">>, <<"vnd.ms-lrm">>, []};
all_ext(<<"ltf">>) -> {<<"application">>, <<"vnd.frogans.ltf">>, []};
all_ext(<<"lvp">>) -> {<<"audio">>, <<"vnd.lucent.voice">>, []};
all_ext(<<"lwp">>) -> {<<"application">>, <<"vnd.lotus-wordpro">>, []};
all_ext(<<"lzh">>) -> {<<"application">>, <<"x-lzh-compressed">>, []};
all_ext(<<"m13">>) -> {<<"application">>, <<"x-msmediaview">>, []};
all_ext(<<"m14">>) -> {<<"application">>, <<"x-msmediaview">>, []};
all_ext(<<"m1v">>) -> {<<"video">>, <<"mpeg">>, []};
all_ext(<<"m21">>) -> {<<"application">>, <<"mp21">>, []};
all_ext(<<"m2a">>) -> {<<"audio">>, <<"mpeg">>, []};
all_ext(<<"m2v">>) -> {<<"video">>, <<"mpeg">>, []};
all_ext(<<"m3a">>) -> {<<"audio">>, <<"mpeg">>, []};
all_ext(<<"m3u8">>) -> {<<"application">>, <<"vnd.apple.mpegurl">>, []};
all_ext(<<"m3u">>) -> {<<"audio">>, <<"x-mpegurl">>, []};
all_ext(<<"m4u">>) -> {<<"video">>, <<"vnd.mpegurl">>, []};
all_ext(<<"m4v">>) -> {<<"video">>, <<"x-m4v">>, []};
all_ext(<<"ma">>) -> {<<"application">>, <<"mathematica">>, []};
all_ext(<<"mads">>) -> {<<"application">>, <<"mads+xml">>, []};
all_ext(<<"mag">>) -> {<<"application">>, <<"vnd.ecowin.chart">>, []};
all_ext(<<"maker">>) -> {<<"application">>, <<"vnd.framemaker">>, []};
all_ext(<<"man">>) -> {<<"text">>, <<"troff">>, []};
all_ext(<<"mar">>) -> {<<"application">>, <<"octet-stream">>, []};
all_ext(<<"mathml">>) -> {<<"application">>, <<"mathml+xml">>, []};
all_ext(<<"mb">>) -> {<<"application">>, <<"mathematica">>, []};
all_ext(<<"mbk">>) -> {<<"application">>, <<"vnd.mobius.mbk">>, []};
all_ext(<<"mbox">>) -> {<<"application">>, <<"mbox">>, []};
all_ext(<<"mc1">>) -> {<<"application">>, <<"vnd.medcalcdata">>, []};
all_ext(<<"mcd">>) -> {<<"application">>, <<"vnd.mcd">>, []};
all_ext(<<"mcurl">>) -> {<<"text">>, <<"vnd.curl.mcurl">>, []};
all_ext(<<"mdb">>) -> {<<"application">>, <<"x-msaccess">>, []};
all_ext(<<"mdi">>) -> {<<"image">>, <<"vnd.ms-modi">>, []};
all_ext(<<"mesh">>) -> {<<"model">>, <<"mesh">>, []};
all_ext(<<"meta4">>) -> {<<"application">>, <<"metalink4+xml">>, []};
all_ext(<<"metalink">>) -> {<<"application">>, <<"metalink+xml">>, []};
all_ext(<<"me">>) -> {<<"text">>, <<"troff">>, []};
all_ext(<<"mets">>) -> {<<"application">>, <<"mets+xml">>, []};
all_ext(<<"mfm">>) -> {<<"application">>, <<"vnd.mfmp">>, []};
all_ext(<<"mft">>) -> {<<"application">>, <<"rpki-manifest">>, []};
all_ext(<<"mgp">>) -> {<<"application">>, <<"vnd.osgeo.mapguide.package">>, []};
all_ext(<<"mgz">>) -> {<<"application">>, <<"vnd.proteus.magazine">>, []};
all_ext(<<"mid">>) -> {<<"audio">>, <<"midi">>, []};
all_ext(<<"midi">>) -> {<<"audio">>, <<"midi">>, []};
all_ext(<<"mie">>) -> {<<"application">>, <<"x-mie">>, []};
all_ext(<<"mif">>) -> {<<"application">>, <<"vnd.mif">>, []};
all_ext(<<"mime">>) -> {<<"message">>, <<"rfc822">>, []};
all_ext(<<"mj2">>) -> {<<"video">>, <<"mj2">>, []};
all_ext(<<"mjp2">>) -> {<<"video">>, <<"mj2">>, []};
all_ext(<<"mk3d">>) -> {<<"video">>, <<"x-matroska">>, []};
all_ext(<<"mka">>) -> {<<"audio">>, <<"x-matroska">>, []};
all_ext(<<"mks">>) -> {<<"video">>, <<"x-matroska">>, []};
all_ext(<<"mkv">>) -> {<<"video">>, <<"x-matroska">>, []};
all_ext(<<"mlp">>) -> {<<"application">>, <<"vnd.dolby.mlp">>, []};
all_ext(<<"mmd">>) -> {<<"application">>, <<"vnd.chipnuts.karaoke-mmd">>, []};
all_ext(<<"mmf">>) -> {<<"application">>, <<"vnd.smaf">>, []};
all_ext(<<"mmr">>) -> {<<"image">>, <<"vnd.fujixerox.edmics-mmr">>, []};
all_ext(<<"mng">>) -> {<<"video">>, <<"x-mng">>, []};
all_ext(<<"mny">>) -> {<<"application">>, <<"x-msmoney">>, []};
all_ext(<<"mobi">>) -> {<<"application">>, <<"x-mobipocket-ebook">>, []};
all_ext(<<"mods">>) -> {<<"application">>, <<"mods+xml">>, []};
all_ext(<<"movie">>) -> {<<"video">>, <<"x-sgi-movie">>, []};
all_ext(<<"mov">>) -> {<<"video">>, <<"quicktime">>, []};
all_ext(<<"mp21">>) -> {<<"application">>, <<"mp21">>, []};
all_ext(<<"mp2a">>) -> {<<"audio">>, <<"mpeg">>, []};
all_ext(<<"mp2">>) -> {<<"audio">>, <<"mpeg">>, []};
all_ext(<<"mp3">>) -> {<<"audio">>, <<"mpeg">>, []};
all_ext(<<"mp4a">>) -> {<<"audio">>, <<"mp4">>, []};
all_ext(<<"mp4s">>) -> {<<"application">>, <<"mp4">>, []};
all_ext(<<"mp4">>) -> {<<"video">>, <<"mp4">>, []};
all_ext(<<"mp4v">>) -> {<<"video">>, <<"mp4">>, []};
all_ext(<<"mpc">>) -> {<<"application">>, <<"vnd.mophun.certificate">>, []};
all_ext(<<"mpeg">>) -> {<<"video">>, <<"mpeg">>, []};
all_ext(<<"mpe">>) -> {<<"video">>, <<"mpeg">>, []};
all_ext(<<"mpg4">>) -> {<<"video">>, <<"mp4">>, []};
all_ext(<<"mpga">>) -> {<<"audio">>, <<"mpeg">>, []};
all_ext(<<"mpg">>) -> {<<"video">>, <<"mpeg">>, []};
all_ext(<<"mpkg">>) -> {<<"application">>, <<"vnd.apple.installer+xml">>, []};
all_ext(<<"mpm">>) -> {<<"application">>, <<"vnd.blueice.multipass">>, []};
all_ext(<<"mpn">>) -> {<<"application">>, <<"vnd.mophun.application">>, []};
all_ext(<<"mpp">>) -> {<<"application">>, <<"vnd.ms-project">>, []};
all_ext(<<"mpt">>) -> {<<"application">>, <<"vnd.ms-project">>, []};
all_ext(<<"mpy">>) -> {<<"application">>, <<"vnd.ibm.minipay">>, []};
all_ext(<<"mqy">>) -> {<<"application">>, <<"vnd.mobius.mqy">>, []};
all_ext(<<"mrc">>) -> {<<"application">>, <<"marc">>, []};
all_ext(<<"mrcx">>) -> {<<"application">>, <<"marcxml+xml">>, []};
all_ext(<<"mscml">>) -> {<<"application">>, <<"mediaservercontrol+xml">>, []};
all_ext(<<"mseed">>) -> {<<"application">>, <<"vnd.fdsn.mseed">>, []};
all_ext(<<"mseq">>) -> {<<"application">>, <<"vnd.mseq">>, []};
all_ext(<<"msf">>) -> {<<"application">>, <<"vnd.epson.msf">>, []};
all_ext(<<"msh">>) -> {<<"model">>, <<"mesh">>, []};
all_ext(<<"msi">>) -> {<<"application">>, <<"x-msdownload">>, []};
all_ext(<<"msl">>) -> {<<"application">>, <<"vnd.mobius.msl">>, []};
all_ext(<<"ms">>) -> {<<"text">>, <<"troff">>, []};
all_ext(<<"msty">>) -> {<<"application">>, <<"vnd.muvee.style">>, []};
all_ext(<<"mts">>) -> {<<"model">>, <<"vnd.mts">>, []};
all_ext(<<"mus">>) -> {<<"application">>, <<"vnd.musician">>, []};
all_ext(<<"musicxml">>) -> {<<"application">>, <<"vnd.recordare.musicxml+xml">>, []};
all_ext(<<"mvb">>) -> {<<"application">>, <<"x-msmediaview">>, []};
all_ext(<<"mwf">>) -> {<<"application">>, <<"vnd.mfer">>, []};
all_ext(<<"mxf">>) -> {<<"application">>, <<"mxf">>, []};
all_ext(<<"mxl">>) -> {<<"application">>, <<"vnd.recordare.musicxml">>, []};
all_ext(<<"mxml">>) -> {<<"application">>, <<"xv+xml">>, []};
all_ext(<<"mxs">>) -> {<<"application">>, <<"vnd.triscape.mxs">>, []};
all_ext(<<"mxu">>) -> {<<"video">>, <<"vnd.mpegurl">>, []};
all_ext(<<"n3">>) -> {<<"text">>, <<"n3">>, []};
all_ext(<<"nb">>) -> {<<"application">>, <<"mathematica">>, []};
all_ext(<<"nbp">>) -> {<<"application">>, <<"vnd.wolfram.player">>, []};
all_ext(<<"nc">>) -> {<<"application">>, <<"x-netcdf">>, []};
all_ext(<<"ncx">>) -> {<<"application">>, <<"x-dtbncx+xml">>, []};
all_ext(<<"nfo">>) -> {<<"text">>, <<"x-nfo">>, []};
all_ext(<<"n-gage">>) -> {<<"application">>, <<"vnd.nokia.n-gage.symbian.install">>, []};
all_ext(<<"ngdat">>) -> {<<"application">>, <<"vnd.nokia.n-gage.data">>, []};
all_ext(<<"nitf">>) -> {<<"application">>, <<"vnd.nitf">>, []};
all_ext(<<"nlu">>) -> {<<"application">>, <<"vnd.neurolanguage.nlu">>, []};
all_ext(<<"nml">>) -> {<<"application">>, <<"vnd.enliven">>, []};
all_ext(<<"nnd">>) -> {<<"application">>, <<"vnd.noblenet-directory">>, []};
all_ext(<<"nns">>) -> {<<"application">>, <<"vnd.noblenet-sealer">>, []};
all_ext(<<"nnw">>) -> {<<"application">>, <<"vnd.noblenet-web">>, []};
all_ext(<<"npx">>) -> {<<"image">>, <<"vnd.net-fpx">>, []};
all_ext(<<"nsc">>) -> {<<"application">>, <<"x-conference">>, []};
all_ext(<<"nsf">>) -> {<<"application">>, <<"vnd.lotus-notes">>, []};
all_ext(<<"ntf">>) -> {<<"application">>, <<"vnd.nitf">>, []};
all_ext(<<"nzb">>) -> {<<"application">>, <<"x-nzb">>, []};
all_ext(<<"oa2">>) -> {<<"application">>, <<"vnd.fujitsu.oasys2">>, []};
all_ext(<<"oa3">>) -> {<<"application">>, <<"vnd.fujitsu.oasys3">>, []};
all_ext(<<"oas">>) -> {<<"application">>, <<"vnd.fujitsu.oasys">>, []};
all_ext(<<"obd">>) -> {<<"application">>, <<"x-msbinder">>, []};
all_ext(<<"obj">>) -> {<<"application">>, <<"x-tgif">>, []};
all_ext(<<"oda">>) -> {<<"application">>, <<"oda">>, []};
all_ext(<<"odb">>) -> {<<"application">>, <<"vnd.oasis.opendocument.database">>, []};
all_ext(<<"odc">>) -> {<<"application">>, <<"vnd.oasis.opendocument.chart">>, []};
all_ext(<<"odf">>) -> {<<"application">>, <<"vnd.oasis.opendocument.formula">>, []};
all_ext(<<"odft">>) -> {<<"application">>, <<"vnd.oasis.opendocument.formula-template">>, []};
all_ext(<<"odg">>) -> {<<"application">>, <<"vnd.oasis.opendocument.graphics">>, []};
all_ext(<<"odi">>) -> {<<"application">>, <<"vnd.oasis.opendocument.image">>, []};
all_ext(<<"odm">>) -> {<<"application">>, <<"vnd.oasis.opendocument.text-master">>, []};
all_ext(<<"odp">>) -> {<<"application">>, <<"vnd.oasis.opendocument.presentation">>, []};
all_ext(<<"ods">>) -> {<<"application">>, <<"vnd.oasis.opendocument.spreadsheet">>, []};
all_ext(<<"odt">>) -> {<<"application">>, <<"vnd.oasis.opendocument.text">>, []};
all_ext(<<"oga">>) -> {<<"audio">>, <<"ogg">>, []};
all_ext(<<"ogg">>) -> {<<"audio">>, <<"ogg">>, []};
all_ext(<<"ogv">>) -> {<<"video">>, <<"ogg">>, []};
all_ext(<<"ogx">>) -> {<<"application">>, <<"ogg">>, []};
all_ext(<<"omdoc">>) -> {<<"application">>, <<"omdoc+xml">>, []};
all_ext(<<"onepkg">>) -> {<<"application">>, <<"onenote">>, []};
all_ext(<<"onetmp">>) -> {<<"application">>, <<"onenote">>, []};
all_ext(<<"onetoc2">>) -> {<<"application">>, <<"onenote">>, []};
all_ext(<<"onetoc">>) -> {<<"application">>, <<"onenote">>, []};
all_ext(<<"opf">>) -> {<<"application">>, <<"oebps-package+xml">>, []};
all_ext(<<"opml">>) -> {<<"text">>, <<"x-opml">>, []};
all_ext(<<"oprc">>) -> {<<"application">>, <<"vnd.palm">>, []};
all_ext(<<"org">>) -> {<<"application">>, <<"vnd.lotus-organizer">>, []};
all_ext(<<"osf">>) -> {<<"application">>, <<"vnd.yamaha.openscoreformat">>, []};
all_ext(<<"osfpvg">>) -> {<<"application">>, <<"vnd.yamaha.openscoreformat.osfpvg+xml">>, []};
all_ext(<<"otc">>) -> {<<"application">>, <<"vnd.oasis.opendocument.chart-template">>, []};
all_ext(<<"otf">>) -> {<<"application">>, <<"x-font-otf">>, []};
all_ext(<<"otg">>) -> {<<"application">>, <<"vnd.oasis.opendocument.graphics-template">>, []};
all_ext(<<"oth">>) -> {<<"application">>, <<"vnd.oasis.opendocument.text-web">>, []};
all_ext(<<"oti">>) -> {<<"application">>, <<"vnd.oasis.opendocument.image-template">>, []};
all_ext(<<"otp">>) -> {<<"application">>, <<"vnd.oasis.opendocument.presentation-template">>, []};
all_ext(<<"ots">>) -> {<<"application">>, <<"vnd.oasis.opendocument.spreadsheet-template">>, []};
all_ext(<<"ott">>) -> {<<"application">>, <<"vnd.oasis.opendocument.text-template">>, []};
all_ext(<<"oxps">>) -> {<<"application">>, <<"oxps">>, []};
all_ext(<<"oxt">>) -> {<<"application">>, <<"vnd.openofficeorg.extension">>, []};
all_ext(<<"p10">>) -> {<<"application">>, <<"pkcs10">>, []};
all_ext(<<"p12">>) -> {<<"application">>, <<"x-pkcs12">>, []};
all_ext(<<"p7b">>) -> {<<"application">>, <<"x-pkcs7-certificates">>, []};
all_ext(<<"p7c">>) -> {<<"application">>, <<"pkcs7-mime">>, []};
all_ext(<<"p7m">>) -> {<<"application">>, <<"pkcs7-mime">>, []};
all_ext(<<"p7r">>) -> {<<"application">>, <<"x-pkcs7-certreqresp">>, []};
all_ext(<<"p7s">>) -> {<<"application">>, <<"pkcs7-signature">>, []};
all_ext(<<"p8">>) -> {<<"application">>, <<"pkcs8">>, []};
all_ext(<<"pas">>) -> {<<"text">>, <<"x-pascal">>, []};
all_ext(<<"paw">>) -> {<<"application">>, <<"vnd.pawaafile">>, []};
all_ext(<<"pbd">>) -> {<<"application">>, <<"vnd.powerbuilder6">>, []};
all_ext(<<"pbm">>) -> {<<"image">>, <<"x-portable-bitmap">>, []};
all_ext(<<"pcap">>) -> {<<"application">>, <<"vnd.tcpdump.pcap">>, []};
all_ext(<<"pcf">>) -> {<<"application">>, <<"x-font-pcf">>, []};
all_ext(<<"pcl">>) -> {<<"application">>, <<"vnd.hp-pcl">>, []};
all_ext(<<"pclxl">>) -> {<<"application">>, <<"vnd.hp-pclxl">>, []};
all_ext(<<"pct">>) -> {<<"image">>, <<"x-pict">>, []};
all_ext(<<"pcurl">>) -> {<<"application">>, <<"vnd.curl.pcurl">>, []};
all_ext(<<"pcx">>) -> {<<"image">>, <<"x-pcx">>, []};
all_ext(<<"pdb">>) -> {<<"application">>, <<"vnd.palm">>, []};
all_ext(<<"pdf">>) -> {<<"application">>, <<"pdf">>, []};
all_ext(<<"pfa">>) -> {<<"application">>, <<"x-font-type1">>, []};
all_ext(<<"pfb">>) -> {<<"application">>, <<"x-font-type1">>, []};
all_ext(<<"pfm">>) -> {<<"application">>, <<"x-font-type1">>, []};
all_ext(<<"pfr">>) -> {<<"application">>, <<"font-tdpfr">>, []};
all_ext(<<"pfx">>) -> {<<"application">>, <<"x-pkcs12">>, []};
all_ext(<<"pgm">>) -> {<<"image">>, <<"x-portable-graymap">>, []};
all_ext(<<"pgn">>) -> {<<"application">>, <<"x-chess-pgn">>, []};
all_ext(<<"pgp">>) -> {<<"application">>, <<"pgp-encrypted">>, []};
all_ext(<<"pic">>) -> {<<"image">>, <<"x-pict">>, []};
all_ext(<<"pkg">>) -> {<<"application">>, <<"octet-stream">>, []};
all_ext(<<"pki">>) -> {<<"application">>, <<"pkixcmp">>, []};
all_ext(<<"pkipath">>) -> {<<"application">>, <<"pkix-pkipath">>, []};
all_ext(<<"plb">>) -> {<<"application">>, <<"vnd.3gpp.pic-bw-large">>, []};
all_ext(<<"plc">>) -> {<<"application">>, <<"vnd.mobius.plc">>, []};
all_ext(<<"plf">>) -> {<<"application">>, <<"vnd.pocketlearn">>, []};
all_ext(<<"pls">>) -> {<<"application">>, <<"pls+xml">>, []};
all_ext(<<"pml">>) -> {<<"application">>, <<"vnd.ctc-posml">>, []};
all_ext(<<"png">>) -> {<<"image">>, <<"png">>, []};
all_ext(<<"pnm">>) -> {<<"image">>, <<"x-portable-anymap">>, []};
all_ext(<<"portpkg">>) -> {<<"application">>, <<"vnd.macports.portpkg">>, []};
all_ext(<<"pot">>) -> {<<"application">>, <<"vnd.ms-powerpoint">>, []};
all_ext(<<"potm">>) -> {<<"application">>, <<"vnd.ms-powerpoint.template.macroenabled.12">>, []};
all_ext(<<"potx">>) -> {<<"application">>, <<"vnd.openxmlformats-officedocument.presentationml.template">>, []};
all_ext(<<"ppam">>) -> {<<"application">>, <<"vnd.ms-powerpoint.addin.macroenabled.12">>, []};
all_ext(<<"ppd">>) -> {<<"application">>, <<"vnd.cups-ppd">>, []};
all_ext(<<"ppm">>) -> {<<"image">>, <<"x-portable-pixmap">>, []};
all_ext(<<"pps">>) -> {<<"application">>, <<"vnd.ms-powerpoint">>, []};
all_ext(<<"ppsm">>) -> {<<"application">>, <<"vnd.ms-powerpoint.slideshow.macroenabled.12">>, []};
all_ext(<<"ppsx">>) -> {<<"application">>, <<"vnd.openxmlformats-officedocument.presentationml.slideshow">>, []};
all_ext(<<"ppt">>) -> {<<"application">>, <<"vnd.ms-powerpoint">>, []};
all_ext(<<"pptm">>) -> {<<"application">>, <<"vnd.ms-powerpoint.presentation.macroenabled.12">>, []};
all_ext(<<"pptx">>) -> {<<"application">>, <<"vnd.openxmlformats-officedocument.presentationml.presentation">>, []};
all_ext(<<"pqa">>) -> {<<"application">>, <<"vnd.palm">>, []};
all_ext(<<"prc">>) -> {<<"application">>, <<"x-mobipocket-ebook">>, []};
all_ext(<<"pre">>) -> {<<"application">>, <<"vnd.lotus-freelance">>, []};
all_ext(<<"prf">>) -> {<<"application">>, <<"pics-rules">>, []};
all_ext(<<"ps">>) -> {<<"application">>, <<"postscript">>, []};
all_ext(<<"psb">>) -> {<<"application">>, <<"vnd.3gpp.pic-bw-small">>, []};
all_ext(<<"psd">>) -> {<<"image">>, <<"vnd.adobe.photoshop">>, []};
all_ext(<<"psf">>) -> {<<"application">>, <<"x-font-linux-psf">>, []};
all_ext(<<"pskcxml">>) -> {<<"application">>, <<"pskc+xml">>, []};
all_ext(<<"p">>) -> {<<"text">>, <<"x-pascal">>, []};
all_ext(<<"ptid">>) -> {<<"application">>, <<"vnd.pvi.ptid1">>, []};
all_ext(<<"pub">>) -> {<<"application">>, <<"x-mspublisher">>, []};
all_ext(<<"pvb">>) -> {<<"application">>, <<"vnd.3gpp.pic-bw-var">>, []};
all_ext(<<"pwn">>) -> {<<"application">>, <<"vnd.3m.post-it-notes">>, []};
all_ext(<<"pya">>) -> {<<"audio">>, <<"vnd.ms-playready.media.pya">>, []};
all_ext(<<"pyv">>) -> {<<"video">>, <<"vnd.ms-playready.media.pyv">>, []};
all_ext(<<"qam">>) -> {<<"application">>, <<"vnd.epson.quickanime">>, []};
all_ext(<<"qbo">>) -> {<<"application">>, <<"vnd.intu.qbo">>, []};
all_ext(<<"qfx">>) -> {<<"application">>, <<"vnd.intu.qfx">>, []};
all_ext(<<"qps">>) -> {<<"application">>, <<"vnd.publishare-delta-tree">>, []};
all_ext(<<"qt">>) -> {<<"video">>, <<"quicktime">>, []};
all_ext(<<"qwd">>) -> {<<"application">>, <<"vnd.quark.quarkxpress">>, []};
all_ext(<<"qwt">>) -> {<<"application">>, <<"vnd.quark.quarkxpress">>, []};
all_ext(<<"qxb">>) -> {<<"application">>, <<"vnd.quark.quarkxpress">>, []};
all_ext(<<"qxd">>) -> {<<"application">>, <<"vnd.quark.quarkxpress">>, []};
all_ext(<<"qxl">>) -> {<<"application">>, <<"vnd.quark.quarkxpress">>, []};
all_ext(<<"qxt">>) -> {<<"application">>, <<"vnd.quark.quarkxpress">>, []};
all_ext(<<"ra">>) -> {<<"audio">>, <<"x-pn-realaudio">>, []};
all_ext(<<"ram">>) -> {<<"audio">>, <<"x-pn-realaudio">>, []};
all_ext(<<"rar">>) -> {<<"application">>, <<"x-rar-compressed">>, []};
all_ext(<<"ras">>) -> {<<"image">>, <<"x-cmu-raster">>, []};
all_ext(<<"rcprofile">>) -> {<<"application">>, <<"vnd.ipunplugged.rcprofile">>, []};
all_ext(<<"rdf">>) -> {<<"application">>, <<"rdf+xml">>, []};
all_ext(<<"rdz">>) -> {<<"application">>, <<"vnd.data-vision.rdz">>, []};
all_ext(<<"rep">>) -> {<<"application">>, <<"vnd.businessobjects">>, []};
all_ext(<<"res">>) -> {<<"application">>, <<"x-dtbresource+xml">>, []};
all_ext(<<"rgb">>) -> {<<"image">>, <<"x-rgb">>, []};
all_ext(<<"rif">>) -> {<<"application">>, <<"reginfo+xml">>, []};
all_ext(<<"rip">>) -> {<<"audio">>, <<"vnd.rip">>, []};
all_ext(<<"ris">>) -> {<<"application">>, <<"x-research-info-systems">>, []};
all_ext(<<"rl">>) -> {<<"application">>, <<"resource-lists+xml">>, []};
all_ext(<<"rlc">>) -> {<<"image">>, <<"vnd.fujixerox.edmics-rlc">>, []};
all_ext(<<"rld">>) -> {<<"application">>, <<"resource-lists-diff+xml">>, []};
all_ext(<<"rm">>) -> {<<"application">>, <<"vnd.rn-realmedia">>, []};
all_ext(<<"rmi">>) -> {<<"audio">>, <<"midi">>, []};
all_ext(<<"rmp">>) -> {<<"audio">>, <<"x-pn-realaudio-plugin">>, []};
all_ext(<<"rms">>) -> {<<"application">>, <<"vnd.jcp.javame.midlet-rms">>, []};
all_ext(<<"rmvb">>) -> {<<"application">>, <<"vnd.rn-realmedia-vbr">>, []};
all_ext(<<"rnc">>) -> {<<"application">>, <<"relax-ng-compact-syntax">>, []};
all_ext(<<"roa">>) -> {<<"application">>, <<"rpki-roa">>, []};
all_ext(<<"roff">>) -> {<<"text">>, <<"troff">>, []};
all_ext(<<"rp9">>) -> {<<"application">>, <<"vnd.cloanto.rp9">>, []};
all_ext(<<"rpss">>) -> {<<"application">>, <<"vnd.nokia.radio-presets">>, []};
all_ext(<<"rpst">>) -> {<<"application">>, <<"vnd.nokia.radio-preset">>, []};
all_ext(<<"rq">>) -> {<<"application">>, <<"sparql-query">>, []};
all_ext(<<"rs">>) -> {<<"application">>, <<"rls-services+xml">>, []};
all_ext(<<"rsd">>) -> {<<"application">>, <<"rsd+xml">>, []};
all_ext(<<"rss">>) -> {<<"application">>, <<"rss+xml">>, []};
all_ext(<<"rtf">>) -> {<<"application">>, <<"rtf">>, []};
all_ext(<<"rtx">>) -> {<<"text">>, <<"richtext">>, []};
all_ext(<<"s3m">>) -> {<<"audio">>, <<"s3m">>, []};
all_ext(<<"saf">>) -> {<<"application">>, <<"vnd.yamaha.smaf-audio">>, []};
all_ext(<<"sbml">>) -> {<<"application">>, <<"sbml+xml">>, []};
all_ext(<<"sc">>) -> {<<"application">>, <<"vnd.ibm.secure-container">>, []};
all_ext(<<"scd">>) -> {<<"application">>, <<"x-msschedule">>, []};
all_ext(<<"scm">>) -> {<<"application">>, <<"vnd.lotus-screencam">>, []};
all_ext(<<"scq">>) -> {<<"application">>, <<"scvp-cv-request">>, []};
all_ext(<<"scs">>) -> {<<"application">>, <<"scvp-cv-response">>, []};
all_ext(<<"scurl">>) -> {<<"text">>, <<"vnd.curl.scurl">>, []};
all_ext(<<"sda">>) -> {<<"application">>, <<"vnd.stardivision.draw">>, []};
all_ext(<<"sdc">>) -> {<<"application">>, <<"vnd.stardivision.calc">>, []};
all_ext(<<"sdd">>) -> {<<"application">>, <<"vnd.stardivision.impress">>, []};
all_ext(<<"sdkd">>) -> {<<"application">>, <<"vnd.solent.sdkm+xml">>, []};
all_ext(<<"sdkm">>) -> {<<"application">>, <<"vnd.solent.sdkm+xml">>, []};
all_ext(<<"sdp">>) -> {<<"application">>, <<"sdp">>, []};
all_ext(<<"sdw">>) -> {<<"application">>, <<"vnd.stardivision.writer">>, []};
all_ext(<<"see">>) -> {<<"application">>, <<"vnd.seemail">>, []};
all_ext(<<"seed">>) -> {<<"application">>, <<"vnd.fdsn.seed">>, []};
all_ext(<<"sema">>) -> {<<"application">>, <<"vnd.sema">>, []};
all_ext(<<"semd">>) -> {<<"application">>, <<"vnd.semd">>, []};
all_ext(<<"semf">>) -> {<<"application">>, <<"vnd.semf">>, []};
all_ext(<<"ser">>) -> {<<"application">>, <<"java-serialized-object">>, []};
all_ext(<<"setpay">>) -> {<<"application">>, <<"set-payment-initiation">>, []};
all_ext(<<"setreg">>) -> {<<"application">>, <<"set-registration-initiation">>, []};
all_ext(<<"sfd-hdstx">>) -> {<<"application">>, <<"vnd.hydrostatix.sof-data">>, []};
all_ext(<<"sfs">>) -> {<<"application">>, <<"vnd.spotfire.sfs">>, []};
all_ext(<<"sfv">>) -> {<<"text">>, <<"x-sfv">>, []};
all_ext(<<"sgi">>) -> {<<"image">>, <<"sgi">>, []};
all_ext(<<"sgl">>) -> {<<"application">>, <<"vnd.stardivision.writer-global">>, []};
all_ext(<<"sgml">>) -> {<<"text">>, <<"sgml">>, []};
all_ext(<<"sgm">>) -> {<<"text">>, <<"sgml">>, []};
all_ext(<<"sh">>) -> {<<"application">>, <<"x-sh">>, []};
all_ext(<<"shar">>) -> {<<"application">>, <<"x-shar">>, []};
all_ext(<<"shf">>) -> {<<"application">>, <<"shf+xml">>, []};
all_ext(<<"sid">>) -> {<<"image">>, <<"x-mrsid-image">>, []};
all_ext(<<"sig">>) -> {<<"application">>, <<"pgp-signature">>, []};
all_ext(<<"sil">>) -> {<<"audio">>, <<"silk">>, []};
all_ext(<<"silo">>) -> {<<"model">>, <<"mesh">>, []};
all_ext(<<"sis">>) -> {<<"application">>, <<"vnd.symbian.install">>, []};
all_ext(<<"sisx">>) -> {<<"application">>, <<"vnd.symbian.install">>, []};
all_ext(<<"sit">>) -> {<<"application">>, <<"x-stuffit">>, []};
all_ext(<<"sitx">>) -> {<<"application">>, <<"x-stuffitx">>, []};
all_ext(<<"skd">>) -> {<<"application">>, <<"vnd.koan">>, []};
all_ext(<<"skm">>) -> {<<"application">>, <<"vnd.koan">>, []};
all_ext(<<"skp">>) -> {<<"application">>, <<"vnd.koan">>, []};
all_ext(<<"skt">>) -> {<<"application">>, <<"vnd.koan">>, []};
all_ext(<<"sldm">>) -> {<<"application">>, <<"vnd.ms-powerpoint.slide.macroenabled.12">>, []};
all_ext(<<"sldx">>) -> {<<"application">>, <<"vnd.openxmlformats-officedocument.presentationml.slide">>, []};
all_ext(<<"slt">>) -> {<<"application">>, <<"vnd.epson.salt">>, []};
all_ext(<<"sm">>) -> {<<"application">>, <<"vnd.stepmania.stepchart">>, []};
all_ext(<<"smf">>) -> {<<"application">>, <<"vnd.stardivision.math">>, []};
all_ext(<<"smi">>) -> {<<"application">>, <<"smil+xml">>, []};
all_ext(<<"smil">>) -> {<<"application">>, <<"smil+xml">>, []};
all_ext(<<"smv">>) -> {<<"video">>, <<"x-smv">>, []};
all_ext(<<"smzip">>) -> {<<"application">>, <<"vnd.stepmania.package">>, []};
all_ext(<<"snd">>) -> {<<"audio">>, <<"basic">>, []};
all_ext(<<"snf">>) -> {<<"application">>, <<"x-font-snf">>, []};
all_ext(<<"so">>) -> {<<"application">>, <<"octet-stream">>, []};
all_ext(<<"spc">>) -> {<<"application">>, <<"x-pkcs7-certificates">>, []};
all_ext(<<"spf">>) -> {<<"application">>, <<"vnd.yamaha.smaf-phrase">>, []};
all_ext(<<"spl">>) -> {<<"application">>, <<"x-futuresplash">>, []};
all_ext(<<"spot">>) -> {<<"text">>, <<"vnd.in3d.spot">>, []};
all_ext(<<"spp">>) -> {<<"application">>, <<"scvp-vp-response">>, []};
all_ext(<<"spq">>) -> {<<"application">>, <<"scvp-vp-request">>, []};
all_ext(<<"spx">>) -> {<<"audio">>, <<"ogg">>, []};
all_ext(<<"sql">>) -> {<<"application">>, <<"x-sql">>, []};
all_ext(<<"src">>) -> {<<"application">>, <<"x-wais-source">>, []};
all_ext(<<"srt">>) -> {<<"application">>, <<"x-subrip">>, []};
all_ext(<<"sru">>) -> {<<"application">>, <<"sru+xml">>, []};
all_ext(<<"srx">>) -> {<<"application">>, <<"sparql-results+xml">>, []};
all_ext(<<"ssdl">>) -> {<<"application">>, <<"ssdl+xml">>, []};
all_ext(<<"sse">>) -> {<<"application">>, <<"vnd.kodak-descriptor">>, []};
all_ext(<<"ssf">>) -> {<<"application">>, <<"vnd.epson.ssf">>, []};
all_ext(<<"ssml">>) -> {<<"application">>, <<"ssml+xml">>, []};
all_ext(<<"st">>) -> {<<"application">>, <<"vnd.sailingtracker.track">>, []};
all_ext(<<"stc">>) -> {<<"application">>, <<"vnd.sun.xml.calc.template">>, []};
all_ext(<<"std">>) -> {<<"application">>, <<"vnd.sun.xml.draw.template">>, []};
all_ext(<<"s">>) -> {<<"text">>, <<"x-asm">>, []};
all_ext(<<"stf">>) -> {<<"application">>, <<"vnd.wt.stf">>, []};
all_ext(<<"sti">>) -> {<<"application">>, <<"vnd.sun.xml.impress.template">>, []};
all_ext(<<"stk">>) -> {<<"application">>, <<"hyperstudio">>, []};
all_ext(<<"stl">>) -> {<<"application">>, <<"vnd.ms-pki.stl">>, []};
all_ext(<<"str">>) -> {<<"application">>, <<"vnd.pg.format">>, []};
all_ext(<<"stw">>) -> {<<"application">>, <<"vnd.sun.xml.writer.template">>, []};
all_ext(<<"sub">>) -> {<<"image">>, <<"vnd.dvb.subtitle">>, []};
all_ext(<<"sus">>) -> {<<"application">>, <<"vnd.sus-calendar">>, []};
all_ext(<<"susp">>) -> {<<"application">>, <<"vnd.sus-calendar">>, []};
all_ext(<<"sv4cpio">>) -> {<<"application">>, <<"x-sv4cpio">>, []};
all_ext(<<"sv4crc">>) -> {<<"application">>, <<"x-sv4crc">>, []};
all_ext(<<"svc">>) -> {<<"application">>, <<"vnd.dvb.service">>, []};
all_ext(<<"svd">>) -> {<<"application">>, <<"vnd.svd">>, []};
all_ext(<<"svg">>) -> {<<"image">>, <<"svg+xml">>, []};
all_ext(<<"svgz">>) -> {<<"image">>, <<"svg+xml">>, []};
all_ext(<<"swa">>) -> {<<"application">>, <<"x-director">>, []};
all_ext(<<"swf">>) -> {<<"application">>, <<"x-shockwave-flash">>, []};
all_ext(<<"swi">>) -> {<<"application">>, <<"vnd.aristanetworks.swi">>, []};
all_ext(<<"sxc">>) -> {<<"application">>, <<"vnd.sun.xml.calc">>, []};
all_ext(<<"sxd">>) -> {<<"application">>, <<"vnd.sun.xml.draw">>, []};
all_ext(<<"sxg">>) -> {<<"application">>, <<"vnd.sun.xml.writer.global">>, []};
all_ext(<<"sxi">>) -> {<<"application">>, <<"vnd.sun.xml.impress">>, []};
all_ext(<<"sxm">>) -> {<<"application">>, <<"vnd.sun.xml.math">>, []};
all_ext(<<"sxw">>) -> {<<"application">>, <<"vnd.sun.xml.writer">>, []};
all_ext(<<"t3">>) -> {<<"application">>, <<"x-t3vm-image">>, []};
all_ext(<<"taglet">>) -> {<<"application">>, <<"vnd.mynfc">>, []};
all_ext(<<"tao">>) -> {<<"application">>, <<"vnd.tao.intent-module-archive">>, []};
all_ext(<<"tar">>) -> {<<"application">>, <<"x-tar">>, []};
all_ext(<<"tcap">>) -> {<<"application">>, <<"vnd.3gpp2.tcap">>, []};
all_ext(<<"tcl">>) -> {<<"application">>, <<"x-tcl">>, []};
all_ext(<<"teacher">>) -> {<<"application">>, <<"vnd.smart.teacher">>, []};
all_ext(<<"tei">>) -> {<<"application">>, <<"tei+xml">>, []};
all_ext(<<"teicorpus">>) -> {<<"application">>, <<"tei+xml">>, []};
all_ext(<<"tex">>) -> {<<"application">>, <<"x-tex">>, []};
all_ext(<<"texi">>) -> {<<"application">>, <<"x-texinfo">>, []};
all_ext(<<"texinfo">>) -> {<<"application">>, <<"x-texinfo">>, []};
all_ext(<<"text">>) -> {<<"text">>, <<"plain">>, []};
all_ext(<<"tfi">>) -> {<<"application">>, <<"thraud+xml">>, []};
all_ext(<<"tfm">>) -> {<<"application">>, <<"x-tex-tfm">>, []};
all_ext(<<"tga">>) -> {<<"image">>, <<"x-tga">>, []};
all_ext(<<"thmx">>) -> {<<"application">>, <<"vnd.ms-officetheme">>, []};
all_ext(<<"tiff">>) -> {<<"image">>, <<"tiff">>, []};
all_ext(<<"tif">>) -> {<<"image">>, <<"tiff">>, []};
all_ext(<<"tmo">>) -> {<<"application">>, <<"vnd.tmobile-livetv">>, []};
all_ext(<<"torrent">>) -> {<<"application">>, <<"x-bittorrent">>, []};
all_ext(<<"tpl">>) -> {<<"application">>, <<"vnd.groove-tool-template">>, []};
all_ext(<<"tpt">>) -> {<<"application">>, <<"vnd.trid.tpt">>, []};
all_ext(<<"tra">>) -> {<<"application">>, <<"vnd.trueapp">>, []};
all_ext(<<"trm">>) -> {<<"application">>, <<"x-msterminal">>, []};
all_ext(<<"tr">>) -> {<<"text">>, <<"troff">>, []};
all_ext(<<"tsd">>) -> {<<"application">>, <<"timestamped-data">>, []};
all_ext(<<"tsv">>) -> {<<"text">>, <<"tab-separated-values">>, []};
all_ext(<<"ttc">>) -> {<<"application">>, <<"x-font-ttf">>, []};
all_ext(<<"t">>) -> {<<"text">>, <<"troff">>, []};
all_ext(<<"ttf">>) -> {<<"application">>, <<"x-font-ttf">>, []};
all_ext(<<"ttl">>) -> {<<"text">>, <<"turtle">>, []};
all_ext(<<"twd">>) -> {<<"application">>, <<"vnd.simtech-mindmapper">>, []};
all_ext(<<"twds">>) -> {<<"application">>, <<"vnd.simtech-mindmapper">>, []};
all_ext(<<"txd">>) -> {<<"application">>, <<"vnd.genomatix.tuxedo">>, []};
all_ext(<<"txf">>) -> {<<"application">>, <<"vnd.mobius.txf">>, []};
all_ext(<<"txt">>) -> {<<"text">>, <<"plain">>, []};
all_ext(<<"u32">>) -> {<<"application">>, <<"x-authorware-bin">>, []};
all_ext(<<"udeb">>) -> {<<"application">>, <<"x-debian-package">>, []};
all_ext(<<"ufd">>) -> {<<"application">>, <<"vnd.ufdl">>, []};
all_ext(<<"ufdl">>) -> {<<"application">>, <<"vnd.ufdl">>, []};
all_ext(<<"ulx">>) -> {<<"application">>, <<"x-glulx">>, []};
all_ext(<<"umj">>) -> {<<"application">>, <<"vnd.umajin">>, []};
all_ext(<<"unityweb">>) -> {<<"application">>, <<"vnd.unity">>, []};
all_ext(<<"uoml">>) -> {<<"application">>, <<"vnd.uoml+xml">>, []};
all_ext(<<"uris">>) -> {<<"text">>, <<"uri-list">>, []};
all_ext(<<"uri">>) -> {<<"text">>, <<"uri-list">>, []};
all_ext(<<"urls">>) -> {<<"text">>, <<"uri-list">>, []};
all_ext(<<"ustar">>) -> {<<"application">>, <<"x-ustar">>, []};
all_ext(<<"utz">>) -> {<<"application">>, <<"vnd.uiq.theme">>, []};
all_ext(<<"uu">>) -> {<<"text">>, <<"x-uuencode">>, []};
all_ext(<<"uva">>) -> {<<"audio">>, <<"vnd.dece.audio">>, []};
all_ext(<<"uvd">>) -> {<<"application">>, <<"vnd.dece.data">>, []};
all_ext(<<"uvf">>) -> {<<"application">>, <<"vnd.dece.data">>, []};
all_ext(<<"uvg">>) -> {<<"image">>, <<"vnd.dece.graphic">>, []};
all_ext(<<"uvh">>) -> {<<"video">>, <<"vnd.dece.hd">>, []};
all_ext(<<"uvi">>) -> {<<"image">>, <<"vnd.dece.graphic">>, []};
all_ext(<<"uvm">>) -> {<<"video">>, <<"vnd.dece.mobile">>, []};
all_ext(<<"uvp">>) -> {<<"video">>, <<"vnd.dece.pd">>, []};
all_ext(<<"uvs">>) -> {<<"video">>, <<"vnd.dece.sd">>, []};
all_ext(<<"uvt">>) -> {<<"application">>, <<"vnd.dece.ttml+xml">>, []};
all_ext(<<"uvu">>) -> {<<"video">>, <<"vnd.uvvu.mp4">>, []};
all_ext(<<"uvva">>) -> {<<"audio">>, <<"vnd.dece.audio">>, []};
all_ext(<<"uvvd">>) -> {<<"application">>, <<"vnd.dece.data">>, []};
all_ext(<<"uvvf">>) -> {<<"application">>, <<"vnd.dece.data">>, []};
all_ext(<<"uvvg">>) -> {<<"image">>, <<"vnd.dece.graphic">>, []};
all_ext(<<"uvvh">>) -> {<<"video">>, <<"vnd.dece.hd">>, []};
all_ext(<<"uvvi">>) -> {<<"image">>, <<"vnd.dece.graphic">>, []};
all_ext(<<"uvvm">>) -> {<<"video">>, <<"vnd.dece.mobile">>, []};
all_ext(<<"uvvp">>) -> {<<"video">>, <<"vnd.dece.pd">>, []};
all_ext(<<"uvvs">>) -> {<<"video">>, <<"vnd.dece.sd">>, []};
all_ext(<<"uvvt">>) -> {<<"application">>, <<"vnd.dece.ttml+xml">>, []};
all_ext(<<"uvvu">>) -> {<<"video">>, <<"vnd.uvvu.mp4">>, []};
all_ext(<<"uvv">>) -> {<<"video">>, <<"vnd.dece.video">>, []};
all_ext(<<"uvvv">>) -> {<<"video">>, <<"vnd.dece.video">>, []};
all_ext(<<"uvvx">>) -> {<<"application">>, <<"vnd.dece.unspecified">>, []};
all_ext(<<"uvvz">>) -> {<<"application">>, <<"vnd.dece.zip">>, []};
all_ext(<<"uvx">>) -> {<<"application">>, <<"vnd.dece.unspecified">>, []};
all_ext(<<"uvz">>) -> {<<"application">>, <<"vnd.dece.zip">>, []};
all_ext(<<"vcard">>) -> {<<"text">>, <<"vcard">>, []};
all_ext(<<"vcd">>) -> {<<"application">>, <<"x-cdlink">>, []};
all_ext(<<"vcf">>) -> {<<"text">>, <<"x-vcard">>, []};
all_ext(<<"vcg">>) -> {<<"application">>, <<"vnd.groove-vcard">>, []};
all_ext(<<"vcs">>) -> {<<"text">>, <<"x-vcalendar">>, []};
all_ext(<<"vcx">>) -> {<<"application">>, <<"vnd.vcx">>, []};
all_ext(<<"vis">>) -> {<<"application">>, <<"vnd.visionary">>, []};
all_ext(<<"viv">>) -> {<<"video">>, <<"vnd.vivo">>, []};
all_ext(<<"vob">>) -> {<<"video">>, <<"x-ms-vob">>, []};
all_ext(<<"vor">>) -> {<<"application">>, <<"vnd.stardivision.writer">>, []};
all_ext(<<"vox">>) -> {<<"application">>, <<"x-authorware-bin">>, []};
all_ext(<<"vrml">>) -> {<<"model">>, <<"vrml">>, []};
all_ext(<<"vsd">>) -> {<<"application">>, <<"vnd.visio">>, []};
all_ext(<<"vsf">>) -> {<<"application">>, <<"vnd.vsf">>, []};
all_ext(<<"vss">>) -> {<<"application">>, <<"vnd.visio">>, []};
all_ext(<<"vst">>) -> {<<"application">>, <<"vnd.visio">>, []};
all_ext(<<"vsw">>) -> {<<"application">>, <<"vnd.visio">>, []};
all_ext(<<"vtu">>) -> {<<"model">>, <<"vnd.vtu">>, []};
all_ext(<<"vxml">>) -> {<<"application">>, <<"voicexml+xml">>, []};
all_ext(<<"w3d">>) -> {<<"application">>, <<"x-director">>, []};
all_ext(<<"wad">>) -> {<<"application">>, <<"x-doom">>, []};
all_ext(<<"wav">>) -> {<<"audio">>, <<"x-wav">>, []};
all_ext(<<"wax">>) -> {<<"audio">>, <<"x-ms-wax">>, []};
all_ext(<<"wbmp">>) -> {<<"image">>, <<"vnd.wap.wbmp">>, []};
all_ext(<<"wbs">>) -> {<<"application">>, <<"vnd.criticaltools.wbs+xml">>, []};
all_ext(<<"wbxml">>) -> {<<"application">>, <<"vnd.wap.wbxml">>, []};
all_ext(<<"wcm">>) -> {<<"application">>, <<"vnd.ms-works">>, []};
all_ext(<<"wdb">>) -> {<<"application">>, <<"vnd.ms-works">>, []};
all_ext(<<"wdp">>) -> {<<"image">>, <<"vnd.ms-photo">>, []};
all_ext(<<"weba">>) -> {<<"audio">>, <<"webm">>, []};
all_ext(<<"webm">>) -> {<<"video">>, <<"webm">>, []};
all_ext(<<"webp">>) -> {<<"image">>, <<"webp">>, []};
all_ext(<<"wg">>) -> {<<"application">>, <<"vnd.pmi.widget">>, []};
all_ext(<<"wgt">>) -> {<<"application">>, <<"widget">>, []};
all_ext(<<"wks">>) -> {<<"application">>, <<"vnd.ms-works">>, []};
all_ext(<<"wma">>) -> {<<"audio">>, <<"x-ms-wma">>, []};
all_ext(<<"wmd">>) -> {<<"application">>, <<"x-ms-wmd">>, []};
all_ext(<<"wmf">>) -> {<<"application">>, <<"x-msmetafile">>, []};
all_ext(<<"wmlc">>) -> {<<"application">>, <<"vnd.wap.wmlc">>, []};
all_ext(<<"wmlsc">>) -> {<<"application">>, <<"vnd.wap.wmlscriptc">>, []};
all_ext(<<"wmls">>) -> {<<"text">>, <<"vnd.wap.wmlscript">>, []};
all_ext(<<"wml">>) -> {<<"text">>, <<"vnd.wap.wml">>, []};
all_ext(<<"wm">>) -> {<<"video">>, <<"x-ms-wm">>, []};
all_ext(<<"wmv">>) -> {<<"video">>, <<"x-ms-wmv">>, []};
all_ext(<<"wmx">>) -> {<<"video">>, <<"x-ms-wmx">>, []};
all_ext(<<"wmz">>) -> {<<"application">>, <<"x-msmetafile">>, []};
all_ext(<<"woff">>) -> {<<"application">>, <<"font-woff">>, []};
all_ext(<<"wpd">>) -> {<<"application">>, <<"vnd.wordperfect">>, []};
all_ext(<<"wpl">>) -> {<<"application">>, <<"vnd.ms-wpl">>, []};
all_ext(<<"wps">>) -> {<<"application">>, <<"vnd.ms-works">>, []};
all_ext(<<"wqd">>) -> {<<"application">>, <<"vnd.wqd">>, []};
all_ext(<<"wri">>) -> {<<"application">>, <<"x-mswrite">>, []};
all_ext(<<"wrl">>) -> {<<"model">>, <<"vrml">>, []};
all_ext(<<"wsdl">>) -> {<<"application">>, <<"wsdl+xml">>, []};
all_ext(<<"wspolicy">>) -> {<<"application">>, <<"wspolicy+xml">>, []};
all_ext(<<"wtb">>) -> {<<"application">>, <<"vnd.webturbo">>, []};
all_ext(<<"wvx">>) -> {<<"video">>, <<"x-ms-wvx">>, []};
all_ext(<<"x32">>) -> {<<"application">>, <<"x-authorware-bin">>, []};
all_ext(<<"x3db">>) -> {<<"model">>, <<"x3d+binary">>, []};
all_ext(<<"x3dbz">>) -> {<<"model">>, <<"x3d+binary">>, []};
all_ext(<<"x3d">>) -> {<<"model">>, <<"x3d+xml">>, []};
all_ext(<<"x3dv">>) -> {<<"model">>, <<"x3d+vrml">>, []};
all_ext(<<"x3dvz">>) -> {<<"model">>, <<"x3d+vrml">>, []};
all_ext(<<"x3dz">>) -> {<<"model">>, <<"x3d+xml">>, []};
all_ext(<<"xaml">>) -> {<<"application">>, <<"xaml+xml">>, []};
all_ext(<<"xap">>) -> {<<"application">>, <<"x-silverlight-app">>, []};
all_ext(<<"xar">>) -> {<<"application">>, <<"vnd.xara">>, []};
all_ext(<<"xbap">>) -> {<<"application">>, <<"x-ms-xbap">>, []};
all_ext(<<"xbd">>) -> {<<"application">>, <<"vnd.fujixerox.docuworks.binder">>, []};
all_ext(<<"xbm">>) -> {<<"image">>, <<"x-xbitmap">>, []};
all_ext(<<"xdf">>) -> {<<"application">>, <<"xcap-diff+xml">>, []};
all_ext(<<"xdm">>) -> {<<"application">>, <<"vnd.syncml.dm+xml">>, []};
all_ext(<<"xdp">>) -> {<<"application">>, <<"vnd.adobe.xdp+xml">>, []};
all_ext(<<"xdssc">>) -> {<<"application">>, <<"dssc+xml">>, []};
all_ext(<<"xdw">>) -> {<<"application">>, <<"vnd.fujixerox.docuworks">>, []};
all_ext(<<"xenc">>) -> {<<"application">>, <<"xenc+xml">>, []};
all_ext(<<"xer">>) -> {<<"application">>, <<"patch-ops-error+xml">>, []};
all_ext(<<"xfdf">>) -> {<<"application">>, <<"vnd.adobe.xfdf">>, []};
all_ext(<<"xfdl">>) -> {<<"application">>, <<"vnd.xfdl">>, []};
all_ext(<<"xht">>) -> {<<"application">>, <<"xhtml+xml">>, []};
all_ext(<<"xhtml">>) -> {<<"application">>, <<"xhtml+xml">>, []};
all_ext(<<"xhvml">>) -> {<<"application">>, <<"xv+xml">>, []};
all_ext(<<"xif">>) -> {<<"image">>, <<"vnd.xiff">>, []};
all_ext(<<"xla">>) -> {<<"application">>, <<"vnd.ms-excel">>, []};
all_ext(<<"xlam">>) -> {<<"application">>, <<"vnd.ms-excel.addin.macroenabled.12">>, []};
all_ext(<<"xlc">>) -> {<<"application">>, <<"vnd.ms-excel">>, []};
all_ext(<<"xlf">>) -> {<<"application">>, <<"x-xliff+xml">>, []};
all_ext(<<"xlm">>) -> {<<"application">>, <<"vnd.ms-excel">>, []};
all_ext(<<"xls">>) -> {<<"application">>, <<"vnd.ms-excel">>, []};
all_ext(<<"xlsb">>) -> {<<"application">>, <<"vnd.ms-excel.sheet.binary.macroenabled.12">>, []};
all_ext(<<"xlsm">>) -> {<<"application">>, <<"vnd.ms-excel.sheet.macroenabled.12">>, []};
all_ext(<<"xlsx">>) -> {<<"application">>, <<"vnd.openxmlformats-officedocument.spreadsheetml.sheet">>, []};
all_ext(<<"xlt">>) -> {<<"application">>, <<"vnd.ms-excel">>, []};
all_ext(<<"xltm">>) -> {<<"application">>, <<"vnd.ms-excel.template.macroenabled.12">>, []};
all_ext(<<"xltx">>) -> {<<"application">>, <<"vnd.openxmlformats-officedocument.spreadsheetml.template">>, []};
all_ext(<<"xlw">>) -> {<<"application">>, <<"vnd.ms-excel">>, []};
all_ext(<<"xm">>) -> {<<"audio">>, <<"xm">>, []};
all_ext(<<"xml">>) -> {<<"application">>, <<"xml">>, []};
all_ext(<<"xo">>) -> {<<"application">>, <<"vnd.olpc-sugar">>, []};
all_ext(<<"xop">>) -> {<<"application">>, <<"xop+xml">>, []};
all_ext(<<"xpi">>) -> {<<"application">>, <<"x-xpinstall">>, []};
all_ext(<<"xpl">>) -> {<<"application">>, <<"xproc+xml">>, []};
all_ext(<<"xpm">>) -> {<<"image">>, <<"x-xpixmap">>, []};
all_ext(<<"xpr">>) -> {<<"application">>, <<"vnd.is-xpr">>, []};
all_ext(<<"xps">>) -> {<<"application">>, <<"vnd.ms-xpsdocument">>, []};
all_ext(<<"xpw">>) -> {<<"application">>, <<"vnd.intercon.formnet">>, []};
all_ext(<<"xpx">>) -> {<<"application">>, <<"vnd.intercon.formnet">>, []};
all_ext(<<"xsl">>) -> {<<"application">>, <<"xml">>, []};
all_ext(<<"xslt">>) -> {<<"application">>, <<"xslt+xml">>, []};
all_ext(<<"xsm">>) -> {<<"application">>, <<"vnd.syncml+xml">>, []};
all_ext(<<"xspf">>) -> {<<"application">>, <<"xspf+xml">>, []};
all_ext(<<"xul">>) -> {<<"application">>, <<"vnd.mozilla.xul+xml">>, []};
all_ext(<<"xvm">>) -> {<<"application">>, <<"xv+xml">>, []};
all_ext(<<"xvml">>) -> {<<"application">>, <<"xv+xml">>, []};
all_ext(<<"xwd">>) -> {<<"image">>, <<"x-xwindowdump">>, []};
all_ext(<<"xyz">>) -> {<<"chemical">>, <<"x-xyz">>, []};
all_ext(<<"xz">>) -> {<<"application">>, <<"x-xz">>, []};
all_ext(<<"yang">>) -> {<<"application">>, <<"yang">>, []};
all_ext(<<"yin">>) -> {<<"application">>, <<"yin+xml">>, []};
all_ext(<<"z1">>) -> {<<"application">>, <<"x-zmachine">>, []};
all_ext(<<"z2">>) -> {<<"application">>, <<"x-zmachine">>, []};
all_ext(<<"z3">>) -> {<<"application">>, <<"x-zmachine">>, []};
all_ext(<<"z4">>) -> {<<"application">>, <<"x-zmachine">>, []};
all_ext(<<"z5">>) -> {<<"application">>, <<"x-zmachine">>, []};
all_ext(<<"z6">>) -> {<<"application">>, <<"x-zmachine">>, []};
all_ext(<<"z7">>) -> {<<"application">>, <<"x-zmachine">>, []};
all_ext(<<"z8">>) -> {<<"application">>, <<"x-zmachine">>, []};
all_ext(<<"zaz">>) -> {<<"application">>, <<"vnd.zzazz.deck+xml">>, []};
all_ext(<<"zip">>) -> {<<"application">>, <<"zip">>, []};
all_ext(<<"zir">>) -> {<<"application">>, <<"vnd.zul">>, []};
all_ext(<<"zirz">>) -> {<<"application">>, <<"vnd.zul">>, []};
all_ext(<<"zmm">>) -> {<<"application">>, <<"vnd.handheld-entertainment+xml">>, []};
%% GENERATED
all_ext(_) -> {<<"application">>, <<"octet-stream">>, []}.

web_ext(<<"css">>) -> {<<"text">>, <<"css">>, []};
web_ext(<<"gif">>) -> {<<"image">>, <<"gif">>, []};
web_ext(<<"html">>) -> {<<"text">>, <<"html">>, []};
web_ext(<<"htm">>) -> {<<"text">>, <<"html">>, []};
web_ext(<<"ico">>) -> {<<"image">>, <<"x-icon">>, []};
web_ext(<<"jpeg">>) -> {<<"image">>, <<"jpeg">>, []};
web_ext(<<"jpg">>) -> {<<"image">>, <<"jpeg">>, []};
web_ext(<<"js">>) -> {<<"application">>, <<"javascript">>, []};
web_ext(<<"mp3">>) -> {<<"audio">>, <<"mpeg">>, []};
web_ext(<<"mp4">>) -> {<<"video">>, <<"mp4">>, []};
web_ext(<<"ogg">>) -> {<<"audio">>, <<"ogg">>, []};
web_ext(<<"ogv">>) -> {<<"video">>, <<"ogg">>, []};
web_ext(<<"png">>) -> {<<"image">>, <<"png">>, []};
web_ext(<<"svg">>) -> {<<"image">>, <<"svg+xml">>, []};
web_ext(<<"wav">>) -> {<<"audio">>, <<"x-wav">>, []};
web_ext(<<"webm">>) -> {<<"video">>, <<"webm">>, []};
web_ext(_) -> {<<"application">>, <<"octet-stream">>, []}.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted deps/cowlib/src/cow_mimetypes.erl.src.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
%% Copyright (c) 2013-2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(cow_mimetypes).

-export([all/1]).
-export([web/1]).

%% @doc Return the mimetype for any file by looking at its extension.

-spec all(file:filename_all()) -> {binary(), binary(), []}.
all(Path) ->
	case filename:extension(Path) of
		<<>> -> {<<"application">>, <<"octet-stream">>, []};
		<< $., Ext/binary >> -> all_ext(Ext)
	end.

%% @doc Return the mimetype for a Web related file by looking at its extension.

-spec web(file:filename_all()) -> {binary(), binary(), []}.
web(Path) ->
	case filename:extension(Path) of
		<<>> -> {<<"application">>, <<"octet-stream">>, []};
		<< $., Ext/binary >> -> web_ext(Ext)
	end.

%% Internal.

%% GENERATED
all_ext(_) -> {<<"application">>, <<"octet-stream">>, []}.

web_ext(<<"css">>) -> {<<"text">>, <<"css">>, []};
web_ext(<<"gif">>) -> {<<"image">>, <<"gif">>, []};
web_ext(<<"html">>) -> {<<"text">>, <<"html">>, []};
web_ext(<<"htm">>) -> {<<"text">>, <<"html">>, []};
web_ext(<<"ico">>) -> {<<"image">>, <<"x-icon">>, []};
web_ext(<<"jpeg">>) -> {<<"image">>, <<"jpeg">>, []};
web_ext(<<"jpg">>) -> {<<"image">>, <<"jpeg">>, []};
web_ext(<<"js">>) -> {<<"application">>, <<"javascript">>, []};
web_ext(<<"mp3">>) -> {<<"audio">>, <<"mpeg">>, []};
web_ext(<<"mp4">>) -> {<<"video">>, <<"mp4">>, []};
web_ext(<<"ogg">>) -> {<<"audio">>, <<"ogg">>, []};
web_ext(<<"ogv">>) -> {<<"video">>, <<"ogg">>, []};
web_ext(<<"png">>) -> {<<"image">>, <<"png">>, []};
web_ext(<<"svg">>) -> {<<"image">>, <<"svg+xml">>, []};
web_ext(<<"wav">>) -> {<<"audio">>, <<"x-wav">>, []};
web_ext(<<"webm">>) -> {<<"video">>, <<"webm">>, []};
web_ext(_) -> {<<"application">>, <<"octet-stream">>, []}.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






















































































































Deleted deps/cowlib/src/cow_multipart.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
%% Copyright (c) 2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(cow_multipart).

%% Parsing.
-export([parse_headers/2]).
-export([parse_body/2]).

%% Building.
-export([boundary/0]).
-export([first_part/2]).
-export([part/2]).
-export([close/1]).

%% Headers.
-export([form_data/1]).
-export([parse_content_disposition/1]).
-export([parse_content_transfer_encoding/1]).
-export([parse_content_type/1]).

-type headers() :: [{iodata(), iodata()}].
-export_type([headers/0]).

-include("cow_inline.hrl").

-define(TEST1_MIME, <<
	"This is a message with multiple parts in MIME format.\r\n"
	"--frontier\r\n"
	"Content-Type: text/plain\r\n"
	"\r\n"
	"This is the body of the message.\r\n"
	"--frontier\r\n"
	"Content-Type: application/octet-stream\r\n"
	"Content-Transfer-Encoding: base64\r\n"
	"\r\n"
	"PGh0bWw+CiAgPGhlYWQ+CiAgPC9oZWFkPgogIDxib2R5PgogICAgPHA+VGhpcyBpcyB0aGUg\r\n"
	"Ym9keSBvZiB0aGUgbWVzc2FnZS48L3A+CiAgPC9ib2R5Pgo8L2h0bWw+Cg==\r\n"
	"--frontier--"
>>).
-define(TEST1_BOUNDARY, <<"frontier">>).

-define(TEST2_MIME, <<
	"--AaB03x\r\n"
	"Content-Disposition: form-data; name=\"submit-name\"\r\n"
	"\r\n"
	"Larry\r\n"
	"--AaB03x\r\n"
	"Content-Disposition: form-data; name=\"files\"\r\n"
	"Content-Type: multipart/mixed; boundary=BbC04y\r\n"
	"\r\n"
	"--BbC04y\r\n"
	"Content-Disposition: file; filename=\"file1.txt\"\r\n"
	"Content-Type: text/plain\r\n"
	"\r\n"
	"... contents of file1.txt ...\r\n"
	"--BbC04y\r\n"
	"Content-Disposition: file; filename=\"file2.gif\"\r\n"
	"Content-Type: image/gif\r\n"
	"Content-Transfer-Encoding: binary\r\n"
	"\r\n"
	"...contents of file2.gif...\r\n"
	"--BbC04y--\r\n"
	"--AaB03x--"
>>).
-define(TEST2_BOUNDARY, <<"AaB03x">>).

-define(TEST3_MIME, <<
	"This is the preamble.\r\n"
	"--boundary\r\n"
	"Content-Type: text/plain\r\n"
	"\r\n"
	"This is the body of the message.\r\n"
	"--boundary--"
	"\r\nThis is the epilogue. Here it includes leading CRLF"
>>).
-define(TEST3_BOUNDARY, <<"boundary">>).

-define(TEST4_MIME, <<
	"This is the preamble.\r\n"
	"--boundary\r\n"
	"Content-Type: text/plain\r\n"
	"\r\n"
	"This is the body of the message.\r\n"
	"--boundary--"
	"\r\n"
>>).
-define(TEST4_BOUNDARY, <<"boundary">>).

%% Parsing.
%%
%% The multipart format is defined in RFC 2045.

%% @doc Parse the headers for the next multipart part.
%%
%% This function skips any preamble before the boundary.
%% The preamble may be retrieved using parse_body/2.
%%
%% This function will accept input of any size, it is
%% up to the caller to limit it if needed.

-spec parse_headers(binary(), binary())
	-> more | {more, binary()}
	| {ok, headers(), binary()}
	| {done, binary()}.
%% If the stream starts with the boundary we can make a few assumptions
%% and quickly figure out if we got the complete list of headers.
parse_headers(<< "--", Stream/bits >>, Boundary) ->
	BoundarySize = byte_size(Boundary),
	case Stream of
		%% Last boundary. Return the epilogue.
		<< Boundary:BoundarySize/binary, "--", Stream2/bits >> ->
			{done, Stream2};
		<< Boundary:BoundarySize/binary, Stream2/bits >> ->
			%% We have all the headers only if there is a \r\n\r\n
			%% somewhere in the data after the boundary.
			case binary:match(Stream2, <<"\r\n\r\n">>) of
				nomatch ->
					more;
				_ ->
					before_parse_headers(Stream2)
			end;
		%% If there isn't enough to represent Boundary \r\n\r\n
		%% then we definitely don't have all the headers.
		_ when byte_size(Stream) < byte_size(Boundary) + 4 ->
			more;
		%% Otherwise we have preamble data to skip.
		%% We still got rid of the first two misleading bytes.
		_ ->
			skip_preamble(Stream, Boundary)
	end;
%% Otherwise we have preamble data to skip.
parse_headers(Stream, Boundary) ->
	skip_preamble(Stream, Boundary).

%% We need to find the boundary and a \r\n\r\n after that.
%% Since the boundary isn't at the start, it must be right
%% after a \r\n too.
skip_preamble(Stream, Boundary) ->
	case binary:match(Stream, <<"\r\n--", Boundary/bits >>) of
		%% No boundary, need more data.
		nomatch ->
			%% We can safely skip the size of the stream
			%% minus the last 3 bytes which may be a partial boundary.
			SkipSize = byte_size(Stream) - 3,
			case SkipSize > 0 of
				false ->
					more;
				true ->
					<< _:SkipSize/binary, Stream2/bits >> = Stream,
					{more, Stream2}
			end;
		{Start, Length} ->
			Start2 = Start + Length,
			<< _:Start2/binary, Stream2/bits >> = Stream,
			case Stream2 of
				%% Last boundary. Return the epilogue.
				<< "--", Stream3/bits >> ->
					{done, Stream3};
				_ ->
					case binary:match(Stream, <<"\r\n\r\n">>) of
						%% We don't have the full headers.
						nomatch ->
							{more, Stream2};
						_ ->
							before_parse_headers(Stream2)
					end
			end
	end.

%% There is a line break right after the boundary, skip it.
%%
%% We only skip it now because there might be no headers at all,
%% which means the \r\n\r\n indicating the end of headers also
%% includes this line break.
before_parse_headers(<< "\r\n", Stream/bits >>) ->
	parse_hd_name(Stream, [], <<>>).

parse_hd_name(<< C, Rest/bits >>, H, SoFar) ->
	case C of
		$: -> parse_hd_before_value(Rest, H, SoFar);
		$\s -> parse_hd_name_ws(Rest, H, SoFar);
		$\t -> parse_hd_name_ws(Rest, H, SoFar);
		?INLINE_LOWERCASE(parse_hd_name, Rest, H, SoFar)
	end.

parse_hd_name_ws(<< C, Rest/bits >>, H, Name) ->
	case C of
		$\s -> parse_hd_name_ws(Rest, H, Name);
		$\t -> parse_hd_name_ws(Rest, H, Name);
		$: -> parse_hd_before_value(Rest, H, Name)
	end.

parse_hd_before_value(<< $\s, Rest/bits >>, H, N) ->
	parse_hd_before_value(Rest, H, N);
parse_hd_before_value(<< $\t, Rest/bits >>, H, N) ->
	parse_hd_before_value(Rest, H, N);
parse_hd_before_value(Buffer, H, N) ->
	parse_hd_value(Buffer, H, N, <<>>).

parse_hd_value(<< $\r, Rest/bits >>, Headers, Name, SoFar) ->
	case Rest of
		<< "\n\r\n", Rest2/bits >> ->
			{ok, [{Name, SoFar}|Headers], Rest2};
		<< $\n, C, Rest2/bits >> when C =:= $\s; C =:= $\t ->
			parse_hd_value(Rest2, Headers, Name, SoFar);
		<< $\n, Rest2/bits >> ->
			parse_hd_name(Rest2, [{Name, SoFar}|Headers], <<>>)
	end;
parse_hd_value(<< C, Rest/bits >>, H, N, SoFar) ->
	parse_hd_value(Rest, H, N, << SoFar/binary, C >>).

%% @doc Parse the body of the current multipart part.
%%
%% The body is everything until the next boundary.

-spec parse_body(binary(), binary())
	-> {ok, binary()} | {ok, binary(), binary()}
	| done | {done, binary()} | {done, binary(), binary()}.
parse_body(Stream, Boundary) ->
	BoundarySize = byte_size(Boundary),
	case Stream of
		<< "--", Boundary:BoundarySize/binary, _/bits >> ->
			done;
		_ ->
			case binary:match(Stream, << "\r\n--", Boundary/bits >>) of
				%% No boundary, check for a possible partial at the end.
				%% Return more or less of the body depending on the result.
				nomatch ->
					StreamSize = byte_size(Stream),
					From = StreamSize - BoundarySize - 3,
					MatchOpts = if
						%% Binary too small to contain boundary, check it fully.
						From < 0 -> [];
						%% Optimize, only check the end of the binary.
						true -> [{scope, {From, StreamSize - From}}]
					end,
					case binary:match(Stream, <<"\r">>, MatchOpts) of
						nomatch ->
							{ok, Stream};
						{Pos, _} ->
							case Stream of
								<< Body:Pos/binary >> ->
									{ok, Body};
								<< Body:Pos/binary, Rest/bits >> ->
									{ok, Body, Rest}
							end
					end;
				%% Boundary found, this is the last chunk of the body.
				{Pos, _} ->
					case Stream of
						<< Body:Pos/binary, "\r\n" >> ->
							{done, Body};
						<< Body:Pos/binary, "\r\n", Rest/bits >> ->
							{done, Body, Rest};
						<< Body:Pos/binary, Rest/bits >> ->
							{done, Body, Rest}
					end
			end
	end.

-ifdef(TEST).
parse_test() ->
	H1 = [{<<"content-type">>, <<"text/plain">>}],
	Body1 = <<"This is the body of the message.">>,
	H2 = lists:sort([{<<"content-type">>, <<"application/octet-stream">>},
		{<<"content-transfer-encoding">>, <<"base64">>}]),
	Body2 = <<"PGh0bWw+CiAgPGhlYWQ+CiAgPC9oZWFkPgogIDxib2R5PgogICAgPHA+VGhpcyBpcyB0aGUg\r\n"
		"Ym9keSBvZiB0aGUgbWVzc2FnZS48L3A+CiAgPC9ib2R5Pgo8L2h0bWw+Cg==">>,
	{ok, H1, Rest} = parse_headers(?TEST1_MIME, ?TEST1_BOUNDARY),
	{done, Body1, Rest2} = parse_body(Rest, ?TEST1_BOUNDARY),
	done = parse_body(Rest2, ?TEST1_BOUNDARY),
	{ok, H2Unsorted, Rest3} = parse_headers(Rest2, ?TEST1_BOUNDARY),
	H2 = lists:sort(H2Unsorted),
	{done, Body2, Rest4} = parse_body(Rest3, ?TEST1_BOUNDARY),
	done = parse_body(Rest4, ?TEST1_BOUNDARY),
	{done, <<>>} = parse_headers(Rest4, ?TEST1_BOUNDARY),
	ok.

parse_interleaved_test() ->
	H1 = [{<<"content-disposition">>, <<"form-data; name=\"submit-name\"">>}],
	Body1 = <<"Larry">>,
	H2 = lists:sort([{<<"content-disposition">>, <<"form-data; name=\"files\"">>},
		{<<"content-type">>, <<"multipart/mixed; boundary=BbC04y">>}]),
	InH1 = lists:sort([{<<"content-disposition">>, <<"file; filename=\"file1.txt\"">>},
		{<<"content-type">>, <<"text/plain">>}]),
	InBody1 = <<"... contents of file1.txt ...">>,
	InH2 = lists:sort([{<<"content-disposition">>, <<"file; filename=\"file2.gif\"">>},
		{<<"content-type">>, <<"image/gif">>},
		{<<"content-transfer-encoding">>, <<"binary">>}]),
	InBody2 = <<"...contents of file2.gif...">>,
	{ok, H1, Rest} = parse_headers(?TEST2_MIME, ?TEST2_BOUNDARY),
	{done, Body1, Rest2} = parse_body(Rest, ?TEST2_BOUNDARY),
	done = parse_body(Rest2, ?TEST2_BOUNDARY),
	{ok, H2Unsorted, Rest3} = parse_headers(Rest2, ?TEST2_BOUNDARY),
	H2 = lists:sort(H2Unsorted),
	{_, ContentType} = lists:keyfind(<<"content-type">>, 1, H2),
	{<<"multipart">>, <<"mixed">>, [{<<"boundary">>, InBoundary}]}
		= parse_content_type(ContentType),
	{ok, InH1Unsorted, InRest} = parse_headers(Rest3, InBoundary),
	InH1 = lists:sort(InH1Unsorted),
	{done, InBody1, InRest2} = parse_body(InRest, InBoundary),
	done = parse_body(InRest2, InBoundary),
	{ok, InH2Unsorted, InRest3} = parse_headers(InRest2, InBoundary),
	InH2 = lists:sort(InH2Unsorted),
	{done, InBody2, InRest4} = parse_body(InRest3, InBoundary),
	done = parse_body(InRest4, InBoundary),
	{done, Rest4} = parse_headers(InRest4, InBoundary),
	{done, <<>>} = parse_headers(Rest4, ?TEST2_BOUNDARY),
	ok.

parse_epilogue_test() ->
	H1 = [{<<"content-type">>, <<"text/plain">>}],
	Body1 = <<"This is the body of the message.">>,
	Epilogue = <<"\r\nThis is the epilogue. Here it includes leading CRLF">>,
	{ok, H1, Rest} = parse_headers(?TEST3_MIME, ?TEST3_BOUNDARY),
	{done, Body1, Rest2} = parse_body(Rest, ?TEST3_BOUNDARY),
	done = parse_body(Rest2, ?TEST3_BOUNDARY),
	{done, Epilogue} = parse_headers(Rest2, ?TEST3_BOUNDARY),
	ok.

parse_epilogue_crlf_test() ->
	H1 = [{<<"content-type">>, <<"text/plain">>}],
	Body1 = <<"This is the body of the message.">>,
	Epilogue = <<"\r\n">>,
	{ok, H1, Rest} = parse_headers(?TEST4_MIME, ?TEST4_BOUNDARY),
	{done, Body1, Rest2} = parse_body(Rest, ?TEST4_BOUNDARY),
	done = parse_body(Rest2, ?TEST4_BOUNDARY),
	{done, Epilogue} = parse_headers(Rest2, ?TEST4_BOUNDARY),
	ok.

parse_partial_test() ->
	{ok, <<0:8000, "abcdef">>, <<"\rghij">>}
		= parse_body(<<0:8000, "abcdef\rghij">>, <<"boundary">>),
	{ok, <<"abcdef">>, <<"\rghij">>}
		= parse_body(<<"abcdef\rghij">>, <<"boundary">>),
	{ok, <<"abc">>, <<"\rdef">>}
		= parse_body(<<"abc\rdef">>, <<"boundaryboundary">>),
	{ok, <<0:8000, "abcdef">>, <<"\r\nghij">>}
		= parse_body(<<0:8000, "abcdef\r\nghij">>, <<"boundary">>),
	{ok, <<"abcdef">>, <<"\r\nghij">>}
		= parse_body(<<"abcdef\r\nghij">>, <<"boundary">>),
	{ok, <<"abc">>, <<"\r\ndef">>}
		= parse_body(<<"abc\r\ndef">>, <<"boundaryboundary">>),
	{ok, <<"boundary">>, <<"\r">>}
		= parse_body(<<"boundary\r">>, <<"boundary">>),
	{ok, <<"boundary">>, <<"\r\n">>}
		= parse_body(<<"boundary\r\n">>, <<"boundary">>),
	{ok, <<"boundary">>, <<"\r\n-">>}
		= parse_body(<<"boundary\r\n-">>, <<"boundary">>),
	{ok, <<"boundary">>, <<"\r\n--">>}
		= parse_body(<<"boundary\r\n--">>, <<"boundary">>),
	ok.
-endif.

-ifdef(PERF).
perf_parse_multipart(Stream, Boundary) ->
	case parse_headers(Stream, Boundary) of
		{ok, _, Rest} ->
			{_, _, Rest2} = parse_body(Rest, Boundary),
			perf_parse_multipart(Rest2, Boundary);
		{done, _} ->
			ok
	end.

horse_parse() ->
	horse:repeat(50000,
		perf_parse_multipart(?TEST1_MIME, ?TEST1_BOUNDARY)
	).
-endif.

%% Building.

%% @doc Generate a new random boundary.
%%
%% The boundary generated has a low probability of ever appearing
%% in the data.

-spec boundary() -> binary().
boundary() ->
	base64:encode(crypto:rand_bytes(48)).

%% @doc Return the first part's head.
%%
%% This works exactly like the part/2 function except there is
%% no leading \r\n. It's not required to use this function,
%% just makes the output a little smaller and prettier.

-spec first_part(binary(), headers()) -> iodata().
first_part(Boundary, Headers) ->
	[<<"--">>, Boundary, <<"\r\n">>, headers_to_iolist(Headers, [])].

%% @doc Return a part's head.

-spec part(binary(), headers()) -> iodata().
part(Boundary, Headers) ->
	[<<"\r\n--">>, Boundary, <<"\r\n">>, headers_to_iolist(Headers, [])].

headers_to_iolist([], Acc) ->
	lists:reverse([<<"\r\n">>|Acc]);
headers_to_iolist([{N, V}|Tail], Acc) ->
	%% We don't want to create a sublist so we list the
	%% values in reverse order so that it gets reversed properly.
	headers_to_iolist(Tail, [<<"\r\n">>, V, <<": ">>, N|Acc]).

%% @doc Return the closing delimiter of the multipart message.

-spec close(binary()) -> iodata().
close(Boundary) ->
	[<<"\r\n--">>, Boundary, <<"--">>].

-ifdef(TEST).
build_test() ->
	Result = string:to_lower(binary_to_list(?TEST1_MIME)),
	Result = string:to_lower(binary_to_list(iolist_to_binary([
		<<"This is a message with multiple parts in MIME format.\r\n">>,
		first_part(?TEST1_BOUNDARY, [{<<"content-type">>, <<"text/plain">>}]),
		<<"This is the body of the message.">>,
		part(?TEST1_BOUNDARY, [
			{<<"content-type">>, <<"application/octet-stream">>},
			{<<"content-transfer-encoding">>, <<"base64">>}]),
		<<"PGh0bWw+CiAgPGhlYWQ+CiAgPC9oZWFkPgogIDxib2R5PgogICAgPHA+VGhpcyBpcyB0aGUg\r\n"
			"Ym9keSBvZiB0aGUgbWVzc2FnZS48L3A+CiAgPC9ib2R5Pgo8L2h0bWw+Cg==">>,
		close(?TEST1_BOUNDARY)
	]))),
	ok.

identity_test() ->
	B = boundary(),
	Preamble = <<"This is a message with multiple parts in MIME format.">>,
	H1 = [{<<"content-type">>, <<"text/plain">>}],
	Body1 = <<"This is the body of the message.">>,
	H2 = lists:sort([{<<"content-type">>, <<"application/octet-stream">>},
		{<<"content-transfer-encoding">>, <<"base64">>}]),
	Body2 = <<"PGh0bWw+CiAgPGhlYWQ+CiAgPC9oZWFkPgogIDxib2R5PgogICAgPHA+VGhpcyBpcyB0aGUg\r\n"
		"Ym9keSBvZiB0aGUgbWVzc2FnZS48L3A+CiAgPC9ib2R5Pgo8L2h0bWw+Cg==">>,
	Epilogue = <<"Gotta go fast!">>,
	M = iolist_to_binary([
		Preamble,
		part(B, H1), Body1,
		part(B, H2), Body2,
		close(B),
		Epilogue
	]),
	{done, Preamble, M2} = parse_body(M, B),
	{ok, H1, M3} = parse_headers(M2, B),
	{done, Body1, M4} = parse_body(M3, B),
	{ok, H2Unsorted, M5} = parse_headers(M4, B),
	H2 = lists:sort(H2Unsorted),
	{done, Body2, M6} = parse_body(M5, B),
	{done, Epilogue} = parse_headers(M6, B),
	ok.
-endif.

-ifdef(PERF).
perf_build_multipart() ->
	B = boundary(),
	[
		<<"preamble\r\n">>,
		first_part(B, [{<<"content-type">>, <<"text/plain">>}]),
		<<"This is the body of the message.">>,
		part(B, [
			{<<"content-type">>, <<"application/octet-stream">>},
			{<<"content-transfer-encoding">>, <<"base64">>}]),
		<<"PGh0bWw+CiAgPGhlYWQ+CiAgPC9oZWFkPgogIDxib2R5PgogICAgPHA+VGhpcyBpcyB0aGUg\r\n"
			"Ym9keSBvZiB0aGUgbWVzc2FnZS48L3A+CiAgPC9ib2R5Pgo8L2h0bWw+Cg==">>,
		close(B),
		<<"epilogue">>
	].

horse_build() ->
	horse:repeat(50000,
		perf_build_multipart()
	).
-endif.

%% Headers.

%% @doc Convenience function for extracting information from headers
%% when parsing a multipart/form-data stream.

-spec form_data(headers())
	-> {data, binary()}
	| {file, binary(), binary(), binary(), binary()}.
form_data(Headers) ->
	{_, DispositionBin} = lists:keyfind(<<"content-disposition">>, 1, Headers),
	{<<"form-data">>, Params} = parse_content_disposition(DispositionBin),
	{_, FieldName} = lists:keyfind(<<"name">>, 1, Params),
	case lists:keyfind(<<"filename">>, 1, Params) of
		false ->
			{data, FieldName};
		{_, Filename} ->
			Type = case lists:keyfind(<<"content-type">>, 1, Headers) of
				false -> <<"text/plain">>;
				{_, T} -> T
			end,
			TransferEncoding = case lists:keyfind(
					<<"content-transfer-encoding">>, 1, Headers) of
				false -> <<"7bit">>;
				{_, TE} -> TE
			end,
			{file, FieldName, Filename, Type, TransferEncoding}
	end.

-ifdef(TEST).
form_data_test_() ->
	Tests = [
		{[{<<"content-disposition">>, <<"form-data; name=\"submit-name\"">>}],
			{data, <<"submit-name">>}},
		{[{<<"content-disposition">>,
				<<"form-data; name=\"files\"; filename=\"file1.txt\"">>},
			{<<"content-type">>, <<"text/x-plain">>}],
			{file, <<"files">>, <<"file1.txt">>,
				<<"text/x-plain">>, <<"7bit">>}}
	],
	[{lists:flatten(io_lib:format("~p", [V])),
		fun() -> R = form_data(V) end} || {V, R} <- Tests].
-endif.

%% @todo parse_content_description
%% @todo parse_content_id

%% @doc Parse an RFC 2183 content-disposition value.
%% @todo Support RFC 2231.

-spec parse_content_disposition(binary())
	-> {binary(), [{binary(), binary()}]}.
parse_content_disposition(Bin) ->
	parse_cd_type(Bin, <<>>).

parse_cd_type(<<>>, Acc) ->
	{Acc, []};
parse_cd_type(<< C, Rest/bits >>, Acc) ->
	case C of
		$; -> {Acc, parse_before_param(Rest, [])};
		$\s -> {Acc, parse_before_param(Rest, [])};
		$\t -> {Acc, parse_before_param(Rest, [])};
		?INLINE_LOWERCASE(parse_cd_type, Rest, Acc)
	end.

-ifdef(TEST).
parse_content_disposition_test_() ->
	Tests = [
		{<<"inline">>, {<<"inline">>, []}},
		{<<"attachment">>, {<<"attachment">>, []}},
		{<<"attachment; filename=genome.jpeg;"
			"  modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\";">>,
			{<<"attachment">>, [
				{<<"filename">>, <<"genome.jpeg">>},
				{<<"modification-date">>, <<"Wed, 12 Feb 1997 16:29:51 -0500">>}
			]}},
		{<<"form-data; name=\"user\"">>,
			{<<"form-data">>, [{<<"name">>, <<"user">>}]}},
		{<<"form-data; NAME=\"submit-name\"">>,
			{<<"form-data">>, [{<<"name">>, <<"submit-name">>}]}},
		{<<"form-data; name=\"files\"; filename=\"file1.txt\"">>,
			{<<"form-data">>, [
				{<<"name">>, <<"files">>},
				{<<"filename">>, <<"file1.txt">>}
			]}},
		{<<"file; filename=\"file1.txt\"">>,
			{<<"file">>, [{<<"filename">>, <<"file1.txt">>}]}},
		{<<"file; filename=\"file2.gif\"">>,
			{<<"file">>, [{<<"filename">>, <<"file2.gif">>}]}}
	],
	[{V, fun() -> R = parse_content_disposition(V) end} || {V, R} <- Tests].
-endif.

-ifdef(PERF).
horse_parse_content_disposition_attachment() ->
	horse:repeat(100000,
		parse_content_disposition(<<"attachment; filename=genome.jpeg;"
			"  modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\";">>)
	).

horse_parse_content_disposition_form_data() ->
	horse:repeat(100000,
		parse_content_disposition(
			<<"form-data; name=\"files\"; filename=\"file1.txt\"">>)
	).

horse_parse_content_disposition_inline() ->
	horse:repeat(100000,
		parse_content_disposition(<<"inline">>)
	).
-endif.

%% @doc Parse an RFC 2045 content-transfer-encoding header.

-spec parse_content_transfer_encoding(binary()) -> binary().
parse_content_transfer_encoding(Bin) ->
	?INLINE_LOWERCASE_BC(Bin).

-ifdef(TEST).
parse_content_transfer_encoding_test_() ->
	Tests = [
		{<<"7bit">>, <<"7bit">>},
		{<<"7BIT">>, <<"7bit">>},
		{<<"8bit">>, <<"8bit">>},
		{<<"binary">>, <<"binary">>},
		{<<"quoted-printable">>, <<"quoted-printable">>},
		{<<"base64">>, <<"base64">>},
		{<<"Base64">>, <<"base64">>},
		{<<"BASE64">>, <<"base64">>},
		{<<"bAsE64">>, <<"base64">>}
	],
	[{V, fun() -> R = parse_content_transfer_encoding(V) end}
		|| {V, R} <- Tests].
-endif.

-ifdef(PERF).
horse_parse_content_transfer_encoding() ->
	horse:repeat(100000,
		parse_content_transfer_encoding(<<"QUOTED-PRINTABLE">>)
	).
-endif.

%% @doc Parse an RFC 2045 content-type header.

-spec parse_content_type(binary())
	-> {binary(), binary(), [{binary(), binary()}]}.
parse_content_type(Bin) ->
	parse_ct_type(Bin, <<>>).

parse_ct_type(<< C, Rest/bits >>, Acc) ->
	case C of
		$/ -> parse_ct_subtype(Rest, Acc, <<>>);
		?INLINE_LOWERCASE(parse_ct_type, Rest, Acc)
	end.

parse_ct_subtype(<<>>, Type, Subtype) when Subtype =/= <<>> ->
	{Type, Subtype, []};
parse_ct_subtype(<< C, Rest/bits >>, Type, Acc) ->
	case C of
		$; -> {Type, Acc, parse_before_param(Rest, [])};
		$\s -> {Type, Acc, parse_before_param(Rest, [])};
		$\t -> {Type, Acc, parse_before_param(Rest, [])};
		?INLINE_LOWERCASE(parse_ct_subtype, Rest, Type, Acc)
	end.

-ifdef(TEST).
parse_content_type_test_() ->
	Tests = [
		{<<"image/gif">>,
			{<<"image">>, <<"gif">>, []}},
		{<<"text/plain">>,
			{<<"text">>, <<"plain">>, []}},
		{<<"text/plain; charset=us-ascii">>,
			{<<"text">>, <<"plain">>, [{<<"charset">>, <<"us-ascii">>}]}},
		{<<"text/plain; charset=\"us-ascii\"">>,
			{<<"text">>, <<"plain">>, [{<<"charset">>, <<"us-ascii">>}]}},
		{<<"multipart/form-data; boundary=AaB03x">>,
			{<<"multipart">>, <<"form-data">>,
				[{<<"boundary">>, <<"AaB03x">>}]}},
		{<<"multipart/mixed; boundary=BbC04y">>,
			{<<"multipart">>, <<"mixed">>, [{<<"boundary">>, <<"BbC04y">>}]}},
		{<<"multipart/mixed; boundary=--------">>,
			{<<"multipart">>, <<"mixed">>, [{<<"boundary">>, <<"--------">>}]}},
		{<<"application/x-horse; filename=genome.jpeg;"
				"  some-date=\"Wed, 12 Feb 1997 16:29:51 -0500\";"
				"  charset=us-ascii; empty=; number=12345">>,
			{<<"application">>, <<"x-horse">>, [
				{<<"filename">>, <<"genome.jpeg">>},
				{<<"some-date">>, <<"Wed, 12 Feb 1997 16:29:51 -0500">>},
				{<<"charset">>, <<"us-ascii">>},
				{<<"empty">>, <<>>},
				{<<"number">>, <<"12345">>}
			]}}
	],
	[{V, fun() -> R = parse_content_type(V) end}
		|| {V, R} <- Tests].
-endif.

-ifdef(PERF).
horse_parse_content_type_zero() ->
	horse:repeat(100000,
		parse_content_type(<<"text/plain">>)
	).

horse_parse_content_type_one() ->
	horse:repeat(100000,
		parse_content_type(<<"text/plain; charset=\"us-ascii\"">>)
	).

horse_parse_content_type_five() ->
	horse:repeat(100000,
		parse_content_type(<<"application/x-horse; filename=genome.jpeg;"
			"  some-date=\"Wed, 12 Feb 1997 16:29:51 -0500\";"
			"  charset=us-ascii; empty=; number=12345">>)
	).
-endif.

%% @doc Parse RFC 2045 parameters.

parse_before_param(<<>>, Params) ->
	lists:reverse(Params);
parse_before_param(<< C, Rest/bits >>, Params) ->
	case C of
		$; -> parse_before_param(Rest, Params);
		$\s -> parse_before_param(Rest, Params);
		$\t -> parse_before_param(Rest, Params);
		?INLINE_LOWERCASE(parse_param_name, Rest, Params, <<>>)
	end.

parse_param_name(<<>>, Params, Acc) ->
	lists:reverse([{Acc, <<>>}|Params]);
parse_param_name(<< C, Rest/bits >>, Params, Acc) ->
	case C of
		$= -> parse_param_value(Rest, Params, Acc);
		?INLINE_LOWERCASE(parse_param_name, Rest, Params, Acc)
	end.

parse_param_value(<<>>, Params, Name) ->
	lists:reverse([{Name, <<>>}|Params]);
parse_param_value(<< C, Rest/bits >>, Params, Name) ->
	case C of
		$" -> parse_param_quoted_value(Rest, Params, Name, <<>>);
		$; -> parse_before_param(Rest, [{Name, <<>>}|Params]);
		$\s -> parse_before_param(Rest, [{Name, <<>>}|Params]);
		$\t -> parse_before_param(Rest, [{Name, <<>>}|Params]);
		C -> parse_param_value(Rest, Params, Name, << C >>)
	end.

parse_param_value(<<>>, Params, Name, Acc) ->
	lists:reverse([{Name, Acc}|Params]);
parse_param_value(<< C, Rest/bits >>, Params, Name, Acc) ->
	case C of
		$; -> parse_before_param(Rest, [{Name, Acc}|Params]);
		$\s -> parse_before_param(Rest, [{Name, Acc}|Params]);
		$\t -> parse_before_param(Rest, [{Name, Acc}|Params]);
		C -> parse_param_value(Rest, Params, Name, << Acc/binary, C >>)
	end.

%% We expect a final $" so no need to test for <<>>.
parse_param_quoted_value(<< $\\, C, Rest/bits >>, Params, Name, Acc) ->
	parse_param_quoted_value(Rest, Params, Name, << Acc/binary, C >>);
parse_param_quoted_value(<< $", Rest/bits >>, Params, Name, Acc) ->
	parse_before_param(Rest, [{Name, Acc}|Params]);
parse_param_quoted_value(<< C, Rest/bits >>, Params, Name, Acc)
		when C =/= $\r ->
	parse_param_quoted_value(Rest, Params, Name, << Acc/binary, C >>).
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted deps/cowlib/src/cow_qs.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
%% Copyright (c) 2013-2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(cow_qs).

-export([parse_qs/1]).
-export([qs/1]).
-export([urldecode/1]).
-export([urlencode/1]).

-type qs_vals() :: [{binary(), binary() | true}].

%% @doc Parse an application/x-www-form-urlencoded string.
%%
%% The percent decoding is inlined to greatly improve the performance
%% by avoiding copying binaries twice (once for extracting, once for
%% decoding) instead of just extracting the proper representation.

-spec parse_qs(binary()) -> qs_vals().
parse_qs(B) ->
	parse_qs_name(B, [], <<>>).

parse_qs_name(<< $%, H, L, Rest/bits >>, Acc, Name) ->
	C = (unhex(H) bsl 4 bor unhex(L)),
	parse_qs_name(Rest, Acc, << Name/bits, C >>);
parse_qs_name(<< $+, Rest/bits >>, Acc, Name) ->
	parse_qs_name(Rest, Acc, << Name/bits, " " >>);
parse_qs_name(<< $=, Rest/bits >>, Acc, Name) when Name =/= <<>> ->
	parse_qs_value(Rest, Acc, Name, <<>>);
parse_qs_name(<< $&, Rest/bits >>, Acc, Name) ->
	case Name of
		<<>> -> parse_qs_name(Rest, Acc, <<>>);
		_ -> parse_qs_name(Rest, [{Name, true}|Acc], <<>>)
	end;
parse_qs_name(<< C, Rest/bits >>, Acc, Name) when C =/= $%, C =/= $= ->
	parse_qs_name(Rest, Acc, << Name/bits, C >>);
parse_qs_name(<<>>, Acc, Name) ->
	case Name of
		<<>> -> lists:reverse(Acc);
		_ -> lists:reverse([{Name, true}|Acc])
	end.

parse_qs_value(<< $%, H, L, Rest/bits >>, Acc, Name, Value) ->
	C = (unhex(H) bsl 4 bor unhex(L)),
	parse_qs_value(Rest, Acc, Name, << Value/bits, C >>);
parse_qs_value(<< $+, Rest/bits >>, Acc, Name, Value) ->
	parse_qs_value(Rest, Acc, Name, << Value/bits, " " >>);
parse_qs_value(<< $&, Rest/bits >>, Acc, Name, Value) ->
	parse_qs_name(Rest, [{Name, Value}|Acc], <<>>);
parse_qs_value(<< C, Rest/bits >>, Acc, Name, Value) when C =/= $% ->
	parse_qs_value(Rest, Acc, Name, << Value/bits, C >>);
parse_qs_value(<<>>, Acc, Name, Value) ->
	lists:reverse([{Name, Value}|Acc]).

-ifdef(TEST).
parse_qs_test_() ->
	Tests = [
		{<<>>, []},
		{<<"&">>, []},
		{<<"a">>, [{<<"a">>, true}]},
		{<<"a&">>, [{<<"a">>, true}]},
		{<<"&a">>, [{<<"a">>, true}]},
		{<<"a&b">>, [{<<"a">>, true}, {<<"b">>, true}]},
		{<<"a&&b">>, [{<<"a">>, true}, {<<"b">>, true}]},
		{<<"a&b&">>, [{<<"a">>, true}, {<<"b">>, true}]},
		{<<"=">>, error},
		{<<"=b">>, error},
		{<<"a=">>, [{<<"a">>, <<>>}]},
		{<<"a=b">>, [{<<"a">>, <<"b">>}]},
		{<<"a=&b=">>, [{<<"a">>, <<>>}, {<<"b">>, <<>>}]},
		{<<"a=b&c&d=e">>, [{<<"a">>, <<"b">>},
			{<<"c">>, true}, {<<"d">>, <<"e">>}]},
		{<<"a=b=c&d=e=f&g=h=i">>, [{<<"a">>, <<"b=c">>},
			{<<"d">>, <<"e=f">>}, {<<"g">>, <<"h=i">>}]},
		{<<"+">>, [{<<" ">>, true}]},
		{<<"+=+">>, [{<<" ">>, <<" ">>}]},
		{<<"a+b=c+d">>, [{<<"a b">>, <<"c d">>}]},
		{<<"+a+=+b+&+c+=+d+">>, [{<<" a ">>, <<" b ">>},
			{<<" c ">>, <<" d ">>}]},
		{<<"a%20b=c%20d">>, [{<<"a b">>, <<"c d">>}]},
		{<<"%25%26%3D=%25%26%3D&_-.=.-_">>, [{<<"%&=">>, <<"%&=">>},
			{<<"_-.">>, <<".-_">>}]},
		{<<"for=extend%2Franch">>, [{<<"for">>, <<"extend/ranch">>}]}
	],
	[{Qs, fun() ->
		E = try parse_qs(Qs) of
			R -> R
		catch _:_ ->
			error
		end
	end} || {Qs, E} <- Tests].

parse_qs_identity_test_() ->
	Tests = [
		<<"+">>,
		<<"hl=en&q=erlang+cowboy">>,
		<<"direction=desc&for=extend%2Franch&sort=updated&state=open">>,
		<<"i=EWiIXmPj5gl6&v=QowBp0oDLQXdd4x_GwiywA&ip=98.20.31.81&"
			"la=en&pg=New8.undertonebrandsafe.com%2F698a2525065ee2"
			"60c0b2f2aaad89ab82&re=&sz=1&fc=1&fr=140&br=3&bv=11.0."
			"696.16&os=3&ov=&rs=vpl&k=cookies%7Csale%7Cbrowser%7Cm"
			"ore%7Cprivacy%7Cstatistics%7Cactivities%7Cauction%7Ce"
			"mail%7Cfree%7Cin...&t=112373&xt=5%7C61%7C0&tz=-1&ev=x"
			"&tk=&za=1&ortb-za=1&zu=&zl=&ax=U&ay=U&ortb-pid=536454"
			".55&ortb-sid=112373.8&seats=999&ortb-xt=IAB24&ortb-ugc=">>,
		<<"i=9pQNskA&v=0ySQQd1F&ev=12345678&t=12345&sz=3&ip=67.58."
			"236.89&la=en&pg=http%3A%2F%2Fwww.yahoo.com%2Fpage1.ht"
			"m&re=http%3A%2F%2Fsearch.google.com&fc=1&fr=1&br=2&bv"
			"=3.0.14&os=1&ov=XP&k=cars%2Cford&rs=js&xt=5%7C22%7C23"
			"4&tz=%2B180&tk=key1%3Dvalue1%7Ckey2%3Dvalue2&zl=4%2C5"
			"%2C6&za=4&zu=competitor.com&ua=Mozilla%2F5.0+%28Windo"
			"ws%3B+U%3B+Windows+NT+6.1%3B+en-US%29+AppleWebKit%2F5"
			"34.13+%28KHTML%2C+like+Gecko%29+Chrome%2F9.0.597.98+S"
			"afari%2F534.13&ortb-za=1%2C6%2C13&ortb-pid=521732&ort"
			"b-sid=521732&ortb-xt=IAB3&ortb-ugc=">>
	],
	[{V, fun() -> V = qs(parse_qs(V)) end} || V <- Tests].
-endif.

-ifdef(PERF).
horse_parse_qs_shorter() ->
	horse:repeat(20000,
		parse_qs(<<"hl=en&q=erlang%20cowboy">>)
	).

horse_parse_qs_short() ->
	horse:repeat(20000,
		parse_qs(
			<<"direction=desc&for=extend%2Franch&sort=updated&state=open">>)
	).

horse_parse_qs_long() ->
	horse:repeat(20000,
		parse_qs(<<"i=EWiIXmPj5gl6&v=QowBp0oDLQXdd4x_GwiywA&ip=98.20.31.81&"
			"la=en&pg=New8.undertonebrandsafe.com%2F698a2525065ee260c0b2f2a"
			"aad89ab82&re=&sz=1&fc=1&fr=140&br=3&bv=11.0.696.16&os=3&ov=&rs"
			"=vpl&k=cookies%7Csale%7Cbrowser%7Cmore%7Cprivacy%7Cstatistics%"
			"7Cactivities%7Cauction%7Cemail%7Cfree%7Cin...&t=112373&xt=5%7C"
			"61%7C0&tz=-1&ev=x&tk=&za=1&ortb-za=1&zu=&zl=&ax=U&ay=U&ortb-pi"
			"d=536454.55&ortb-sid=112373.8&seats=999&ortb-xt=IAB24&ortb-ugc"
			"=">>)
	).

horse_parse_qs_longer() ->
	horse:repeat(20000,
		parse_qs(<<"i=9pQNskA&v=0ySQQd1F&ev=12345678&t=12345&sz=3&ip=67.58."
			"236.89&la=en&pg=http%3A%2F%2Fwww.yahoo.com%2Fpage1.htm&re=http"
			"%3A%2F%2Fsearch.google.com&fc=1&fr=1&br=2&bv=3.0.14&os=1&ov=XP"
			"&k=cars%2cford&rs=js&xt=5%7c22%7c234&tz=%2b180&tk=key1%3Dvalue"
			"1%7Ckey2%3Dvalue2&zl=4,5,6&za=4&zu=competitor.com&ua=Mozilla%2"
			"F5.0%20(Windows%3B%20U%3B%20Windows%20NT%206.1%3B%20en-US)%20A"
			"ppleWebKit%2F534.13%20(KHTML%2C%20like%20Gecko)%20Chrome%2F9.0"
			".597.98%20Safari%2F534.13&ortb-za=1%2C6%2C13&ortb-pid=521732&o"
			"rtb-sid=521732&ortb-xt=IAB3&ortb-ugc=">>)
	).
-endif.

%% @doc Build an application/x-www-form-urlencoded string.

-spec qs(qs_vals()) -> binary().
qs([]) ->
	<<>>;
qs(L) ->
	qs(L, <<>>).

qs([], Acc) ->
	<< $&, Qs/bits >> = Acc,
	Qs;
qs([{Name, true}|Tail], Acc) ->
	Acc2 = urlencode(Name, << Acc/bits, $& >>),
	qs(Tail, Acc2);
qs([{Name, Value}|Tail], Acc) ->
	Acc2 = urlencode(Name, << Acc/bits, $& >>),
	Acc3 = urlencode(Value, << Acc2/bits, $= >>),
	qs(Tail, Acc3).

-define(QS_SHORTER, [
	{<<"hl">>, <<"en">>},
	{<<"q">>, <<"erlang cowboy">>}
]).

-define(QS_SHORT, [
	{<<"direction">>, <<"desc">>},
	{<<"for">>, <<"extend/ranch">>},
	{<<"sort">>, <<"updated">>},
	{<<"state">>, <<"open">>}
]).

-define(QS_LONG, [
	{<<"i">>, <<"EWiIXmPj5gl6">>},
	{<<"v">>, <<"QowBp0oDLQXdd4x_GwiywA">>},
	{<<"ip">>, <<"98.20.31.81">>},
	{<<"la">>, <<"en">>},
	{<<"pg">>, <<"New8.undertonebrandsafe.com/"
		"698a2525065ee260c0b2f2aaad89ab82">>},
	{<<"re">>, <<>>},
	{<<"sz">>, <<"1">>},
	{<<"fc">>, <<"1">>},
	{<<"fr">>, <<"140">>},
	{<<"br">>, <<"3">>},
	{<<"bv">>, <<"11.0.696.16">>},
	{<<"os">>, <<"3">>},
	{<<"ov">>, <<>>},
	{<<"rs">>, <<"vpl">>},
	{<<"k">>, <<"cookies|sale|browser|more|privacy|statistics|"
		"activities|auction|email|free|in...">>},
	{<<"t">>, <<"112373">>},
	{<<"xt">>, <<"5|61|0">>},
	{<<"tz">>, <<"-1">>},
	{<<"ev">>, <<"x">>},
	{<<"tk">>, <<>>},
	{<<"za">>, <<"1">>},
	{<<"ortb-za">>, <<"1">>},
	{<<"zu">>, <<>>},
	{<<"zl">>, <<>>},
	{<<"ax">>, <<"U">>},
	{<<"ay">>, <<"U">>},
	{<<"ortb-pid">>, <<"536454.55">>},
	{<<"ortb-sid">>, <<"112373.8">>},
	{<<"seats">>, <<"999">>},
	{<<"ortb-xt">>, <<"IAB24">>},
	{<<"ortb-ugc">>, <<>>}
]).

-define(QS_LONGER, [
	{<<"i">>, <<"9pQNskA">>},
	{<<"v">>, <<"0ySQQd1F">>},
	{<<"ev">>, <<"12345678">>},
	{<<"t">>, <<"12345">>},
	{<<"sz">>, <<"3">>},
	{<<"ip">>, <<"67.58.236.89">>},
	{<<"la">>, <<"en">>},
	{<<"pg">>, <<"http://www.yahoo.com/page1.htm">>},
	{<<"re">>, <<"http://search.google.com">>},
	{<<"fc">>, <<"1">>},
	{<<"fr">>, <<"1">>},
	{<<"br">>, <<"2">>},
	{<<"bv">>, <<"3.0.14">>},
	{<<"os">>, <<"1">>},
	{<<"ov">>, <<"XP">>},
	{<<"k">>, <<"cars,ford">>},
	{<<"rs">>, <<"js">>},
	{<<"xt">>, <<"5|22|234">>},
	{<<"tz">>, <<"+180">>},
	{<<"tk">>, <<"key1=value1|key2=value2">>},
	{<<"zl">>, <<"4,5,6">>},
	{<<"za">>, <<"4">>},
	{<<"zu">>, <<"competitor.com">>},
	{<<"ua">>, <<"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) "
		"AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.98 "
		"Safari/534.13">>},
	{<<"ortb-za">>, <<"1,6,13">>},
	{<<"ortb-pid">>, <<"521732">>},
	{<<"ortb-sid">>, <<"521732">>},
	{<<"ortb-xt">>, <<"IAB3">>},
	{<<"ortb-ugc">>, <<>>}
]).

-ifdef(TEST).
qs_test_() ->
	Tests = [
		{[<<"a">>], error},
		{[{<<"a">>, <<"b">>, <<"c">>}], error},
		{[], <<>>},
		{[{<<"a">>, true}], <<"a">>},
		{[{<<"a">>, true}, {<<"b">>, true}], <<"a&b">>},
		{[{<<"a">>, <<>>}], <<"a=">>},
		{[{<<"a">>, <<"b">>}], <<"a=b">>},
		{[{<<"a">>, <<>>}, {<<"b">>, <<>>}], <<"a=&b=">>},
		{[{<<"a">>, <<"b">>}, {<<"c">>, true}, {<<"d">>, <<"e">>}],
			<<"a=b&c&d=e">>},
		{[{<<"a">>, <<"b=c">>}, {<<"d">>, <<"e=f">>}, {<<"g">>, <<"h=i">>}],
			<<"a=b%3Dc&d=e%3Df&g=h%3Di">>},
		{[{<<" ">>, true}], <<"+">>},
		{[{<<" ">>, <<" ">>}], <<"+=+">>},
		{[{<<"a b">>, <<"c d">>}], <<"a+b=c+d">>},
		{[{<<" a ">>, <<" b ">>}, {<<" c ">>, <<" d ">>}],
			<<"+a+=+b+&+c+=+d+">>},
		{[{<<"%&=">>, <<"%&=">>}, {<<"_-.">>, <<".-_">>}],
			<<"%25%26%3D=%25%26%3D&_-.=.-_">>},
		{[{<<"for">>, <<"extend/ranch">>}], <<"for=extend%2Franch">>}
	],
	[{lists:flatten(io_lib:format("~p", [Vals])), fun() ->
		E = try qs(Vals) of
			R -> R
		catch _:_ ->
			error
		end
	end} || {Vals, E} <- Tests].

qs_identity_test_() ->
	Tests = [
		[{<<"+">>, true}],
		?QS_SHORTER,
		?QS_SHORT,
		?QS_LONG,
		?QS_LONGER
	],
	[{lists:flatten(io_lib:format("~p", [V])), fun() ->
		V = parse_qs(qs(V))
	end} || V <- Tests].
-endif.

-ifdef(PERF).
horse_qs_shorter() ->
	horse:repeat(20000, qs(?QS_SHORTER)).

horse_qs_short() ->
	horse:repeat(20000, qs(?QS_SHORT)).

horse_qs_long() ->
	horse:repeat(20000, qs(?QS_LONG)).

horse_qs_longer() ->
	horse:repeat(20000, qs(?QS_LONGER)).
-endif.

%% @doc Decode a percent encoded string (x-www-form-urlencoded rules).

-spec urldecode(B) -> B when B::binary().
urldecode(B) ->
	urldecode(B, <<>>).

urldecode(<< $%, H, L, Rest/bits >>, Acc) ->
	C = (unhex(H) bsl 4 bor unhex(L)),
	urldecode(Rest, << Acc/bits, C >>);
urldecode(<< $+, Rest/bits >>, Acc) ->
	urldecode(Rest, << Acc/bits, " " >>);
urldecode(<< C, Rest/bits >>, Acc) when C =/= $% ->
	urldecode(Rest, << Acc/bits, C >>);
urldecode(<<>>, Acc) ->
	Acc.

unhex($0) ->  0;
unhex($1) ->  1;
unhex($2) ->  2;
unhex($3) ->  3;
unhex($4) ->  4;
unhex($5) ->  5;
unhex($6) ->  6;
unhex($7) ->  7;
unhex($8) ->  8;
unhex($9) ->  9;
unhex($A) -> 10;
unhex($B) -> 11;
unhex($C) -> 12;
unhex($D) -> 13;
unhex($E) -> 14;
unhex($F) -> 15;
unhex($a) -> 10;
unhex($b) -> 11;
unhex($c) -> 12;
unhex($d) -> 13;
unhex($e) -> 14;
unhex($f) -> 15.

-ifdef(TEST).
urldecode_test_() ->
	Tests = [
		{<<"%20">>, <<" ">>},
		{<<"+">>, <<" ">>},
		{<<"%00">>, <<0>>},
		{<<"%fF">>, <<255>>},
		{<<"123">>, <<"123">>},
		{<<"%i5">>, error},
		{<<"%5">>, error}
	],
	[{Qs, fun() ->
		E = try urldecode(Qs) of
			R -> R
		catch _:_ ->
			error
		end
	end} || {Qs, E} <- Tests].

urldecode_identity_test_() ->
	Tests = [
		<<"+">>,
		<<"nothingnothingnothingnothing">>,
		<<"Small+fast+modular+HTTP+server">>,
		<<"Small%2C+fast%2C+modular+HTTP+server.">>,
		<<"%E3%83%84%E3%82%A4%E3%83%B3%E3%82%BD%E3%82%A6%E3%83"
			"%AB%E3%80%9C%E8%BC%AA%E5%BB%BB%E3%81%99%E3%82%8B%E6%97%8B%E5"
			"%BE%8B%E3%80%9C">>
	],
	[{V, fun() -> V = urlencode(urldecode(V)) end} || V <- Tests].
-endif.

-ifdef(PERF).
horse_urldecode() ->
	horse:repeat(100000,
		urldecode(<<"nothingnothingnothingnothing">>)
	).

horse_urldecode_plus() ->
	horse:repeat(100000,
		urldecode(<<"Small+fast+modular+HTTP+server">>)
	).

horse_urldecode_hex() ->
	horse:repeat(100000,
		urldecode(<<"Small%2C%20fast%2C%20modular%20HTTP%20server.">>)
	).

horse_urldecode_jp_hex() ->
	horse:repeat(100000,
		urldecode(<<"%E3%83%84%E3%82%A4%E3%83%B3%E3%82%BD%E3%82%A6%E3%83"
			"%AB%E3%80%9C%E8%BC%AA%E5%BB%BB%E3%81%99%E3%82%8B%E6%97%8B%E5"
			"%BE%8B%E3%80%9C">>)
	).

horse_urldecode_mix() ->
	horse:repeat(100000,
		urldecode(<<"Small%2C+fast%2C+modular+HTTP+server.">>)
	).
-endif.

%% @doc Percent encode a string (x-www-form-urlencoded rules).

-spec urlencode(B) -> B when B::binary().
urlencode(B) ->
	urlencode(B, <<>>).

urlencode(<< $\s, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $+ >>);
urlencode(<< $-, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $- >>);
urlencode(<< $., Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $. >>);
urlencode(<< $0, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $0 >>);
urlencode(<< $1, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $1 >>);
urlencode(<< $2, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $2 >>);
urlencode(<< $3, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $3 >>);
urlencode(<< $4, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $4 >>);
urlencode(<< $5, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $5 >>);
urlencode(<< $6, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $6 >>);
urlencode(<< $7, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $7 >>);
urlencode(<< $8, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $8 >>);
urlencode(<< $9, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $9 >>);
urlencode(<< $A, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $A >>);
urlencode(<< $B, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $B >>);
urlencode(<< $C, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $C >>);
urlencode(<< $D, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $D >>);
urlencode(<< $E, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $E >>);
urlencode(<< $F, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $F >>);
urlencode(<< $G, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $G >>);
urlencode(<< $H, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $H >>);
urlencode(<< $I, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $I >>);
urlencode(<< $J, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $J >>);
urlencode(<< $K, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $K >>);
urlencode(<< $L, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $L >>);
urlencode(<< $M, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $M >>);
urlencode(<< $N, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $N >>);
urlencode(<< $O, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $O >>);
urlencode(<< $P, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $P >>);
urlencode(<< $Q, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $Q >>);
urlencode(<< $R, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $R >>);
urlencode(<< $S, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $S >>);
urlencode(<< $T, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $T >>);
urlencode(<< $U, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $U >>);
urlencode(<< $V, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $V >>);
urlencode(<< $W, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $W >>);
urlencode(<< $X, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $X >>);
urlencode(<< $Y, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $Y >>);
urlencode(<< $Z, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $Z >>);
urlencode(<< $_, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $_ >>);
urlencode(<< $a, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $a >>);
urlencode(<< $b, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $b >>);
urlencode(<< $c, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $c >>);
urlencode(<< $d, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $d >>);
urlencode(<< $e, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $e >>);
urlencode(<< $f, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $f >>);
urlencode(<< $g, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $g >>);
urlencode(<< $h, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $h >>);
urlencode(<< $i, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $i >>);
urlencode(<< $j, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $j >>);
urlencode(<< $k, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $k >>);
urlencode(<< $l, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $l >>);
urlencode(<< $m, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $m >>);
urlencode(<< $n, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $n >>);
urlencode(<< $o, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $o >>);
urlencode(<< $p, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $p >>);
urlencode(<< $q, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $q >>);
urlencode(<< $r, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $r >>);
urlencode(<< $s, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $s >>);
urlencode(<< $t, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $t >>);
urlencode(<< $u, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $u >>);
urlencode(<< $v, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $v >>);
urlencode(<< $w, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $w >>);
urlencode(<< $x, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $x >>);
urlencode(<< $y, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $y >>);
urlencode(<< $z, Rest/bits >>, Acc) -> urlencode(Rest, << Acc/bits, $z >>);
urlencode(<< C, Rest/bits >>, Acc) ->
	H = hex(C bsr 4),
	L = hex(C band 16#0f),
	urlencode(Rest, << Acc/bits, $%, H, L >>);
urlencode(<<>>, Acc) ->
	Acc.

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(10) -> $A;
hex(11) -> $B;
hex(12) -> $C;
hex(13) -> $D;
hex(14) -> $E;
hex(15) -> $F.

-ifdef(TEST).
urlencode_test_() ->
	Tests = [
		{<<255, 0>>, <<"%FF%00">>},
		{<<255, " ">>, <<"%FF+">>},
		{<<" ">>, <<"+">>},
		{<<"aBc123">>, <<"aBc123">>},
		{<<".-_">>, <<".-_">>}
	],
	[{V, fun() -> E = urlencode(V) end} || {V, E} <- Tests].

urlencode_identity_test_() ->
	Tests = [
		<<"+">>,
		<<"nothingnothingnothingnothing">>,
		<<"Small fast modular HTTP server">>,
		<<"Small, fast, modular HTTP server.">>,
		<<227,131,132,227,130,164,227,131,179,227,130,189,227,
			130,166,227,131,171,227,128,156,232,188,170,229,187,187,227,
			129,153,227,130,139,230,151,139,229,190,139,227,128,156>>
	],
	[{V, fun() -> V = urldecode(urlencode(V)) end} || V <- Tests].
-endif.

-ifdef(PERF).
horse_urlencode() ->
	horse:repeat(100000,
		urlencode(<<"nothingnothingnothingnothing">>)
	).

horse_urlencode_plus() ->
	horse:repeat(100000,
		urlencode(<<"Small fast modular HTTP server">>)
	).

horse_urlencode_jp() ->
	horse:repeat(100000,
		urlencode(<<227,131,132,227,130,164,227,131,179,227,130,189,227,
			130,166,227,131,171,227,128,156,232,188,170,229,187,187,227,
			129,153,227,130,139,230,151,139,229,190,139,227,128,156>>)
	).

horse_urlencode_mix() ->
	horse:repeat(100000,
		urlencode(<<"Small, fast, modular HTTP server.">>)
	).
-endif.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted deps/cowlib/src/cow_spdy.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
%% Copyright (c) 2013-2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(cow_spdy).

%% Zstream.
-export([deflate_init/0]).
-export([inflate_init/0]).

%% Parse.
-export([split/1]).
-export([parse/2]).

%% Build.
-export([data/3]).
-export([syn_stream/12]).
-export([syn_reply/6]).
-export([rst_stream/2]).
%% @todo settings
-export([ping/1]).
-export([goaway/2]).
%% @todo headers
%% @todo window_update

-include("cow_spdy.hrl").

%% Zstream.

deflate_init() ->
	Zdef = zlib:open(),
	ok = zlib:deflateInit(Zdef),
	_ = zlib:deflateSetDictionary(Zdef, ?ZDICT),
	Zdef.

inflate_init() ->
	Zinf = zlib:open(),
	ok = zlib:inflateInit(Zinf),
	Zinf.

%% Parse.

split(Data = << _:40, Length:24, _/bits >>)
		when byte_size(Data) >= Length + 8 ->
	Length2 = Length + 8,
	<< Frame:Length2/binary, Rest/bits >> = Data,
	{true, Frame, Rest};
split(_) ->
	false.

parse(<< 0:1, StreamID:31, 0:7, IsFinFlag:1, _:24, Data/bits >>, _) ->
	{data, StreamID, from_flag(IsFinFlag), Data};
parse(<< 1:1, 3:15, 1:16, 0:6, IsUnidirectionalFlag:1, IsFinFlag:1,
		_:25, StreamID:31, _:1, AssocToStreamID:31, Priority:3, _:5,
		0:8, Rest/bits >>, Zinf) ->
	case parse_headers(Rest, Zinf) of
		{ok, Headers, [{<<":host">>, Host}, {<<":method">>, Method},
				{<<":path">>, Path}, {<<":scheme">>, Scheme},
				{<<":version">>, Version}]} ->
			{syn_stream, StreamID, AssocToStreamID, from_flag(IsFinFlag),
				from_flag(IsUnidirectionalFlag), Priority, Method,
				Scheme, Host, Path, Version, Headers};
		_ ->
			{error, badprotocol}
	end;
parse(<< 1:1, 3:15, 2:16, 0:7, IsFinFlag:1, _:25,
		StreamID:31, Rest/bits >>, Zinf) ->
	case parse_headers(Rest, Zinf) of
		{ok, Headers, [{<<":status">>, Status}, {<<":version">>, Version}]} ->
			{syn_reply, StreamID, from_flag(IsFinFlag),
				Status, Version, Headers};
		_ ->
			{error, badprotocol}
	end;
parse(<< 1:1, 3:15, 3:16, 0:8, _:56, StatusCode:32 >>, _)
		when StatusCode =:= 0; StatusCode > 11 ->
	{error, badprotocol};
parse(<< 1:1, 3:15, 3:16, 0:8, _:25, StreamID:31, StatusCode:32 >>, _) ->
	Status = case StatusCode of
		1 -> protocol_error;
		2 -> invalid_stream;
		3 -> refused_stream;
		4 -> unsupported_version;
		5 -> cancel;
		6 -> internal_error;
		7 -> flow_control_error;
		8 -> stream_in_use;
		9 -> stream_already_closed;
		10 -> invalid_credentials;
		11 -> frame_too_large
	end,
	{rst_stream, StreamID, Status};
parse(<< 1:1, 3:15, 4:16, 0:7, ClearSettingsFlag:1, _:24,
		NbEntries:32, Rest/bits >>, _) ->
	try
		Settings = [begin
			Is0 = 0,
			Key = case ID of
				1 -> upload_bandwidth;
				2 -> download_bandwidth;
				3 -> round_trip_time;
				4 -> max_concurrent_streams;
				5 -> current_cwnd;
				6 -> download_retrans_rate;
				7 -> initial_window_size;
				8 -> client_certificate_vector_size
			end,
			{Key, Value, from_flag(PersistFlag), from_flag(WasPersistedFlag)}
		end || << Is0:6, WasPersistedFlag:1, PersistFlag:1,
			ID:24, Value:32 >> <= Rest],
		NbEntries = length(Settings),
		{settings, from_flag(ClearSettingsFlag), Settings}
	catch _:_ ->
		{error, badprotocol}
	end;
parse(<< 1:1, 3:15, 6:16, 0:8, _:24, PingID:32 >>, _) ->
	{ping, PingID};
parse(<< 1:1, 3:15, 7:16, 0:8, _:56, StatusCode:32 >>, _)
		when StatusCode > 2 ->
	{error, badprotocol};
parse(<< 1:1, 3:15, 7:16, 0:8, _:25, LastGoodStreamID:31,
		StatusCode:32 >>, _) ->
	Status = case StatusCode of
		0 -> ok;
		1 -> protocol_error;
		2 -> internal_error
	end,
	{goaway, LastGoodStreamID, Status};
parse(<< 1:1, 3:15, 8:16, 0:7, IsFinFlag:1, _:25, StreamID:31,
		Rest/bits >>, Zinf) ->
	case parse_headers(Rest, Zinf) of
		{ok, Headers, []} ->
			{headers, StreamID, from_flag(IsFinFlag), Headers};
		_ ->
			{error, badprotocol}
	end;
parse(<< 1:1, 3:15, 9:16, 0:8, _:57, 0:31 >>, _) ->
	{error, badprotocol};
parse(<< 1:1, 3:15, 9:16, 0:8, _:25, StreamID:31,
		_:1, DeltaWindowSize:31 >>, _) ->
	{window_update, StreamID, DeltaWindowSize};
parse(_, _) ->
	{error, badprotocol}.

parse_headers(Data, Zinf) ->
	[<< NbHeaders:32, Rest/bits >>] = inflate(Zinf, Data),
	parse_headers(Rest, NbHeaders, [], []).

parse_headers(<<>>, 0, Headers, SpHeaders) ->
	{ok, lists:reverse(Headers), lists:sort(SpHeaders)};
parse_headers(<<>>, _, _, _) ->
	error;
parse_headers(_, 0, _, _) ->
	error;
parse_headers(<< 0:32, _/bits >>, _, _, _) ->
	error;
parse_headers(<< L1:32, Key:L1/binary, L2:32, Value:L2/binary, Rest/bits >>,
		NbHeaders, Acc, SpAcc) ->
	case Key of
		<< $:, _/bits >> ->
			parse_headers(Rest, NbHeaders - 1, Acc,
				lists:keystore(Key, 1, SpAcc, {Key, Value}));
		_ ->
			parse_headers(Rest, NbHeaders - 1, [{Key, Value}|Acc], SpAcc)
	end.

inflate(Zinf, Data) ->
	try
		zlib:inflate(Zinf, Data)
	catch _:_ ->
		ok = zlib:inflateSetDictionary(Zinf, ?ZDICT),
		zlib:inflate(Zinf, <<>>)
	end.

from_flag(0) -> false;
from_flag(1) -> true.

%% Build.

data(StreamID, IsFin, Data) ->
	IsFinFlag = to_flag(IsFin),
	Length = iolist_size(Data),
	[<< 0:1, StreamID:31, 0:7, IsFinFlag:1, Length:24 >>, Data].

syn_stream(Zdef, StreamID, AssocToStreamID, IsFin, IsUnidirectional,
		Priority, Method, Scheme, Host, Path, Version, Headers) ->
	IsFinFlag = to_flag(IsFin),
	IsUnidirectionalFlag = to_flag(IsUnidirectional),
	HeaderBlock = build_headers(Zdef, [
		{<<":method">>, Method},
		{<<":scheme">>, Scheme},
		{<<":host">>, Host},
		{<<":path">>, Path},
		{<<":version">>, Version}
		|Headers]),
	Length = 10 + iolist_size(HeaderBlock),
	[<< 1:1, 3:15, 1:16, 0:6, IsUnidirectionalFlag:1, IsFinFlag:1,
		Length:24, 0:1, StreamID:31, 0:1, AssocToStreamID:31,
		Priority:3, 0:5, 0:8 >>, HeaderBlock].

syn_reply(Zdef, StreamID, IsFin, Status, Version, Headers) ->
	IsFinFlag = to_flag(IsFin),
	HeaderBlock = build_headers(Zdef, [
		{<<":status">>, Status},
		{<<":version">>, Version}
		|Headers]),
	Length = 4 + iolist_size(HeaderBlock),
	[<< 1:1, 3:15, 2:16, 0:7, IsFinFlag:1, Length:24,
		0:1, StreamID:31 >>, HeaderBlock].

rst_stream(StreamID, Status) ->
	StatusCode = case Status of
		protocol_error -> 1;
		invalid_stream -> 2;
		refused_stream -> 3;
		unsupported_version -> 4;
		cancel -> 5;
		internal_error -> 6;
		flow_control_error -> 7;
		stream_in_use -> 8;
		stream_already_closed -> 9;
		invalid_credentials -> 10;
		frame_too_large -> 11
	end,
	<< 1:1, 3:15, 3:16, 0:8, 8:24,
		0:1, StreamID:31, StatusCode:32 >>.

%% @todo settings

ping(PingID) ->
	<< 1:1, 3:15, 6:16, 0:8, 4:24, PingID:32 >>.

goaway(LastGoodStreamID, Status) ->
	StatusCode = case Status of
		ok -> 0;
		protocol_error -> 1;
		internal_error -> 2
	end,
	<< 1:1, 3:15, 7:16, 0:8, 8:24,
		0:1, LastGoodStreamID:31, StatusCode:32 >>.

%% @todo headers
%% @todo window_update

build_headers(Zdef, Headers) ->
	NbHeaders = length(Headers),
	Headers2 = [begin
		L1 = iolist_size(Key),
		L2 = iolist_size(Value),
		[<< L1:32 >>, Key, << L2:32 >>, Value]
	end || {Key, Value} <- Headers],
	zlib:deflate(Zdef, [<< NbHeaders:32 >>, Headers2], full).

to_flag(false) -> 0;
to_flag(true) -> 1.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


















































































































































































































































































































































































































































































































































Deleted deps/cowlib/src/cow_spdy.hrl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
%% Zlib dictionary.

-define(ZDICT, <<
	16#00, 16#00, 16#00, 16#07, 16#6f, 16#70, 16#74, 16#69,
	16#6f, 16#6e, 16#73, 16#00, 16#00, 16#00, 16#04, 16#68,
	16#65, 16#61, 16#64, 16#00, 16#00, 16#00, 16#04, 16#70,
	16#6f, 16#73, 16#74, 16#00, 16#00, 16#00, 16#03, 16#70,
	16#75, 16#74, 16#00, 16#00, 16#00, 16#06, 16#64, 16#65,
	16#6c, 16#65, 16#74, 16#65, 16#00, 16#00, 16#00, 16#05,
	16#74, 16#72, 16#61, 16#63, 16#65, 16#00, 16#00, 16#00,
	16#06, 16#61, 16#63, 16#63, 16#65, 16#70, 16#74, 16#00,
	16#00, 16#00, 16#0e, 16#61, 16#63, 16#63, 16#65, 16#70,
	16#74, 16#2d, 16#63, 16#68, 16#61, 16#72, 16#73, 16#65,
	16#74, 16#00, 16#00, 16#00, 16#0f, 16#61, 16#63, 16#63,
	16#65, 16#70, 16#74, 16#2d, 16#65, 16#6e, 16#63, 16#6f,
	16#64, 16#69, 16#6e, 16#67, 16#00, 16#00, 16#00, 16#0f,
	16#61, 16#63, 16#63, 16#65, 16#70, 16#74, 16#2d, 16#6c,
	16#61, 16#6e, 16#67, 16#75, 16#61, 16#67, 16#65, 16#00,
	16#00, 16#00, 16#0d, 16#61, 16#63, 16#63, 16#65, 16#70,
	16#74, 16#2d, 16#72, 16#61, 16#6e, 16#67, 16#65, 16#73,
	16#00, 16#00, 16#00, 16#03, 16#61, 16#67, 16#65, 16#00,
	16#00, 16#00, 16#05, 16#61, 16#6c, 16#6c, 16#6f, 16#77,
	16#00, 16#00, 16#00, 16#0d, 16#61, 16#75, 16#74, 16#68,
	16#6f, 16#72, 16#69, 16#7a, 16#61, 16#74, 16#69, 16#6f,
	16#6e, 16#00, 16#00, 16#00, 16#0d, 16#63, 16#61, 16#63,
	16#68, 16#65, 16#2d, 16#63, 16#6f, 16#6e, 16#74, 16#72,
	16#6f, 16#6c, 16#00, 16#00, 16#00, 16#0a, 16#63, 16#6f,
	16#6e, 16#6e, 16#65, 16#63, 16#74, 16#69, 16#6f, 16#6e,
	16#00, 16#00, 16#00, 16#0c, 16#63, 16#6f, 16#6e, 16#74,
	16#65, 16#6e, 16#74, 16#2d, 16#62, 16#61, 16#73, 16#65,
	16#00, 16#00, 16#00, 16#10, 16#63, 16#6f, 16#6e, 16#74,
	16#65, 16#6e, 16#74, 16#2d, 16#65, 16#6e, 16#63, 16#6f,
	16#64, 16#69, 16#6e, 16#67, 16#00, 16#00, 16#00, 16#10,
	16#63, 16#6f, 16#6e, 16#74, 16#65, 16#6e, 16#74, 16#2d,
	16#6c, 16#61, 16#6e, 16#67, 16#75, 16#61, 16#67, 16#65,
	16#00, 16#00, 16#00, 16#0e, 16#63, 16#6f, 16#6e, 16#74,
	16#65, 16#6e, 16#74, 16#2d, 16#6c, 16#65, 16#6e, 16#67,
	16#74, 16#68, 16#00, 16#00, 16#00, 16#10, 16#63, 16#6f,
	16#6e, 16#74, 16#65, 16#6e, 16#74, 16#2d, 16#6c, 16#6f,
	16#63, 16#61, 16#74, 16#69, 16#6f, 16#6e, 16#00, 16#00,
	16#00, 16#0b, 16#63, 16#6f, 16#6e, 16#74, 16#65, 16#6e,
	16#74, 16#2d, 16#6d, 16#64, 16#35, 16#00, 16#00, 16#00,
	16#0d, 16#63, 16#6f, 16#6e, 16#74, 16#65, 16#6e, 16#74,
	16#2d, 16#72, 16#61, 16#6e, 16#67, 16#65, 16#00, 16#00,
	16#00, 16#0c, 16#63, 16#6f, 16#6e, 16#74, 16#65, 16#6e,
	16#74, 16#2d, 16#74, 16#79, 16#70, 16#65, 16#00, 16#00,
	16#00, 16#04, 16#64, 16#61, 16#74, 16#65, 16#00, 16#00,
	16#00, 16#04, 16#65, 16#74, 16#61, 16#67, 16#00, 16#00,
	16#00, 16#06, 16#65, 16#78, 16#70, 16#65, 16#63, 16#74,
	16#00, 16#00, 16#00, 16#07, 16#65, 16#78, 16#70, 16#69,
	16#72, 16#65, 16#73, 16#00, 16#00, 16#00, 16#04, 16#66,
	16#72, 16#6f, 16#6d, 16#00, 16#00, 16#00, 16#04, 16#68,
	16#6f, 16#73, 16#74, 16#00, 16#00, 16#00, 16#08, 16#69,
	16#66, 16#2d, 16#6d, 16#61, 16#74, 16#63, 16#68, 16#00,
	16#00, 16#00, 16#11, 16#69, 16#66, 16#2d, 16#6d, 16#6f,
	16#64, 16#69, 16#66, 16#69, 16#65, 16#64, 16#2d, 16#73,
	16#69, 16#6e, 16#63, 16#65, 16#00, 16#00, 16#00, 16#0d,
	16#69, 16#66, 16#2d, 16#6e, 16#6f, 16#6e, 16#65, 16#2d,
	16#6d, 16#61, 16#74, 16#63, 16#68, 16#00, 16#00, 16#00,
	16#08, 16#69, 16#66, 16#2d, 16#72, 16#61, 16#6e, 16#67,
	16#65, 16#00, 16#00, 16#00, 16#13, 16#69, 16#66, 16#2d,
	16#75, 16#6e, 16#6d, 16#6f, 16#64, 16#69, 16#66, 16#69,
	16#65, 16#64, 16#2d, 16#73, 16#69, 16#6e, 16#63, 16#65,
	16#00, 16#00, 16#00, 16#0d, 16#6c, 16#61, 16#73, 16#74,
	16#2d, 16#6d, 16#6f, 16#64, 16#69, 16#66, 16#69, 16#65,
	16#64, 16#00, 16#00, 16#00, 16#08, 16#6c, 16#6f, 16#63,
	16#61, 16#74, 16#69, 16#6f, 16#6e, 16#00, 16#00, 16#00,
	16#0c, 16#6d, 16#61, 16#78, 16#2d, 16#66, 16#6f, 16#72,
	16#77, 16#61, 16#72, 16#64, 16#73, 16#00, 16#00, 16#00,
	16#06, 16#70, 16#72, 16#61, 16#67, 16#6d, 16#61, 16#00,
	16#00, 16#00, 16#12, 16#70, 16#72, 16#6f, 16#78, 16#79,
	16#2d, 16#61, 16#75, 16#74, 16#68, 16#65, 16#6e, 16#74,
	16#69, 16#63, 16#61, 16#74, 16#65, 16#00, 16#00, 16#00,
	16#13, 16#70, 16#72, 16#6f, 16#78, 16#79, 16#2d, 16#61,
	16#75, 16#74, 16#68, 16#6f, 16#72, 16#69, 16#7a, 16#61,
	16#74, 16#69, 16#6f, 16#6e, 16#00, 16#00, 16#00, 16#05,
	16#72, 16#61, 16#6e, 16#67, 16#65, 16#00, 16#00, 16#00,
	16#07, 16#72, 16#65, 16#66, 16#65, 16#72, 16#65, 16#72,
	16#00, 16#00, 16#00, 16#0b, 16#72, 16#65, 16#74, 16#72,
	16#79, 16#2d, 16#61, 16#66, 16#74, 16#65, 16#72, 16#00,
	16#00, 16#00, 16#06, 16#73, 16#65, 16#72, 16#76, 16#65,
	16#72, 16#00, 16#00, 16#00, 16#02, 16#74, 16#65, 16#00,
	16#00, 16#00, 16#07, 16#74, 16#72, 16#61, 16#69, 16#6c,
	16#65, 16#72, 16#00, 16#00, 16#00, 16#11, 16#74, 16#72,
	16#61, 16#6e, 16#73, 16#66, 16#65, 16#72, 16#2d, 16#65,
	16#6e, 16#63, 16#6f, 16#64, 16#69, 16#6e, 16#67, 16#00,
	16#00, 16#00, 16#07, 16#75, 16#70, 16#67, 16#72, 16#61,
	16#64, 16#65, 16#00, 16#00, 16#00, 16#0a, 16#75, 16#73,
	16#65, 16#72, 16#2d, 16#61, 16#67, 16#65, 16#6e, 16#74,
	16#00, 16#00, 16#00, 16#04, 16#76, 16#61, 16#72, 16#79,
	16#00, 16#00, 16#00, 16#03, 16#76, 16#69, 16#61, 16#00,
	16#00, 16#00, 16#07, 16#77, 16#61, 16#72, 16#6e, 16#69,
	16#6e, 16#67, 16#00, 16#00, 16#00, 16#10, 16#77, 16#77,
	16#77, 16#2d, 16#61, 16#75, 16#74, 16#68, 16#65, 16#6e,
	16#74, 16#69, 16#63, 16#61, 16#74, 16#65, 16#00, 16#00,
	16#00, 16#06, 16#6d, 16#65, 16#74, 16#68, 16#6f, 16#64,
	16#00, 16#00, 16#00, 16#03, 16#67, 16#65, 16#74, 16#00,
	16#00, 16#00, 16#06, 16#73, 16#74, 16#61, 16#74, 16#75,
	16#73, 16#00, 16#00, 16#00, 16#06, 16#32, 16#30, 16#30,
	16#20, 16#4f, 16#4b, 16#00, 16#00, 16#00, 16#07, 16#76,
	16#65, 16#72, 16#73, 16#69, 16#6f, 16#6e, 16#00, 16#00,
	16#00, 16#08, 16#48, 16#54, 16#54, 16#50, 16#2f, 16#31,
	16#2e, 16#31, 16#00, 16#00, 16#00, 16#03, 16#75, 16#72,
	16#6c, 16#00, 16#00, 16#00, 16#06, 16#70, 16#75, 16#62,
	16#6c, 16#69, 16#63, 16#00, 16#00, 16#00, 16#0a, 16#73,
	16#65, 16#74, 16#2d, 16#63, 16#6f, 16#6f, 16#6b, 16#69,
	16#65, 16#00, 16#00, 16#00, 16#0a, 16#6b, 16#65, 16#65,
	16#70, 16#2d, 16#61, 16#6c, 16#69, 16#76, 16#65, 16#00,
	16#00, 16#00, 16#06, 16#6f, 16#72, 16#69, 16#67, 16#69,
	16#6e, 16#31, 16#30, 16#30, 16#31, 16#30, 16#31, 16#32,
	16#30, 16#31, 16#32, 16#30, 16#32, 16#32, 16#30, 16#35,
	16#32, 16#30, 16#36, 16#33, 16#30, 16#30, 16#33, 16#30,
	16#32, 16#33, 16#30, 16#33, 16#33, 16#30, 16#34, 16#33,
	16#30, 16#35, 16#33, 16#30, 16#36, 16#33, 16#30, 16#37,
	16#34, 16#30, 16#32, 16#34, 16#30, 16#35, 16#34, 16#30,
	16#36, 16#34, 16#30, 16#37, 16#34, 16#30, 16#38, 16#34,
	16#30, 16#39, 16#34, 16#31, 16#30, 16#34, 16#31, 16#31,
	16#34, 16#31, 16#32, 16#34, 16#31, 16#33, 16#34, 16#31,
	16#34, 16#34, 16#31, 16#35, 16#34, 16#31, 16#36, 16#34,
	16#31, 16#37, 16#35, 16#30, 16#32, 16#35, 16#30, 16#34,
	16#35, 16#30, 16#35, 16#32, 16#30, 16#33, 16#20, 16#4e,
	16#6f, 16#6e, 16#2d, 16#41, 16#75, 16#74, 16#68, 16#6f,
	16#72, 16#69, 16#74, 16#61, 16#74, 16#69, 16#76, 16#65,
	16#20, 16#49, 16#6e, 16#66, 16#6f, 16#72, 16#6d, 16#61,
	16#74, 16#69, 16#6f, 16#6e, 16#32, 16#30, 16#34, 16#20,
	16#4e, 16#6f, 16#20, 16#43, 16#6f, 16#6e, 16#74, 16#65,
	16#6e, 16#74, 16#33, 16#30, 16#31, 16#20, 16#4d, 16#6f,
	16#76, 16#65, 16#64, 16#20, 16#50, 16#65, 16#72, 16#6d,
	16#61, 16#6e, 16#65, 16#6e, 16#74, 16#6c, 16#79, 16#34,
	16#30, 16#30, 16#20, 16#42, 16#61, 16#64, 16#20, 16#52,
	16#65, 16#71, 16#75, 16#65, 16#73, 16#74, 16#34, 16#30,
	16#31, 16#20, 16#55, 16#6e, 16#61, 16#75, 16#74, 16#68,
	16#6f, 16#72, 16#69, 16#7a, 16#65, 16#64, 16#34, 16#30,
	16#33, 16#20, 16#46, 16#6f, 16#72, 16#62, 16#69, 16#64,
	16#64, 16#65, 16#6e, 16#34, 16#30, 16#34, 16#20, 16#4e,
	16#6f, 16#74, 16#20, 16#46, 16#6f, 16#75, 16#6e, 16#64,
	16#35, 16#30, 16#30, 16#20, 16#49, 16#6e, 16#74, 16#65,
	16#72, 16#6e, 16#61, 16#6c, 16#20, 16#53, 16#65, 16#72,
	16#76, 16#65, 16#72, 16#20, 16#45, 16#72, 16#72, 16#6f,
	16#72, 16#35, 16#30, 16#31, 16#20, 16#4e, 16#6f, 16#74,
	16#20, 16#49, 16#6d, 16#70, 16#6c, 16#65, 16#6d, 16#65,
	16#6e, 16#74, 16#65, 16#64, 16#35, 16#30, 16#33, 16#20,
	16#53, 16#65, 16#72, 16#76, 16#69, 16#63, 16#65, 16#20,
	16#55, 16#6e, 16#61, 16#76, 16#61, 16#69, 16#6c, 16#61,
	16#62, 16#6c, 16#65, 16#4a, 16#61, 16#6e, 16#20, 16#46,
	16#65, 16#62, 16#20, 16#4d, 16#61, 16#72, 16#20, 16#41,
	16#70, 16#72, 16#20, 16#4d, 16#61, 16#79, 16#20, 16#4a,
	16#75, 16#6e, 16#20, 16#4a, 16#75, 16#6c, 16#20, 16#41,
	16#75, 16#67, 16#20, 16#53, 16#65, 16#70, 16#74, 16#20,
	16#4f, 16#63, 16#74, 16#20, 16#4e, 16#6f, 16#76, 16#20,
	16#44, 16#65, 16#63, 16#20, 16#30, 16#30, 16#3a, 16#30,
	16#30, 16#3a, 16#30, 16#30, 16#20, 16#4d, 16#6f, 16#6e,
	16#2c, 16#20, 16#54, 16#75, 16#65, 16#2c, 16#20, 16#57,
	16#65, 16#64, 16#2c, 16#20, 16#54, 16#68, 16#75, 16#2c,
	16#20, 16#46, 16#72, 16#69, 16#2c, 16#20, 16#53, 16#61,
	16#74, 16#2c, 16#20, 16#53, 16#75, 16#6e, 16#2c, 16#20,
	16#47, 16#4d, 16#54, 16#63, 16#68, 16#75, 16#6e, 16#6b,
	16#65, 16#64, 16#2c, 16#74, 16#65, 16#78, 16#74, 16#2f,
	16#68, 16#74, 16#6d, 16#6c, 16#2c, 16#69, 16#6d, 16#61,
	16#67, 16#65, 16#2f, 16#70, 16#6e, 16#67, 16#2c, 16#69,
	16#6d, 16#61, 16#67, 16#65, 16#2f, 16#6a, 16#70, 16#67,
	16#2c, 16#69, 16#6d, 16#61, 16#67, 16#65, 16#2f, 16#67,
	16#69, 16#66, 16#2c, 16#61, 16#70, 16#70, 16#6c, 16#69,
	16#63, 16#61, 16#74, 16#69, 16#6f, 16#6e, 16#2f, 16#78,
	16#6d, 16#6c, 16#2c, 16#61, 16#70, 16#70, 16#6c, 16#69,
	16#63, 16#61, 16#74, 16#69, 16#6f, 16#6e, 16#2f, 16#78,
	16#68, 16#74, 16#6d, 16#6c, 16#2b, 16#78, 16#6d, 16#6c,
	16#2c, 16#74, 16#65, 16#78, 16#74, 16#2f, 16#70, 16#6c,
	16#61, 16#69, 16#6e, 16#2c, 16#74, 16#65, 16#78, 16#74,
	16#2f, 16#6a, 16#61, 16#76, 16#61, 16#73, 16#63, 16#72,
	16#69, 16#70, 16#74, 16#2c, 16#70, 16#75, 16#62, 16#6c,
	16#69, 16#63, 16#70, 16#72, 16#69, 16#76, 16#61, 16#74,
	16#65, 16#6d, 16#61, 16#78, 16#2d, 16#61, 16#67, 16#65,
	16#3d, 16#67, 16#7a, 16#69, 16#70, 16#2c, 16#64, 16#65,
	16#66, 16#6c, 16#61, 16#74, 16#65, 16#2c, 16#73, 16#64,
	16#63, 16#68, 16#63, 16#68, 16#61, 16#72, 16#73, 16#65,
	16#74, 16#3d, 16#75, 16#74, 16#66, 16#2d, 16#38, 16#63,
	16#68, 16#61, 16#72, 16#73, 16#65, 16#74, 16#3d, 16#69,
	16#73, 16#6f, 16#2d, 16#38, 16#38, 16#35, 16#39, 16#2d,
	16#31, 16#2c, 16#75, 16#74, 16#66, 16#2d, 16#2c, 16#2a,
	16#2c, 16#65, 16#6e, 16#71, 16#3d, 16#30, 16#2e >>).
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<










































































































































































































































































































































































Deleted deps/cowlib/src/cowlib.app.src.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
%% Copyright (c) 2013-2014, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

{application, cowlib, [
	{description, "Support library for manipulating Web protocols."},
	{vsn, "1.0.2"},
	{id, "git"},
	{modules, []},
	{registered, []},
	{applications, [
		kernel,
		stdlib,
		crypto
	]}
]}.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




















































Deleted deps/plug/CHANGELOG.md.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
## Changelog

## v1.0.3

* Enhancements
  * Raise if new lines are used in header values

* Bug fix
  * Allow mime type lookup of uppercase extensions
  * Do not validate uppercase headers in production to avoid performance hits
  * Prevent Plug.Parsers from clobbering existing conn.params when part of it is unfetched

## v1.0.2

* Bug fix
  * Ensure cookie store returns a Session ID so they can be dropped

## v1.0.1

* Enhancements
  * Allow configuring all options supported by the underlying transport (i.e. cowboy)

## v1.0.0

* Enhancements
  * Allow custom headers in `Plug.Static`

* Bug fix
  * No longer automatically assume "priv" for cert and key files for Cowboy SSL
  * Raise if response has been sent more than once in test connection
  * Raise when body is nil on `Plug.Conn.resp/3`
  * Show more info and escape messages in `Plug.Debugger`

## v0.14.0

* Enhancements
  * Support `:rewrite_on` on `Plug.SSL`
  * Add `Plug.Conn.merge_resp_headers/2`

* Bug fix
  * Ensure message encryptor and verifier do not error on bad data

## v0.13.1

* Enhancements
  * Add `conn.request_path`
  * Raise if `put_session/3` is invoked when response is sent

* Bug fixes
  * Fix empty params being encoded into query string as '&'

* Deprecations
  * `Plug.Conn.full_path/2` is deprecated in favor of `conn.request_path`
  * `Plug.Test.put_req_header/3` and `Plug.Test.delete_req_header/3` is deprecated in favor of similarly named functions in `Plug.Conn`

## v0.13.0

* Enhancements
  * Raise if a header in upcase is given
  * Store timestamps in sessions ETS table and document each entry format
  * Allow private options when specifying routes in `Plug.Router`
  * Allow the session to be cleared and ignored when an invalid CSRF token is given
  * Allow log level to be configured in `Plug.Logger`
  * Generate masked CSRF tokens to avoid BREACH attacks

* Backwards incompatible changes
  * `Plug.Logger` no longer sets the request id. Use `Plug.RequestId` instead

## v0.12.2

* Enhancements
  * Add `Plug.HTML`

* Bug fixes
  * Do not crash on poorly encoded cookies
  * Decode parameters before matching on the router

## v0.12.1

* Enhancements
  * Add `Plug.SSL` with redirection from HTTP and HSTS support
  * Remove the need for `:encrypt` option from `Plug.Session.COOKIE`. The need for encryption can be fully specified by passing `:encrypted_salt` option. This improvement is backwards compatible.

* Bug fixes
  * Ensure we don't parse body params if they were already parsed

## v0.12.0

* Enhancements
  * Add `query_params` and `body_params` to keep query and body parameters apart from `params`
  * Allow custom encoders when encoding query parameters
  * Assert valid utf-8 on url encoded and multipart bodies

* Bug fixes
  * Use only body parameters when detecting method override
  * Add Vary header when serving gzipped content in Plug.Static

* Deprecations
  * `fetch_params/2` is deprecated in favor of `fetch_query_params/2`

## v0.11.3

* Bug fixes
  * Ensure test adapter reuses the given connection

* Deprecations
  * The `:headers` option in `Plug.Test.conn/4` is deprecated in favor of `put_req_header/3`

## v0.11.2

* Enhancements
  * Add `:log_on_halt` option to `Plug.Builder` and `Plug.Router`
  * Use raw files and delayed writes on upload

* Bug fixes
  * Do not read the whole request body at once
  * Improve performance of url encoded params

* Deprecations
  * `Plug.Builder.compile/1` and `Plug.Builder.compile/2` are deprecated in favor of explicit `Plug.Builder.compile/3`

## v0.11.1

* Enhancements
  * Allow Plug mimes to be configured via application environment
  * Extend JSON parser to be compatible with all json compatible content types. This includes types with suffix `+json`
  * Add `Plug.Conn.clear_session/1`

* Bug fixes
  * Do not require cowboy at compile time
  * Also parse request bodies on DELETE requests

## v0.11.0

* Enhancements
  * Add `Plug.Conn.async_assign/3` and `Plug.Conn.await_assign/3` to start and await for assigns asynchronously, mimic'ing `Task.async/1` and `Task.await/2` behaviour
  * Add `Plug.Conn.WrapperError` to propagate an error with the connection for better debugging by either `Plug.Debugger` or `Plug.ErrorHandler`
  * Add `Plug.Conn.update_resp_header/4` to update a response header or set its initial value if not present

* Bug fixes
  * Skip parsing of files when no filename is sent
  * Fix how script_name are accumulated with multiple calls to `Plug.Router.forward/2`

* Backwards incompatible changes
  * `Plug.CSRFProtection` now uses a session to store tokens. Tokens are now generated on demand and can be accessed via `Plug.CSRFProtection.get_csrf_token/0`

## v0.10.0

* Enhancements
  * Add `:only` option to `Plug.Static` to avoid all requests triggering file system queries
  * Add ETag management to `Plug.Static` when requests to not contain a versioned query string
  * Enforce atom or string keys in `Plug.Conn.put_session/3` and friends and normalize keys to strings

* Bug fixes
  * Add UTF-8 tag to debugger templates

* Backwards incompatible changes
  * `Plug.CSRFProtection` now uses a cookie instead of session and expects a `"_csrf_token"` parameter instead of `"csrf_token"`

## v0.9.0

* Enhancements
  * Add `Plug.Conn.full_path/1`
  * Add `Plug.CSRFProtection` that adds cross-forgery protection
  * Add `Plug.ErrorHandler` that allows an error page to be sent on crashes (instead of a blank one)
  * Support host option in `Plug.Router`

* Backwards incompatible changes
  * Add `Plug.Router.Utils.build_match/1` was renamed to `build_path_match/1`

## v0.8.4

* Bug fixes
  * Clean up `{:plug_conn, :sent}` messages from listener inbox and ensure connection works accross processes

* Deprecations
  * Deprecate `recycle/2` in favor of `recycle_cookies` in Plug.Test

## v0.8.3

* Enhancements
  * Use PKCS7 padding in MessageEncryptor (the same as OpenSSL)
  * Add support for custom serializers in cookie session store
  * Allow customization of key generation in cookie session store
  * Automatically import `Plug.Conn` in Plug builder
  * Render errors from Plug when using Ranch/Cowboy nicely
  * Provide `Plug.Crypto.secure_compare/2` for comparing binaries
  * Add `Plug.Debugger` for helpful pages whenever there is a failure during a request

* Deprecations
  * Deprecate `:accept` in favor of `:pass` in Plug.Parsers

## v0.8.2

* Enhancements
  * Add `Plug.Conn.Utils.media_type/1` to provide media type parsing with wildcard support
  * Do not print adapter data by default when inspecting the connection
  * Allow plug_status to simplify the definition of plug aware exceptions
  * Allow cache headers in `Plug.Static` to be turned off

* Bug fix
  * Support dots on header parameter parsing

## v0.8.1

* Enhancements
  * Add a `Plug.Parsers.JSON` that expects a JSON decoder as argument

* Bug fix
  * Properly populate `params` field for test connections
  * Fix `Plug.Logger` not reporting the proper path

## v0.8.0

* Enhancements
  * Add `fetch_session/2`, `fetch_params/2` and `fetch_cookies/2` so they can be pluggable
  * Raise an error message on invalid router indentifiers
  * Add `put_status/2` and support atom status codes
  * Add `secret_key_base` field to the connection

* Backwards incompatible changes
  * Add `encryption_salt` and `signing_salt` to the CookieStore and derive actual keys from `secret_key_base`

## v0.7.0

* Enhancements
  * Support Elixir 1.0.0-rc1
  * Support haltable pipelines with `Plug.Conn.halt/2`
  * Ensure both Plug.Builder and Plug.Router's `call/2` are overridable

* Bug fix
  * Properly report times in Logger

* Backwards incompatible changes
  * Remove support for Plug wrappers

## v0.6.0

* Enhancements
  * Add `Plug.Logger`
  * Add `conn.peer` and `conn.remote_ip`
  * Add `Plug.Conn.sendfile/5`
  * Allow `call/2` from `Plug.Builder` to be overridable

## v0.5.3

* Enhancements
  * Update to Cowboy v1.0.0
  * Update mime types list
  * Update to Elixir v0.15.0

## v0.5.2

* Enhancements
  * Update to Elixir v0.14.3
  * Cowboy adapter now returns `{:error,:eaddrinuse}` when port is already in use
  * Add `Plug.Test.recycle/2` that copies relevant data between connections for future requests

## v0.5.1

* Enhancements
  * Add ability to configure when `Plug.Parsers` raises `UnsupportedMediaTypeError`
  * Add `Plug.Conn.Query.encode/1`
  * Add `CookieStore` for session

* Bug fixes
  * Ensure plug parses content-type with CRLF as LWS

## v0.5.0

* Enhancements
  * Update to Elixir v0.14.0
  * Update Cowboy adapter to v0.10.0
  * Add `Plug.Conn.read_body/2`

* Backwards incompatible changes
  * `Plug.Parsers` now expect `:length` instead of `:limit` and also accept `:read_length` and `:read_timeout`

## v0.4.4

* Enhancements
  * Update to Elixir v0.13.3

## v0.4.3

* Enhancements
  * Update to Elixir v0.13.2

## v0.4.2

* Enhancements
  * Update to Elixir v0.13.1

## v0.4.1

* Enhancements
  * Remove `:mime` dependency in favor of `Plug.MIME`
  * Improve errors when Cowboy is not available
  * First hex package release

## v0.4.0

* Enhancements
  * Support `before_send/1` callbacks
  * Add `Plug.Static`
  * Allow iodata as the body
  * Do not allow response headers to be set if the response was already sent
  * Add `Plug.Conn.private` to be used as storage by libraries/frameworks
  * Add `get_req_header` and `get_resp_header` for fetching request and response headers

* Backwards incompatible changes
  * `Plug.Connection` was removed in favor of `Plug.Conn`
  * `Plug.Conn` is now a struct
  * assigns, cookies, params and sessions have been converted to maps

## v0.3.0

* Definition of the Plug specification
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




























































































































































































































































































































































































































































































































































































































































Deleted deps/plug/LICENSE.

1
2
3
4
5
6
7
8
9
10
11
12
13
Copyright (c) 2013 Plataformatec.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
<
<
<
<
<
<
<
<
<
<
<
<
<


























Deleted deps/plug/README.md.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# Plug

[![Build Status](https://travis-ci.org/elixir-lang/plug.svg?branch=master)](https://travis-ci.org/elixir-lang/plug)
[![Inline docs](http://inch-ci.org/github/elixir-lang/plug.svg?branch=master)](http://inch-ci.org/github/elixir-lang/plug)

Plug is:

1. A specification for composable modules between web applications
2. Connection adapters for different web servers in the Erlang VM

[Documentation for Plug is available online](http://hexdocs.pm/plug/).

## Hello world

```elixir
defmodule MyPlug do
  import Plug.Conn

  def init(options) do
    # initialize options

    options
  end

  def call(conn, _opts) do
    conn
    |> put_resp_content_type("text/plain")
    |> send_resp(200, "Hello world")
  end
end
```

The snippet above shows a very simple example on how to use Plug. Save that snippet to a file and run it inside the plug application with:

    $ iex -S mix
    iex> c "path/to/file.ex"
    [MyPlug]
    iex> {:ok, _} = Plug.Adapters.Cowboy.http MyPlug, []
    {:ok, #PID<...>}

Access "http://localhost:4000/" and we are done!

## Installation

You can use plug in your projects in two steps:

1. Add plug and your webserver of choice (currently cowboy) to your `mix.exs` dependencies:

    ```elixir
    def deps do
      [{:cowboy, "~> 1.0.0"},
       {:plug, "~> 1.0"}]
    end
    ```

2. List both `:cowboy` and `:plug` as your application dependencies:

    ```elixir
    def application do
      [applications: [:cowboy, :plug]]
    end
    ```

## The Plug.Conn

In the hello world example, we defined our first plug. What is a plug after all?

A plug takes two shapes. It is a function that receives a connection and a set of options as arguments and returns the connection or it is a module that provides an `init/1` function to initialize options and implement the `call/2` function, receiving the connection and the initialized options, and returning the connection.

As per the specification above, a connection is represented by the `Plug.Conn` struct:

```elixir
%Plug.Conn{host: "www.example.com",
           path_info: ["bar", "baz"],
           ...}
```

Data can be read directly from the connection and also pattern matched on. Manipulating the connection often happens with the use of the functions defined in the `Plug.Conn` module. In our example, both `put_resp_content_type/2` and `send_resp/3` are defined in `Plug.Conn`.

Remember that, as everything else in Elixir, **a connection is immutable**, so every manipulation returns a new copy of the connection:

```elixir
conn = put_resp_content_type(conn, "text/plain")
conn = send_resp(conn, 200, "ok")
conn
```

Finally, keep in mind that a connection is a **direct interface to the underlying web server**. When you call `send_resp/3` above, it will immediately send the given status and body back to the client. This makes features like streaming a breeze to work with.

## The Plug Router

In practice, developers rarely write their own plugs. For example, Plug ships with a router that allows developers to quickly match on incoming requests and perform some action:

```elixir
defmodule AppRouter do
  use Plug.Router

  plug :match
  plug :dispatch

  get "/hello" do
    send_resp(conn, 200, "world")
  end

  forward "/users", to: UsersRouter

  match _ do
    send_resp(conn, 404, "oops")
  end
end
```

The router is a plug and, not only that, it contains its own plug pipeline too. The example above says that when the router is invoked, it will invoke the `:match` plug, represented by a local `match/2` function, and then call the `:dispatch` plug which will execute the matched code.

Plug ships with many plugs that you can add to the router plug pipeline, allowing you to plug something before a route matches or before a route is dispatched to. For example, if you want to add logging to the router, just do:

```elixir
plug Plug.Logger
plug :match
plug :dispatch
```

Note `Plug.Router` compiles all of your routes into a single function and relies on the Erlang VM to optimize the underlying routes into a tree lookup, instead of a linear lookup that would instead match route-per-route. This means route lookups are extremely fast in Plug!

This also means that a catch all `match` is recommended to be defined, as in the example above, otherwise routing fails with a function clause error (as it would in any regular Elixir function).

Each route needs to return the connection as per the Plug specification. See `Plug.Router` docs for more information.

## Testing plugs

Plug ships with a `Plug.Test` module that makes testing your plugs easy. Here is how we can test the router from above (or any other plug):

```elixir
defmodule MyPlugTest do
  use ExUnit.Case, async: true
  use Plug.Test

  @opts AppRouter.init([])

  test "returns hello world" do
    # Create a test connection
    conn = conn(:get, "/hello")

    # Invoke the plug
    conn = AppRouter.call(conn, @opts)

    # Assert the response and status
    assert conn.state == :sent
    assert conn.status == 200
    assert conn.resp_body == "world"
  end
end
```

### Available Plugs

This project aims to ship with different plugs that can be re-used across applications:

  * `Plug.CSRFProtection` - adds Cross-Site Request Forgery protection to your application. Typically required if you are using `Plug.Session`;
  * `Plug.Head` - converts HEAD requests to GET requests;
  * `Plug.Logger` - logs requests;
  * `Plug.MethodOverride` - overrides a request method with one specified in headers;
  * `Plug.Parsers` - responsible for parsing the request body given its content-type;
  * `Plug.RequestId` - sets up a request ID to be used in logs;
  * `Plug.Session` - handles session management and storage;
  * `Plug.SSL` - enforce requests through SSL;
  * `Plug.Static` - serves static files;

You can go into more details about each of them [in our docs](http://hexdocs.pm/plug/).

### Helper modules

Modules that can be used after you use `Plug.Router` or `Plug.Builder` to help development:

  * `Plug.Debugger` - shows a helpful debugging page every time there is a failure in a request;
  * `Plug.ErrorHandler` - allows developers to customize error pages in case of crashes instead of sending a blank one;

## License

Plug source code is released under Apache 2 License.
Check LICENSE file for more information.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<










































































































































































































































































































































































Deleted deps/plug/lib/plug.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
defmodule Plug do
  @moduledoc """
  The plug specification.

  There are two kind of plugs: function plugs and module plugs.

  #### Function plugs

  A function plug is any function that receives a connection and a set of
  options and returns a connection. Its type signature must be:

      (Plug.Conn.t, Plug.opts) :: Plug.Conn.t

  #### Module plugs

  A module plug is an extension of the function plug. It is a module that must
  export:

  * a `call/2` function with the signature defined above
  * an `init/1` function which takes a set of options and initializes it.

  The result returned by `init/1` is passed as second argument to `call/2`. Note
  that `init/1` may be called during compilation and as such it must not return
  pids, ports or values that are not specific to the runtime.

  The API expected by a module plug is defined as a behaviour by the
  `Plug` module (this module).

  ## Examples

  Here's an example of a function plug:

      def json_header_plug(conn, opts) do
        conn |> put_resp_content_type("application/json")
      end

  Here's an example of a module plug:

      defmodule JSONHeaderPlug do
        def init(opts) do
          opts
        end

        def call(conn, _opts) do
          conn |> put_resp_content_type("application/json")
        end
      end

  ## The Plug pipeline

  The `Plug.Builder` module provides conveniences for building plug
  pipelines.
  """

  @type opts :: tuple | atom | integer | float | [opts]

  use Behaviour
  use Application

  defcallback init(opts) :: opts
  defcallback call(Plug.Conn.t, opts) :: Plug.Conn.t

  @doc false
  def start(_type, _args) do
    Logger.add_translator {Plug.Adapters.Translator, :translate}
    Plug.Supervisor.start_link()
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<








































































































































Deleted deps/plug/lib/plug/adapters/cowboy.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
defmodule Plug.Adapters.Cowboy do
  @moduledoc """
  Adapter interface to the Cowboy webserver.

  ## Options

  * `:ip` - the ip to bind the server to.
    Must be a tuple in the format `{x, y, z, w}`.

  * `:port` - the port to run the server.
    Defaults to 4000 (http) and 4040 (https).

  * `:acceptors` - the number of acceptors for the listener.
    Defaults to 100.

  * `:max_connections` - max number of connections supported.
    Defaults to `:infinity`.

  * `:dispatch` - manually configure Cowboy's dispatch.
    If this option is used, the given plug won't be initialized
    nor dispatched to (and doing so becomes the user's responsibility).

  * `:ref` - the reference name to be used.
    Defaults to `plug.HTTP` (http) and `plug.HTTPS` (https).
    This is the value that needs to be given on shutdown.

  * `:compress` - Cowboy will attempt to compress the response body.
    Defaults to false.

  * `:timeout` - Time in ms with no requests before Cowboy closes the connection.
    Defaults to 5000ms.

  * `:protocol_options` - Specifies remaining protocol options,
    see [Cowboy protocol docs](http://ninenines.eu/docs/en/cowboy/1.0/manual/cowboy_protocol/).

  All other options are given to the underlying transport.
  """

  # Made public with @doc false for testing.
  @doc false
  def args(scheme, plug, opts, cowboy_options) do
    cowboy_options
    |> Keyword.put_new(:ref, build_ref(plug, scheme))
    |> Keyword.put_new(:dispatch, cowboy_options[:dispatch] || dispatch_for(plug, opts))
    |> normalize_cowboy_options(scheme)
    |> to_args()
  end

  @doc """
  Run cowboy under http.

  ## Example

      # Starts a new interface
      Plug.Adapters.Cowboy.http MyPlug, [], port: 80

      # The interface above can be shutdown with
      Plug.Adapters.Cowboy.shutdown MyPlug.HTTP

  """
  @spec http(module(), Keyword.t, Keyword.t) ::
        {:ok, pid} | {:error, :eaddrinuse} | {:error, term}
  def http(plug, opts, cowboy_options \\ []) do
    run(:http, plug, opts, cowboy_options)
  end

  @doc """
  Run cowboy under https.

  Besides the options described in the module documentation,
  this module also accepts all options defined in [the `ssl`
  erlang module] (http://www.erlang.org/doc/man/ssl.html),
  like keyfile, certfile, cacertfile and others.

  The certificate files can be given as a relative path.
  For such, the `:otp_app` option must also be given and
  certificates will be looked from the priv directory of
  the given application.

  ## Example

      # Starts a new interface
      Plug.Adapters.Cowboy.https MyPlug, [],
        port: 443,
        password: "SECRET",
        otp_app: :my_app,
        keyfile: "priv/ssl/key.pem",
        certfile: "priv/ssl/cert.pem"

      # The interface above can be shutdown with
      Plug.Adapters.Cowboy.shutdown MyPlug.HTTPS

  """
  @spec https(module(), Keyword.t, Keyword.t) ::
        {:ok, pid} | {:error, :eaddrinuse} | {:error, term}
  def https(plug, opts, cowboy_options \\ []) do
    Application.ensure_all_started(:ssl)
    run(:https, plug, opts, cowboy_options)
  end

  @doc """
  Shutdowns the given reference.
  """
  def shutdown(ref) do
    :cowboy.stop_listener(ref)
  end

  @doc """
  Returns a child spec to be supervised by your application.
  """
  def child_spec(scheme, plug, opts, cowboy_options \\ []) do
    [ref, nb_acceptors, trans_opts, proto_opts] = args(scheme, plug, opts, cowboy_options)
    ranch_module = case scheme do
      :http  -> :ranch_tcp
      :https -> :ranch_ssl
    end
    :ranch.child_spec(ref, nb_acceptors, ranch_module, trans_opts, :cowboy_protocol, proto_opts)
  end

  ## Helpers

  @http_cowboy_options  [port: 4000]
  @https_cowboy_options [port: 4040]
  @protocol_options [:timeout, :compress]

  defp run(scheme, plug, opts, cowboy_options) do
    case Application.ensure_all_started(:cowboy) do
      {:ok, _} ->
        :ok
      {:error, {:cowboy, _}} ->
        raise "could not start the cowboy application. Please ensure it is listed " <>
              "as a dependency both in deps and application in your mix.exs"
    end
    apply(:cowboy, :"start_#{scheme}", args(scheme, plug, opts, cowboy_options))
  end

  defp normalize_cowboy_options(cowboy_options, :http) do
    Keyword.merge @http_cowboy_options, cowboy_options
  end

  defp normalize_cowboy_options(cowboy_options, :https) do
    assert_ssl_options(cowboy_options)
    cowboy_options = Keyword.merge @https_cowboy_options, cowboy_options
    cowboy_options = Enum.reduce [:keyfile, :certfile, :cacertfile], cowboy_options, &normalize_ssl_file(&1, &2)
    cowboy_options = Enum.reduce [:password], cowboy_options, &to_char_list(&2, &1)
    cowboy_options
  end

  defp to_args(all_opts) do
    {initial_transport_options, opts} = Enum.partition(all_opts, &is_atom/1)
    opts = Keyword.delete(opts, :otp_app)
    {ref, opts} = Keyword.pop(opts, :ref)
    {dispatch, opts} = Keyword.pop(opts, :dispatch)
    {acceptors, opts} = Keyword.pop(opts, :acceptors, 100)
    {protocol_options, opts} = Keyword.pop(opts, :protocol_options, [])

    dispatch = :cowboy_router.compile(dispatch)
    {extra_options, transport_options} = Keyword.split(opts, @protocol_options)
    protocol_options = [env: [dispatch: dispatch]] ++ protocol_options ++ extra_options

    [ref, acceptors, initial_transport_options ++ transport_options, protocol_options]
  end

  defp build_ref(plug, scheme) do
    Module.concat(plug, scheme |> to_string |> String.upcase)
  end

  defp dispatch_for(plug, opts) do
    opts = plug.init(opts)
    [{:_, [ {:_, Plug.Adapters.Cowboy.Handler, {plug, opts}} ]}]
  end

  defp normalize_ssl_file(key, cowboy_options) do
    value = cowboy_options[key]

    cond do
      is_nil(value) ->
        cowboy_options
      Path.type(value) == :absolute ->
        put_ssl_file cowboy_options, key, value
      true ->
        put_ssl_file cowboy_options, key, Path.expand(value, otp_app(cowboy_options))
    end
  end

  defp assert_ssl_options(cowboy_options) do
    unless Keyword.has_key?(cowboy_options, :key) or
           Keyword.has_key?(cowboy_options, :keyfile) do
      fail "missing option :key/:keyfile"
    end
    unless Keyword.has_key?(cowboy_options, :cert) or
           Keyword.has_key?(cowboy_options, :certfile) do
      fail "missing option :cert/:certfile"
    end
  end

  defp put_ssl_file(cowboy_options, key, value) do
    value = to_char_list(value)
    unless File.exists?(value) do
      fail "the file #{value} required by SSL's #{inspect key} does not exist"
    end
    Keyword.put(cowboy_options, key, value)
  end

  defp otp_app(cowboy_options) do
    if app = cowboy_options[:otp_app] do
      Application.app_dir(app)
    else
      fail "to use a relative certificate with https, the :otp_app " <>
           "option needs to be given to the adapter"
    end
  end

  defp to_char_list(cowboy_options, key) do
    if value = cowboy_options[key] do
      Keyword.put cowboy_options, key, to_char_list(value)
    else
      cowboy_options
    end
  end

  defp fail(message) do
    raise ArgumentError, message: "could not start Cowboy adapter, " <> message
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


































































































































































































































































































































































































































































Deleted deps/plug/lib/plug/adapters/cowboy/conn.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
defmodule Plug.Adapters.Cowboy.Conn do
  @behaviour Plug.Conn.Adapter
  @moduledoc false

  alias :cowboy_req, as: Request

  def conn(req, transport) do
    {path, req} = Request.path req
    {host, req} = Request.host req
    {port, req} = Request.port req
    {meth, req} = Request.method req
    {hdrs, req} = Request.headers req
    {qs, req}   = Request.qs req
    {peer, req} = Request.peer req
    {remote_ip, _} = peer

    %Plug.Conn{
      adapter: {__MODULE__, req},
      host: host,
      method: meth,
      owner: self(),
      path_info: split_path(path),
      peer: peer,
      port: port,
      remote_ip: remote_ip,
      query_string: qs,
      req_headers: hdrs,
      request_path: path,
      scheme: scheme(transport)
   }
  end

  def send_resp(req, status, headers, body) do
    {:ok, req} = Request.reply(status, headers, body, req)
    {:ok, nil, req}
  end

  def send_file(req, status, headers, path, offset, length) do
    %File.Stat{type: :regular, size: size} = File.stat!(path)

    length =
      cond do
        length == :all -> size
        is_integer(length) -> length
      end

    body_fun = fn(socket, transport) -> transport.sendfile(socket, path, offset, length) end

    {:ok, req} = Request.reply(status, headers, Request.set_resp_body_fun(length, body_fun, req))
    {:ok, nil, req}
  end

  def send_chunked(req, status, headers) do
    {:ok, req} = Request.chunked_reply(status, headers, req)
    {:ok, nil, req}
  end

  def chunk(req, body) do
    Request.chunk(body, req)
  end

  def read_req_body(req, opts \\ []) do
    Request.body(req, opts)
  end

  def parse_req_multipart(req, opts, callback) do
    # We need to remove the length from the list
    # otherwise cowboy will attempt to load the
    # whole length at once.
    {limit, opts} = Keyword.pop(opts, :length, 8_000_000)
    {:ok, limit, acc, req} = parse_multipart(Request.part(req), limit, opts, [], callback)

    params = Enum.reduce(acc, %{}, &Plug.Conn.Query.decode_pair/2)

    if limit > 0 do
      {:ok, params, req}
    else
      {:more, params, req}
    end
  end

  ## Helpers

  defp scheme(:tcp), do: :http
  defp scheme(:ssl), do: :https

  defp split_path(path) do
    segments = :binary.split(path, "/", [:global])
    for segment <- segments, segment != "", do: segment
  end

  ## Multipart

  defp parse_multipart({:ok, headers, req}, limit, opts, acc, callback) when limit >= 0 do
    case callback.(headers) do
      {:binary, name} ->
        {:ok, limit, body, req} =
          parse_multipart_body(Request.part_body(req, opts), limit, opts, "")

        Plug.Conn.Utils.validate_utf8!(body, "multipart body")
        parse_multipart(Request.part(req), limit, opts, [{name, body}|acc], callback)

      {:file, name, path, %Plug.Upload{} = uploaded} ->
        {:ok, file} = File.open(path, [:write, :binary, :delayed_write, :raw])

        {:ok, limit, req} =
          parse_multipart_file(Request.part_body(req, opts), limit, opts, file)

        :ok = File.close(file)
        parse_multipart(Request.part(req), limit, opts, [{name, uploaded}|acc], callback)

      :skip ->
        parse_multipart(Request.part(req), limit, opts, acc, callback)
    end
  end

  defp parse_multipart({:ok, _headers, req}, limit, _opts, acc, _callback) do
    {:ok, limit, acc, req}
  end

  defp parse_multipart({:done, req}, limit, _opts, acc, _callback) do
    {:ok, limit, acc, req}
  end

  defp parse_multipart_body({:more, tail, req}, limit, opts, body) when limit >= 0 do
    parse_multipart_body(Request.part_body(req, opts), limit - byte_size(tail), opts, body <> tail)
  end

  defp parse_multipart_body({:more, _tail, req}, limit, _opts, body) do
    {:ok, limit, body, req}
  end

  defp parse_multipart_body({:ok, tail, req}, limit, _opts, body) when limit >= byte_size(tail) do
    {:ok, limit, body <> tail, req}
  end

  defp parse_multipart_body({:ok, _tail, req}, limit, _opts, body) do
    {:ok, limit, body, req}
  end

  defp parse_multipart_file({:more, tail, req}, limit, opts, file) when limit >= 0 do
    IO.binwrite(file, tail)
    parse_multipart_file(Request.part_body(req, opts), limit - byte_size(tail), opts, file)
  end

  defp parse_multipart_file({:more, _tail, req}, limit, _opts, _file) do
    {:ok, limit, req}
  end

  defp parse_multipart_file({:ok, tail, req}, limit, _opts, file) when limit >= byte_size(tail) do
    IO.binwrite(file, tail)
    {:ok, limit, req}
  end

  defp parse_multipart_file({:ok, _tail, req}, limit, _opts, _file) do
    {:ok, limit, req}
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




























































































































































































































































































































Deleted deps/plug/lib/plug/adapters/cowboy/handler.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
defmodule Plug.Adapters.Cowboy.Handler do
  @moduledoc false
  @connection Plug.Adapters.Cowboy.Conn
  @already_sent {:plug_conn, :sent}

  def init({transport, :http}, req, {plug, opts}) when transport in [:tcp, :ssl] do
    {:upgrade, :protocol, __MODULE__, req, {transport, plug, opts}}
  end

  def upgrade(req, env, __MODULE__, {transport, plug, opts}) do
    conn = @connection.conn(req, transport)
    try do
      %{adapter: {@connection, req}} =
        conn
        |> plug.call(opts)
        |> maybe_send(plug)

      {:ok, req, [{:result, :ok} | env]}
    catch
      :error, value ->
        stack = System.stacktrace()
        exception = Exception.normalize(:error, value, stack)
        reason = {{exception, stack}, {plug, :call, [conn, opts]}}
        terminate(reason, req, stack)
      :throw, value ->
        stack = System.stacktrace()
        reason = {{{:nocatch, value}, stack}, {plug, :call, [conn, opts]}}
        terminate(reason, req, stack)
      :exit, value ->
        stack = System.stacktrace()
        reason = {value, {plug, :call, [conn, opts]}}
        terminate(reason, req, stack)
    after
      receive do
        @already_sent -> :ok
      after
        0 -> :ok
      end
    end
  end

  defp maybe_send(%Plug.Conn{state: :unset}, _plug),      do: raise Plug.Conn.NotSentError
  defp maybe_send(%Plug.Conn{state: :set} = conn, _plug), do: Plug.Conn.send_resp(conn)
  defp maybe_send(%Plug.Conn{} = conn, _plug),            do: conn
  defp maybe_send(other, plug) do
    raise "Cowboy adapter expected #{inspect plug} to return Plug.Conn but got: #{inspect other}"
  end

  defp terminate(reason, req, stack) do
    :cowboy_req.maybe_reply(stack, req)
    exit(reason)
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<










































































































Deleted deps/plug/lib/plug/adapters/test/conn.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
defmodule Plug.Adapters.Test.Conn do
  @behaviour Plug.Conn.Adapter
  @moduledoc false

  ## Test helpers

  def conn(conn, method, uri, body_or_params) do
    maybe_flush()

    uri    = URI.parse(uri)
    method = method |> to_string |> String.upcase
    query  = uri.query || ""
    owner  = self()

    {body, params, req_headers} = body_or_params(body_or_params, query, conn.req_headers)
    state = %{method: method, params: params, req_body: body,
              chunks: nil, ref: make_ref, owner: owner}

    %Plug.Conn{conn |
      adapter: {__MODULE__, state},
      host: uri.host || "www.example.com",
      method: method,
      owner: owner,
      path_info: split_path(uri.path),
      port: uri.port || 80,
      peer: {{127, 0, 0, 1}, 111317},
      remote_ip: {127, 0, 0, 1},
      req_headers: req_headers,
      request_path: uri.path,
      query_string: query,
      params: params || %Plug.Conn.Unfetched{aspect: :params},
      scheme: (uri.scheme || "http") |> String.downcase |> String.to_atom}
  end

  ## Connection adapter

  def send_resp(%{method: "HEAD"} = state, status, headers, _body) do
    do_send state, status, headers, ""
  end
  def send_resp(state, status, headers, body) do
    do_send state, status, headers, IO.iodata_to_binary(body)
  end

  def send_file(%{method: "HEAD"} = state, status, headers, _path, _offset, _length) do
    do_send state, status, headers, ""
  end
  def send_file(state, status, headers, path, offset, length) do
    %File.Stat{type: :regular, size: size} = File.stat!(path)

    length =
      cond do
        length == :all -> size
        is_integer(length) -> length
      end

    {:ok, data} = File.open!(path, [:read, :binary], fn device ->
      :file.pread(device, offset, length)
    end)

    do_send state, status, headers, data
  end

  def send_chunked(state, _status, _headers),
    do: {:ok, "", %{state | chunks: ""}}
  def chunk(%{method: "HEAD"} = state, _body),
    do: {:ok, "", state}
  def chunk(%{chunks: chunks} = state, body) do
    body = chunks <> IO.iodata_to_binary(body)
    {:ok, body, %{state | chunks: body}}
  end

  defp do_send(%{owner: owner, ref: ref} = state, status, headers, body) do
    send owner, {ref, {status, headers, body}}
    {:ok, body, state}
  end

  def read_req_body(%{req_body: body} = state, opts \\ []) do
    size = min(byte_size(body), Keyword.get(opts, :length, 8_000_000))
    data = :binary.part(body, 0, size)
    rest = :binary.part(body, size, byte_size(body) - size)
    tag =
      case rest do
        "" -> :ok
        _  -> :more
      end
    {tag, data, %{state | req_body: rest}}
  end

  def parse_req_multipart(%{params: multipart} = state, _limit, _callback) do
    {:ok, multipart, %{state | params: nil}}
  end

  ## Private helpers

  defp body_or_params(nil, _query, headers),
    do: {"", nil, headers}

  defp body_or_params(body, _query, headers) when is_binary(body) do
    {body, nil, headers}
  end

  defp body_or_params(params, query, headers) when is_list(params) do
    body_or_params(Enum.into(params, %{}), query, headers)
  end

  defp body_or_params(params, query, headers) when is_map(params) do
    headers = :lists.keystore("content-type", 1, headers,
                              {"content-type", "multipart/mixed; charset: utf-8"})
    params = Map.merge(Plug.Conn.Query.decode(query), stringify_params(params))
    {"", params, headers}
  end

  defp stringify_params([{_, _}|_] = params),
    do: Enum.into(params, %{}, &stringify_kv/1)
  defp stringify_params([_|_] = params),
    do: Enum.map(params, &stringify_params/1)
  defp stringify_params(%{__struct__: mod} = struct) when is_atom(mod),
    do: struct
  defp stringify_params(%{} = params),
    do: Enum.into(params, %{}, &stringify_kv/1)
  defp stringify_params(other),
    do: other

  defp stringify_kv({k, v}),
    do: {to_string(k), stringify_params(v)}

  defp split_path(path) do
    segments = :binary.split(path, "/", [:global])
    for segment <- segments, segment != "", do: segment
  end

  @already_sent {:plug_conn, :sent}

  defp maybe_flush() do
    receive do
      @already_sent -> :ok
    after
      0 -> :ok
    end
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


























































































































































































































































































Deleted deps/plug/lib/plug/adapters/translator.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
defmodule Plug.Adapters.Translator do
  @moduledoc """
  A translator module shared by adapters that ship with Plug.

  We host all translations in a single module which is added
  to Logger when the :plug application starts.
  """

  ## Entry point

  @doc """
  The `translate/4` function expected by custom Logger translators.
  """
  def translate(min_level, :error, :format,
                {'Ranch listener' ++ _, [ref, protocol, pid, reason]}) do
    {:ok, translate_ranch(min_level, ref, protocol, pid, reason)}
  end

  def translate(_min_level, _level, _kind, _data) do
    :none
  end

  ## Ranch/Cowboy

  defp translate_ranch(min_level, _ref, :cowboy_protocol, pid,
                       {reason, {mod, :call, [%Plug.Conn{} = conn, _opts]}}) do
    [inspect(pid), " running ", inspect(mod), " terminated\n",
      conn_info(min_level, conn) |
      Exception.format(:exit, reason, [])]
  end

  defp translate_ranch(_min_level, ref, protocol, pid, reason) do
    ["Ranch protocol ", inspect(pid), " (", inspect(protocol),
      ") of listener ", inspect(ref), " terminated\n" |
      Exception.format(:exit, reason, [])]
  end

  ## Helpers

  defp conn_info(_min_level, conn) do
    [server_info(conn), request_info(conn)]
  end

  defp server_info(%Plug.Conn{host: host, port: port, scheme: scheme}) do
    ["Server: ", host, ":", Integer.to_string(port), ?\s, ?(, Atom.to_string(scheme), ?), ?\n]
  end

  defp request_info(%Plug.Conn{method: method, query_string: query_string} = conn) do
    ["Request: ", method, ?\s, path_to_iodata(conn.request_path, query_string), ?\n]
  end

  defp path_to_iodata(path, ""), do: path
  defp path_to_iodata(path, qs), do: [path, ??, qs]
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<












































































































Deleted deps/plug/lib/plug/builder.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
defmodule Plug.Builder do
  @moduledoc """
  Conveniences for building plugs.

  This module can be `use`-d into a module in order to build
  a plug pipeline:

      defmodule MyApp do
        use Plug.Builder

        plug Plug.Logger
        plug :hello, upper: true

        # A function from another module can be plugged too, provided it's
        # imported into the current module first.
        import AnotherModule, only: [interesting_plug: 2]
        plug :interesting_plug

        def hello(conn, opts) do
          body = if opts[:upper], do: "WORLD", else: "world"
          send_resp(conn, 200, body)
        end
      end

  Multiple plugs can be defined with the `plug/2` macro, forming a pipeline.
  The plugs in the pipeline will be executed in the order they've been added
  through the `plug/2` macro. In the example above, `Plug.Logger` will be
  called first and then the `:hello` function plug will be called on the
  resulting connection.

  `Plug.Builder` also imports the `Plug.Conn` module, making functions like
  `send_resp/3` available.

  ## Options

  When used, the following options are accepted by `Plug.Builder`:

    * `:log_on_halt` - accepts the level to log whenever the request is halted

  ## Plug behaviour

  Internally, `Plug.Builder` implements the `Plug` behaviour, which means both
  the `init/1` and `call/2` functions are defined.

  By implementing the Plug API, `Plug.Builder` guarantees this module is a plug
  and can be handed to a web server or used as part of another pipeline.

  ## Overriding the default Plug API functions

  Both the `init/1` and `call/2` functions defined by `Plug.Builder` can be
  manually overridden. For example, the `init/1` function provided by
  `Plug.Builder` returns the options that it receives as an argument, but its
  behaviour can be customized:

      defmodule PlugWithCustomOptions do
        use Plug.Builder
        plug Plug.Logger

        def init(opts) do
          opts
        end
      end

  The `call/2` function that `Plug.Builder` provides is used internally to
  execute all the plugs listed using the `plug` macro, so overriding the
  `call/2` function generally implies using `super` in order to still call the
  plug chain:

      defmodule PlugWithCustomCall do
        use Plug.Builder
        plug Plug.Logger
        plug Plug.Head

        def call(conn, _opts) do
          super(conn, opts) # calls Plug.Logger and Plug.Head
          assign(conn, :called_all_plugs, true)
        end
      end


  ## Halting a plug pipeline

  A plug pipeline can be halted with `Plug.Conn.halt/1`. The builder will
  prevent further plugs downstream from being invoked and return the current
  connection. In the following example, the `Plug.Logger` plug never gets
  called:

      defmodule PlugUsingHalt do
        use Plug.Builder

        plug :stopper
        plug Plug.Logger

        def stopper(conn, _opts) do
          halt(conn)
        end
      end
  """

  @type plug :: module | atom

  @doc false
  defmacro __using__(opts) do
    quote do
      @behaviour Plug
      @plug_builder_opts unquote(opts)

      def init(opts) do
        opts
      end

      def call(conn, opts) do
        plug_builder_call(conn, opts)
      end

      defoverridable [init: 1, call: 2]

      import Plug.Conn
      import Plug.Builder, only: [plug: 1, plug: 2]

      Module.register_attribute(__MODULE__, :plugs, accumulate: true)
      @before_compile Plug.Builder
    end
  end

  @doc false
  defmacro __before_compile__(env) do
    plugs        = Module.get_attribute(env.module, :plugs)
    builder_opts = Module.get_attribute(env.module, :plug_builder_opts)

    if plugs == [] do
      raise "no plugs have been defined in #{inspect env.module}"
    end

    {conn, body} = Plug.Builder.compile(env, plugs, builder_opts)

    quote do
      defp plug_builder_call(unquote(conn), _), do: unquote(body)
    end
  end

  @doc """
  A macro that stores a new plug. `opts` will be passed unchanged to the new
  plug.

  This macro doesn't add any guards when adding the new plug to the pipeline;
  for more information about adding plugs with guards see `compile/1`.

  ## Examples

      plug Plug.Logger               # plug module
      plug :foo, some_options: true  # plug function

  """
  defmacro plug(plug, opts \\ []) do
    quote do
      @plugs {unquote(plug), unquote(opts), true}
    end
  end

  @doc """
  Compiles a plug pipeline.

  Each element of the plug pipeline (according to the type signature of this
  function) has the form:

      {plug_name, options, guards}

  Note that this function expects a reversed pipeline (with the last plug that
  has to be called coming first in the pipeline).

  The function returns a tuple with the first element being a quoted reference
  to the connection and the second element being the compiled quoted pipeline.

  ## Examples

      Plug.Builder.compile(env, [
        {Plug.Logger, [], true}, # no guards, as added by the Plug.Builder.plug/2 macro
        {Plug.Head, [], quote(do: a when is_binary(a))}
      ], [])

  """
  @spec compile(Macro.Env.t, [{plug, Plug.opts, Macro.t}], Keyword.t) :: {Macro.t, Macro.t}
  def compile(env, pipeline, builder_opts) do
    conn = quote do: conn
    {conn, Enum.reduce(pipeline, conn, &quote_plug(init_plug(&1), &2, env, builder_opts))}
  end

  # Initializes the options of a plug at compile time.
  defp init_plug({plug, opts, guards}) do
    case Atom.to_char_list(plug) do
      'Elixir.' ++ _ -> init_module_plug(plug, opts, guards)
      _              -> init_fun_plug(plug, opts, guards)
    end
  end

  defp init_module_plug(plug, opts, guards) do
    initialized_opts = plug.init(opts)

    if function_exported?(plug, :call, 2) do
      {:module, plug, initialized_opts, guards}
    else
      raise ArgumentError, message: "#{inspect plug} plug must implement call/2"
    end
  end

  defp init_fun_plug(plug, opts, guards) do
    {:function, plug, opts, guards}
  end

  # `acc` is a series of nested plug calls in the form of
  # plug3(plug2(plug1(conn))). `quote_plug` wraps a new plug around that series
  # of calls.
  defp quote_plug({plug_type, plug, opts, guards}, acc, env, builder_opts) do
    call = quote_plug_call(plug_type, plug, opts)

    error_message = case plug_type do
      :module   -> "expected #{inspect plug}.call/2 to return a Plug.Conn"
      :function -> "expected #{plug}/2 to return a Plug.Conn"
    end <> ", all plugs must receive a connection (conn) and return a connection"

    quote do
      case unquote(compile_guards(call, guards)) do
        %Plug.Conn{halted: true} = conn ->
          unquote(log_halt(plug_type, plug, env, builder_opts))
          conn
        %Plug.Conn{} = conn ->
          unquote(acc)
        _ ->
          raise unquote(error_message)
      end
    end
  end

  defp quote_plug_call(:function, plug, opts) do
    quote do: unquote(plug)(conn, unquote(Macro.escape(opts)))
  end

  defp quote_plug_call(:module, plug, opts) do
    quote do: unquote(plug).call(conn, unquote(Macro.escape(opts)))
  end

  defp compile_guards(call, true) do
    call
  end

  defp compile_guards(call, guards) do
    quote do
      case true do
        true when unquote(guards) -> unquote(call)
        true -> conn
      end
    end
  end

  defp log_halt(plug_type, plug, env, builder_opts) do
    if level = builder_opts[:log_on_halt] do
      message = case plug_type do
        :module   -> "#{inspect env.module} halted in #{inspect plug}.call/2"
        :function -> "#{inspect env.module} halted in #{inspect plug}/2"
      end

      quote do
        require Logger
        Logger.unquote(level)(unquote(message))
      end
    else
      nil
    end
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






























































































































































































































































































































































































































































































































































Deleted deps/plug/lib/plug/conn.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
alias Plug.Conn.Unfetched

defmodule Plug.Conn do
  @moduledoc """
  The Plug connection.

  This module defines a `Plug.Conn` struct and the main functions
  for working with Plug connections.

  Note request headers are normalized to lowercase and response
  headers are expected to have lower-case keys.

  ## Request fields

  These fields contain request information:

  * `host` - the requested host as a binary, example: `"www.example.com"`
  * `method` - the request method as a binary, example: `"GET"`
  * `path_info` - the path split into segments, example: `["hello", "world"]`
  * `script_name` - the initial portion of the URL's path that corresponds to the application
    routing, as segments, example: ["sub","app"].
  * `request_path` - the requested path, example: `/trailing/and//double//slashes/`
  * `port` - the requested port as an integer, example: `80`
  * `peer` - the actual TCP peer that connected, example: `{{127, 0, 0, 1}, 12345}`. Often this
    is not the actual IP and port of the client, but rather of a load-balancer or request-router.
  * `remote_ip` - the IP of the client, example: `{151, 236, 219, 228}`. This field is meant to
    be overwritten by plugs that understand e.g. the `X-Forwarded-For` header or HAProxy's PROXY
    protocol. It defaults to peer's IP.
  * `req_headers` - the request headers as a list, example: `[{"content-type", "text/plain"}]`
  * `scheme` - the request scheme as an atom, example: `:http`
  * `query_string` - the request query string as a binary, example: `"foo=bar"`

  ## Fetchable fields

  The request information in these fields is not populated until it is fetched
  using the associated `fetch_` function. For example, the `cookies` field uses
  `fetch_cookies/2`.

  If you access these fields before fetching them, they will be returned as
  `Plug.Conn.Unfetched` structs.

  * `cookies`- the request cookies with the response cookies
  * `query_params` - the request query params
  * `params` - the request params. Usually populated by a plug, like `Plug.Parsers`
  * `req_cookies` - the request cookies (without the response ones)

  ## Response fields

  These fields contain response information:

  * `resp_body` - the response body, by default is an empty string. It is set
    to nil after the response is set, except for test connections.
  * `resp_charset` - the response charset, defaults to "utf-8"
  * `resp_cookies` - the response cookies with their name and options
  * `resp_headers` - the response headers as a dict, by default `cache-control`
    is set to `"max-age=0, private, must-revalidate"`
  * `status` - the response status

  Furthermore, the `before_send` field stores callbacks that are invoked
  before the connection is sent. Callbacks are invoked in the reverse order
  they are registered (callbacks registered first are invoked last) in order
  to reproduce a pipeline ordering.

  ## Connection fields

  * `assigns` - shared user data as a dict
  * `owner` - the Elixir process that owns the connection
  * `halted` - the boolean status on whether the pipeline was halted
  * `secret_key_base` - a secret key used to verify and encrypt cookies.
    the field must be set manually whenever one of those features are used.
    This data must be kept in the connection and never used directly, always
    use `Plug.Crypto.KeyGenerator.generate/3` to derive keys from it
  * `state` - the connection state

  The connection state is used to track the connection lifecycle. It starts
  as `:unset` but is changed to `:set` (via `Plug.Conn.resp/3`) or `:file`
  (when invoked via `Plug.Conn.send_file/3`). Its final result is
  `:sent` or `:chunked` depending on the response model.

  ## Private fields

  These fields are reserved for libraries/framework usage.

  * `adapter` - holds the adapter information in a tuple
  * `private` - shared library data as a dict

  ## Protocols

  `Plug.Conn` implements both the Collectable and Inspect protocols
  out of the box. The inspect protocol provides a nice representation
  of the connection while the collectable protocol allows developers
  to easily chunk data. For example:

      # Send the chunked response headers
      conn = send_chunked(conn, 200)

      # Pipe the given list into a connection
      # Each item is emitted as a chunk
      Enum.into(~w(each chunk as a word), conn)

  """

  @type adapter         :: {module, term}
  @type assigns         :: %{atom => any}
  @type before_send     :: [(t -> t)]
  @type body            :: iodata | nil
  @type cookies         :: %{binary => binary}
  @type halted          :: boolean
  @type headers         :: [{binary, binary}]
  @type host            :: binary
  @type int_status      :: non_neg_integer | nil
  @type owner           :: pid
  @type method          :: binary
  @type param           :: binary | %{binary => param} | [param]
  @type params          :: %{binary => param}
  @type peer            :: {:inet.ip_address, :inet.port_number}
  @type port_number     :: :inet.port_number
  @type query_string    :: String.t
  @type resp_cookies    :: %{binary => %{}}
  @type scheme          :: :http | :https
  @type secret_key_base :: binary | nil
  @type segments        :: [binary]
  @type state           :: :unset | :set | :file | :chunked | :sent
  @type status          :: atom | int_status

  @type t :: %__MODULE__{
              adapter:         adapter,
              assigns:         assigns,
              before_send:     before_send,
              body_params:     params | Unfetched.t,
              cookies:         cookies | Unfetched.t,
              host:            host,
              method:          method,
              owner:           owner,
              params:          params | Unfetched.t,
              path_info:       segments,
              port:            :inet.port_number,
              private:         assigns,
              query_params:    params | Unfetched.t,
              query_string:    query_string,
              peer:            peer,
              remote_ip:       :inet.ip_address,
              req_cookies:     cookies | Unfetched.t,
              req_headers:     headers,
              request_path:    binary,
              resp_body:       body,
              resp_cookies:    resp_cookies,
              resp_headers:    headers,
              scheme:          scheme,
              script_name:     segments,
              secret_key_base: secret_key_base,
              state:           state,
              status:          int_status}

  defstruct adapter:         {Plug.Conn, nil},
            assigns:         %{},
            before_send:     [],
            body_params:     %Unfetched{aspect: :body_params},
            cookies:         %Unfetched{aspect: :cookies},
            halted:          false,
            host:            "www.example.com",
            method:          "GET",
            owner:           nil,
            params:          %Unfetched{aspect: :params},
            path_info:       [],
            port:            0,
            private:         %{},
            query_params:    %Unfetched{aspect: :query_params},
            query_string:    "",
            peer:            nil,
            remote_ip:       nil,
            req_cookies:     %Unfetched{aspect: :cookies},
            req_headers:     [],
            request_path:    "",
            resp_body:       nil,
            resp_cookies:    %{},
            resp_headers:    [{"cache-control", "max-age=0, private, must-revalidate"}],
            scheme:          :http,
            script_name:     [],
            secret_key_base: nil,
            state:           :unset,
            status:          nil

  defmodule NotSentError do
    defexception message: "no response was set nor sent from the connection"

    @moduledoc """
    Error raised when no response is sent in a request
    """
  end

  defmodule AlreadySentError do
    defexception message: "the response was already sent"

    @moduledoc """
    Error raised when trying to modify or send an already sent response
    """
  end

  defmodule InvalidHeaderError do
    defexception message: "header is invalid"

    @moduledoc ~S"""
    Error raised when trying to send a header that errors, for example:

      * the header key contains uppercase chars
      * the header value contains newlines \n
    """
  end

  alias Plug.Conn
  @already_sent {:plug_conn, :sent}
  @unsent [:unset, :set]

  @doc """
  Assigns a value to a key in the connection

  ## Examples

      iex> conn.assigns[:hello]
      nil
      iex> conn = assign(conn, :hello, :world)
      iex> conn.assigns[:hello]
      :world

  """
  @spec assign(t, atom, term) :: t
  def assign(%Conn{assigns: assigns} = conn, key, value) when is_atom(key) do
    %{conn | assigns: Map.put(assigns, key, value)}
  end

  @doc """
  Starts a task to assign a value to a key in the connection.

  `await_assign/2` can be used to wait for the async task to complete and
  retrieve the resulting value.

  Behind the scenes, it uses `Task.async/1`.

  ## Examples

      iex> conn.assigns[:hello]
      nil
      iex> conn = async_assign(conn, :hello, fn -> :world end)
      iex> conn.assigns[:hello]
      %Task{...}

  """
  @spec async_assign(t, atom, (() -> term)) :: t
  def async_assign(%Conn{} = conn, key, fun) when is_atom(key) and is_function(fun, 0) do
    assign(conn, key, Task.async(fun))
  end

  @doc """
  Awaits the completion of an async assign.

  Returns a connection with the value resulting from the async assignment placed
  under `key` in the `:assigns` field.

  Behind the scenes, it uses `Task.await/2`.

  ## Examples

      iex> conn.assigns[:hello]
      nil
      iex> conn = async_assign(conn, :hello, fn -> :world end)
      iex> conn = await_assign(conn, :hello) # blocks until `conn.assigns[:hello]` is available
      iex> conn.assigns[:hello]
      :world

  """
  @spec await_assign(t, atom, timeout) :: t
  def await_assign(%Conn{} = conn, key, timeout \\ 5000) when is_atom(key) do
    task = Map.fetch!(conn.assigns, key)
    assign(conn, key, Task.await(task, timeout))
  end

  @doc """
  Assigns a new **private** key and value in the connection.

  This storage is meant to be used by libraries and frameworks to avoid writing
  to the user storage (the `:assigns` field). It is recommended for
  libraries/frameworks to prefix the keys with the library name.

  For example, if some plug needs to store a `:hello` key, it
  should do so as `:plug_hello`:

      iex> conn.private[:plug_hello]
      nil
      iex> conn = put_private(conn, :plug_hello, :world)
      iex> conn.private[:plug_hello]
      :world

  """
  @spec put_private(t, atom, term) :: t
  def put_private(%Conn{private: private} = conn, key, value) when is_atom(key) do
    %{conn | private: Map.put(private, key, value)}
  end

  @doc """
  Stores the given status code in the connection.

  The status code can be `nil`, an integer or an atom. The list of allowed
  atoms is available in `Plug.Conn.Status`.

  Raises a `Plug.Conn.AlreadySentError` if the connection has already been
  `:sent`.
  """
  @spec put_status(t, status) :: t
  def put_status(%Conn{state: :sent}, _status),
    do: raise AlreadySentError
  def put_status(%Conn{} = conn, nil),
    do: %{conn | status: nil}
  def put_status(%Conn{} = conn, status),
    do: %{conn | status: Plug.Conn.Status.code(status)}

  @doc """
  Sends a response to the client.

  It expects the connection state to be `:set`, otherwise raises an
  `ArgumentError` for `:unset` connections or a `Plug.Conn.AlreadySentError` for
  already `:sent` connections.

  At the end sets the connection state to `:sent`.
  """
  @spec send_resp(t) :: t | no_return
  def send_resp(conn)

  def send_resp(%Conn{state: :unset}) do
    raise ArgumentError, message: "cannot send a response that was not set"
  end

  def send_resp(%Conn{adapter: {adapter, payload}, state: :set, owner: owner} = conn) do
    conn = run_before_send(conn, :set)
    {:ok, body, payload} = adapter.send_resp(payload, conn.status, conn.resp_headers, conn.resp_body)
    send owner, @already_sent
    %{conn | adapter: {adapter, payload}, resp_body: body, state: :sent}
  end

  def send_resp(%Conn{}) do
    raise AlreadySentError
  end

  @doc """
  Sends a file as the response body with the given `status`
  and optionally starting at the given offset until the given length.

  If available, the file is sent directly over the socket using
  the operating system `sendfile` operation.

  It expects a connection that has not been `:sent` yet and sets its
  state to `:sent` afterwards. Otherwise raises `Plug.Conn.AlreadySentError`.
  """
  @spec send_file(t, status, filename :: binary, offset ::integer, length :: integer | :all) :: t | no_return
  def send_file(conn, status, file, offset \\ 0, length  \\ :all)

  def send_file(%Conn{state: state}, status, _file, _offset, _length)
      when not state in @unsent do
    _ = Plug.Conn.Status.code(status)
    raise AlreadySentError
  end

  def send_file(%Conn{adapter: {adapter, payload}, owner: owner} = conn, status, file, offset, length)
      when is_binary(file) do
    conn = run_before_send(%{conn | status: Plug.Conn.Status.code(status), resp_body: nil}, :file)
    {:ok, body, payload} = adapter.send_file(payload, conn.status, conn.resp_headers, file, offset, length)
    send owner, @already_sent
    %{conn | adapter: {adapter, payload}, state: :sent, resp_body: body}
  end

  @doc """
  Sends the response headers as a chunked response.

  It expects a connection that has not been `:sent` yet and sets its
  state to `:chunked` afterwards. Otherwise raises `Plug.Conn.AlreadySentError`.
  """
  @spec send_chunked(t, status) :: t | no_return
  def send_chunked(%Conn{state: state}, status)
      when not state in @unsent do
    _ = Plug.Conn.Status.code(status)
    raise AlreadySentError
  end

  def send_chunked(%Conn{adapter: {adapter, payload}, owner: owner} = conn, status) do
    conn = run_before_send(%{conn | status: Plug.Conn.Status.code(status), resp_body: nil}, :chunked)
    {:ok, body, payload} = adapter.send_chunked(payload, conn.status, conn.resp_headers)
    send owner, @already_sent
    %{conn | adapter: {adapter, payload}, resp_body: body}
  end

  @doc """
  Sends a chunk as part of a chunked response.

  It expects a connection with state `:chunked` as set by
  `send_chunked/2`. It returns `{:ok, conn}` in case of success,
  otherwise `{:error, reason}`.
  """
  @spec chunk(t, body) :: {:ok, t} | {:error, term} | no_return
  def chunk(%Conn{adapter: {adapter, payload}, state: :chunked} = conn, chunk) do
    case adapter.chunk(payload, chunk) do
      :ok                  -> {:ok, conn}
      {:ok, body, payload} -> {:ok, %{conn | resp_body: body, adapter: {adapter, payload}}}
      {:error, _} = error  -> error
    end
  end

  def chunk(%Conn{}, chunk) when is_binary(chunk) or is_list(chunk) do
    raise ArgumentError, message: "chunk/2 expects a chunked response. Please ensure " <>
                                  "you have called send_chunked/2 before you send a chunk"
  end

  @doc """
  Sends a response with the given status and body.

  See `send_resp/1` for more information.
  """
  @spec send_resp(t, status, body) :: t | no_return
  def send_resp(%Conn{} = conn, status, body) do
    conn |> resp(status, body) |> send_resp()
  end

  @doc """
  Sets the response to the given `status` and `body`.

  It sets the connection state to `:set` (if not already `:set`)
  and raises `Plug.Conn.AlreadySentError` if it was already `:sent`.
  """
  @spec resp(t, status, body) :: t
  def resp(%Conn{state: state}, status, _body)
      when not state in @unsent do
    _ = Plug.Conn.Status.code(status)
    raise AlreadySentError
  end

  def resp(%Conn{}, _status, nil) do
    raise ArgumentError, "response body cannot be set to nil"
  end

  def resp(%Conn{} = conn, status, body)
      when is_binary(body) or is_list(body) do
    %{conn | status: Plug.Conn.Status.code(status), resp_body: body, state: :set}
  end

  @doc """
  Returns the values of the request header specified by `key`.
  """
  @spec get_req_header(t, binary) :: [binary]
  def get_req_header(%Conn{req_headers: headers}, key) when is_binary(key) do
    for {k, v} <- headers, k == key, do: v
  end

  @doc """
  Adds a new request header (`key`) if not present, otherwise replaces the
  previous value of that header with `value`.

  Raises a `Plug.Conn.AlreadySentError` if the connection has already been
  `:sent`.
  """
  @spec put_req_header(t, binary, binary) :: t
  def put_req_header(%Conn{state: :sent}, _key, _value) do
    raise AlreadySentError
  end

  def put_req_header(%Conn{adapter: adapter, req_headers: headers} = conn, key, value) when
      is_binary(key) and is_binary(value) do
    validate_header_key!(adapter, key)
    %{conn | req_headers: List.keystore(headers, key, 0, {key, value})}
  end

  @doc """
  Deletes a request header if present.

  Raises a `Plug.Conn.AlreadySentError` if the connection has already been
  `:sent`.
  """
  @spec delete_req_header(t, binary) :: t
  def delete_req_header(%Conn{state: :sent}, _key) do
    raise AlreadySentError
  end

  def delete_req_header(%Conn{req_headers: headers} = conn, key) when
      is_binary(key) do
    %{conn | req_headers: List.keydelete(headers, key, 0)}
  end

  @doc """
  Updates a request header if present, otherwise it sets it to an initial
  value.

  Raises a `Plug.Conn.AlreadySentError` if the connection has already been
  `:sent`.
  """
  @spec update_req_header(t, binary, binary, (binary -> binary)) :: t
  def update_req_header(%Conn{state: :sent}, _key, _initial, _fun) do
    raise AlreadySentError
  end

  def update_req_header(%Conn{} = conn, key, initial, fun) when
      is_binary(key) and is_binary(initial) and is_function(fun, 1) do
    case get_req_header(conn, key) do
      []          -> put_req_header(conn, key, initial)
      [current|_] -> put_req_header(conn, key, fun.(current))
    end
  end

  @doc """
  Returns the values of the response header specified by `key`.

  ## Examples

      iex> conn = %{conn | resp_headers: [{"content-type", "text/plain"}]}
      iex> conn |> get_resp_header("content-type")
      ["text/plain"]

  """
  @spec get_resp_header(t, binary) :: [binary]
  def get_resp_header(%Conn{resp_headers: headers}, key) when is_binary(key) do
    for {k, v} <- headers, k == key, do: v
  end

  @doc """
  Adds a new response header (`key`) if not present, otherwise replaces the
  previous value of that header with `value`.

  Raises a `Plug.Conn.AlreadySentError` if the connection has already been
  `:sent`.
  """
  @spec put_resp_header(t, binary, binary) :: t
  def put_resp_header(%Conn{state: :sent}, _key, _value) do
    raise AlreadySentError
  end

  def put_resp_header(%Conn{adapter: adapter, resp_headers: headers} = conn, key, value) when
      is_binary(key) and is_binary(value) do
    validate_header_key!(adapter, key)
    validate_header_value!(value)
    %{conn | resp_headers: List.keystore(headers, key, 0, {key, value})}
  end

  @doc """
  Merges a series of response headers into the connection.
  """
  @spec merge_resp_headers(t, Enum.t) :: t
  def merge_resp_headers(%Conn{state: :sent}, _headers) do
    raise AlreadySentError
  end

  def merge_resp_headers(conn, headers) when headers == %{} do
    conn
  end

  def merge_resp_headers(%Conn{resp_headers: current} = conn, headers) do
    headers =
      Enum.reduce headers, current, fn
        {key, value}, acc when is_binary(key) and is_binary(value) ->
          List.keystore(acc, key, 0, {key, value})
      end
    %{conn | resp_headers: headers}
  end

  @doc """
  Deletes a response header if present.

  Raises a `Plug.Conn.AlreadySentError` if the connection has already been
  `:sent`.
  """
  @spec delete_resp_header(t, binary) :: t
  def delete_resp_header(%Conn{state: :sent}, _key) do
    raise AlreadySentError
  end

  def delete_resp_header(%Conn{resp_headers: headers} = conn, key) when
      is_binary(key) do
    %{conn | resp_headers: List.keydelete(headers, key, 0)}
  end

  @doc """
  Updates a response header if present, otherwise it sets it to an initial
  value.

  Raises a `Plug.Conn.AlreadySentError` if the connection has already been
  `:sent`.
  """
  @spec update_resp_header(t, binary, binary, (binary -> binary)) :: t
  def update_resp_header(%Conn{state: :sent}, _key, _initial, _fun) do
    raise AlreadySentError
  end

  def update_resp_header(%Conn{} = conn, key, initial, fun) when
      is_binary(key) and is_binary(initial) and is_function(fun, 1) do
    case get_resp_header(conn, key) do
      []          -> put_resp_header(conn, key, initial)
      [current|_] -> put_resp_header(conn, key, fun.(current))
    end
  end

  @doc """
  Sets the value of the `"content-type"` response header taking into account the
  `charset`.
  """
  @spec put_resp_content_type(t, binary, binary | nil) :: t
  def put_resp_content_type(conn, content_type, charset \\ "utf-8")

  def put_resp_content_type(conn, content_type, nil) when is_binary(content_type) do
    conn |> put_resp_header("content-type", content_type)
  end

  def put_resp_content_type(conn, content_type, charset) when
      is_binary(content_type) and is_binary(charset) do
    conn |> put_resp_header("content-type", "#{content_type}; charset=#{charset}")
  end

  @doc """
  Fetches query parameters from the query string.

  This function does not fetch parameters from the body. To fetch
  parameters from the body, use the `Plug.Parsers` plug.
  """
  @spec fetch_query_params(t, Keyword.t) :: t
  def fetch_query_params(conn, opts \\ [])

  def fetch_query_params(%Conn{query_params: %Unfetched{}, params: params,
                               query_string: query_string} = conn, _opts) do
    Plug.Conn.Utils.validate_utf8!(query_string, "query string")
    query_params = Plug.Conn.Query.decode(query_string)

    case params do
      %Unfetched{} -> %{conn | query_params: query_params, params: query_params}
      %{}          -> %{conn | query_params: query_params, params: Map.merge(query_params, params)}
    end
  end

  def fetch_query_params(%Conn{} = conn, _opts) do
    conn
  end

  @doc """
  Reads the request body.

  This function reads a chunk of the request body up to a given `:length`. If
  there is more data to be read, then `{:more, partial_body, conn}` is
  returned. Otherwise `{:ok, body, conn}` is returned. In case of an error
  reading the socket, `{:error, reason}` is returned as per `:gen_tcp.recv/2`.

  In order to, for instance, support slower clients you can tune the
  `:read_length` and `:read_timeout` options. These specify how much time should
  be allowed to pass for each read from the underlying socket.

  Because the request body can be of any size, reading the body will only
  work once, as Plug will not cache the result of these operations. If you
  need to access the body multiple times, it is your responsibility to store
  it. Finally keep in mind some plugs like `Plug.Parsers` may read the body,
  so the body may be unavailable after accessing such plugs.

  This function is able to handle both chunked and identity transfer-encoding
  by default.

  ## Options

  * `:length` - sets the maximum number of bytes to read from the body for each
    chunk, defaults to 8_000_000 bytes
  * `:read_length` - sets the amount of bytes to read at one time from the
    underlying socket to fill the chunk, defaults to 1_000_000 bytes
  * `:read_timeout` - sets the timeout for each socket read, defaults to
    15_000 ms

  The values above are not meant to be exact. For example, setting the
  length to 8_000_000 may end-up reading some hundred bytes more from
  the socket until we halt.

  ## Examples

      {:ok, body, conn} = Plug.Conn.read_body(conn, length: 1_000_000)

  """
  @spec read_body(t, Keyword.t) :: {:ok, binary, t} |
                                   {:more, binary, t} |
                                   {:error, term}
  def read_body(%Conn{adapter: {adapter, state}} = conn, opts \\ []) do
    case adapter.read_req_body(state, opts) do
      {:ok, data, state} ->
        {:ok, data, %{conn | adapter: {adapter, state}}}
      {:more, data, state} ->
        {:more, data, %{conn | adapter: {adapter, state}}}
      {:error, reason} ->
        {:error, reason}
    end
  end

  @doc """
  Fetches cookies from the request headers.
  """
  @spec fetch_cookies(t, Keyword.t) :: t
  def fetch_cookies(conn, opts \\ [])

  def fetch_cookies(%Conn{req_cookies: %Unfetched{},
                          resp_cookies: resp_cookies,
                          req_headers: req_headers} = conn, _opts) do
    req_cookies =
      for {"cookie", cookie} <- req_headers,
          kv <- Plug.Conn.Cookies.decode(cookie),
          into: %{},
          do: kv

    cookies = Enum.reduce(resp_cookies, req_cookies, fn
      {key, opts}, acc ->
        if value = Map.get(opts, :value) do
          Map.put(acc, key, value)
        else
          Map.delete(acc, key)
        end
    end)

    %{conn | req_cookies: req_cookies, cookies: cookies}
  end

  def fetch_cookies(%Conn{} = conn, _opts) do
    conn
  end

  @doc """
  Puts a response cookie.

  ## Options

    * `:domain` - the domain the cookie applies to
    * `:max_age` - the cookie max-age
    * `:path` - the path the cookie applies to
    * `:http_only` - when false, the cookie is accessible beyond http
    * `:secure` - if the cookie must be sent only over https. Defaults
      to true when the connection is https

  """
  @spec put_resp_cookie(t, binary, binary, Keyword.t) :: t
  def put_resp_cookie(%Conn{resp_cookies: resp_cookies, scheme: scheme} = conn, key, value, opts \\ []) when
      is_binary(key) and is_binary(value) and is_list(opts) do
    cookie = [{:value, value}|opts] |> :maps.from_list() |> maybe_secure_cookie(scheme)
    resp_cookies = Map.put(resp_cookies, key, cookie)
    %{conn | resp_cookies: resp_cookies} |> update_cookies(&Map.put(&1, key, value))
  end

  defp maybe_secure_cookie(cookie, :https), do: Map.put_new(cookie, :secure, true)
  defp maybe_secure_cookie(cookie, _),      do: cookie

  @epoch {{1970, 1, 1}, {0, 0, 0}}

  @doc """
  Deletes a response cookie.

  Deleting a cookie requires the same options as to when the cookie was put.
  Check `put_resp_cookie/4` for more information.
  """
  @spec delete_resp_cookie(t, binary, Keyword.t) :: t
  def delete_resp_cookie(%Conn{resp_cookies: resp_cookies} = conn, key, opts \\ []) when
      is_binary(key) and is_list(opts) do
    opts = [universal_time: @epoch, max_age: 0] ++ opts
    resp_cookies = Map.put(resp_cookies, key, :maps.from_list(opts))
    %{conn | resp_cookies: resp_cookies} |> update_cookies(&Map.delete(&1, key))
  end

  @doc """
  Fetches the session from the session store. Will also fetch cookies.
  """
  @spec fetch_session(t, Keyword.t) :: t
  def fetch_session(conn, opts \\ [])

  def fetch_session(%Conn{private: private} = conn, _opts) do
    case Map.fetch(private, :plug_session_fetch) do
      {:ok, :done} -> conn
      {:ok, fun} -> conn |> fetch_cookies |> fun.()
      :error -> raise ArgumentError, "cannot fetch session without a configured session plug"
    end
  end

  @doc """
  Puts the specified `value` in the session for the given `key`.

  The key can be a string or an atom, where atoms are
  automatically converted to strings. Can only be invoked
  on unsent `conn`s. Will raise otherwise.
  """
  @spec put_session(t, String.t | atom, any) :: t
  def put_session(%Conn{state: state}, _key, _value) when not state in @unsent,
    do: raise AlreadySentError
  def put_session(conn, key, value) do
    put_session(conn, &Map.put(&1, session_key(key), value))
  end

  @doc """
  Returns session value for the given `key`.

  The key can be a string or an atom, where atoms are
  automatically converted to strings.
  """
  @spec get_session(t, String.t | atom) :: any
  def get_session(conn, key) do
    conn |> get_session |> Map.get(session_key(key))
  end

  @doc """
  Deletes the session for the given `key`.

  The key can be a string or an atom, where atoms are
  automatically converted to strings.
  """
  @spec delete_session(t, String.t | atom) :: t
  def delete_session(%Conn{state: state}, _key) when not state in @unsent,
    do: raise AlreadySentError
  def delete_session(conn, key) do
    put_session(conn, &Map.delete(&1, session_key(key)))
  end

  @doc """
  Clears the entire session.

  This function removes every key from the session, clearing the session.

  Note that, even if `clear_session/1` is used, the session is still sent to the
  client. If the session should be effectively *dropped*, `configure_session/2`
  should be used with the `:drop` option set to `true`.
  """
  @spec clear_session(t) :: t
  def clear_session(conn) do
    put_session(conn, fn(_existing) -> Map.new end)
  end

  @doc """
  Configures the session.

  ## Options

    * `:renew` - generates a new session id for the cookie
    * `:drop` - drops the session, a session cookie will not be included in the
      response
    * `:ignore` - ignores all changes made to the session in this request cycle

  """
  @spec configure_session(t, Keyword.t) :: t
  def configure_session(%Conn{state: state}, _opts) when not state in @unsent,
    do: raise AlreadySentError
  def configure_session(conn, opts) do
    # Ensure the session is available.
    _ = get_session(conn)

    cond do
      opts[:renew]  -> put_private(conn, :plug_session_info, :renew)
      opts[:drop]   -> put_private(conn, :plug_session_info, :drop)
      opts[:ignore] -> put_private(conn, :plug_session_info, :ignore)
      true          -> conn
    end
  end

  @doc """
  Registers a callback to be invoked before the response is sent.

  Callbacks are invoked in the reverse order they are defined (callbacks
  defined first are invoked last).
  """
  @spec register_before_send(t, (t -> t)) :: t
  def register_before_send(%Conn{state: state}, _callback)
      when not state in @unsent do
    raise AlreadySentError
  end

  def register_before_send(%Conn{before_send: before_send} = conn, callback)
      when is_function(callback, 1) do
    %{conn | before_send: [callback|before_send]}
  end

  @doc """
  Halts the Plug pipeline by preventing further plugs downstream from being
  invoked. See the docs for `Plug.Builder` for more information on halting a
  plug pipeline.
  """
  @spec halt(t) :: t
  def halt(%Conn{} = conn) do
    %{conn | halted: true}
  end

  ## Helpers

  defp run_before_send(%Conn{before_send: before_send} = conn, new) do
    conn = Enum.reduce before_send, %{conn | state: new}, &(&1.(&2))
    if conn.state != new do
      raise ArgumentError, message: "cannot send/change response from run_before_send callback"
    end
    %{conn | resp_headers: merge_headers(conn.resp_headers, conn.resp_cookies)}
  end

  defp merge_headers(headers, cookies) do
    Enum.reduce(cookies, headers, fn {key, opts}, acc ->
      [{"set-cookie", Plug.Conn.Cookies.encode(key, opts)}|acc]
    end)
  end

  defp update_cookies(%Conn{state: :sent}, _fun),
    do: raise AlreadySentError
  defp update_cookies(%Conn{cookies: %Unfetched{}} = conn, _fun),
    do: conn
  defp update_cookies(%Conn{cookies: cookies} = conn, fun),
    do: %{conn | cookies: fun.(cookies)}

  defp session_key(binary) when is_binary(binary), do: binary
  defp session_key(atom) when is_atom(atom), do: Atom.to_string(atom)

  defp get_session(%Conn{private: private}) do
    if session = Map.get(private, :plug_session) do
      session
    else
      raise ArgumentError, message: "session not fetched, call fetch_session/2"
    end
  end

  defp put_session(conn, fun) do
    private = conn.private
              |> Map.put(:plug_session, get_session(conn) |> fun.())
              |> Map.put_new(:plug_session_info, :write)

    %{conn | private: private}
  end

  defp validate_header_key!({Plug.Adapters.Test.Conn, _}, key) do
    unless valid_header_key?(key) do
      raise InvalidHeaderError, message: "header key is not lowercase: " <> inspect(key)
    end
  end

  defp validate_header_key!(_adapter, _key) do
    :ok
  end

  # Any string containing an UPPERCASE char is not valid.
  defp valid_header_key?(<<h, _::binary>>) when h in ?A..?Z, do: false
  defp valid_header_key?(<<_, t::binary>>), do: valid_header_key?(t)
  defp valid_header_key?(<<>>), do: true
  defp valid_header_key?(_), do: false

  defp validate_header_value!(value) do
    case :binary.match(value, "\n") do
      {_, _}   -> raise InvalidHeaderError, message: "header value contains newline (\\n): " <> inspect(value)
      :nomatch -> :ok
    end
  end
end

defimpl Inspect, for: Plug.Conn do
  def inspect(conn, opts) do
    conn =
      if opts.limit == :infinity do
        conn
      else
        update_in conn.adapter, fn {adapter, _data} -> {adapter, :...} end
      end

    Inspect.Any.inspect(conn, opts)
  end
end

defimpl Collectable, for: Plug.Conn do
  def into(conn) do
    {conn, fn
      conn, {:cont, x} ->
        {:ok, conn} = Plug.Conn.chunk(conn, x)
        conn
      conn, _ ->
        conn
    end}
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted deps/plug/lib/plug/conn/adapter.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
defmodule Plug.Conn.Adapter do
  @moduledoc """
  Specification of the connection adapter API implemented by webservers
  """
  use Behaviour

  alias Plug.Conn
  @typep payload :: term

  @doc """
  Sends the given status, headers and body as a response
  back to the client.

  If the request has method `"HEAD"`, the adapter should
  not send the response to the client.

  Webservers are advised to return `nil` as the sent_body,
  as the body can no longer be manipulated. However, the
  test implementation returns the actual body so it can
  be used during testing.
  """
  defcallback send_resp(payload, Conn.status, Conn.headers, Conn.body) ::
              {:ok, sent_body :: binary | nil, payload}

  @doc """
  Sends the given status, headers and file as a response
  back to the client.

  If the request has method `"HEAD"`, the adapter should
  not send the response to the client.

  Webservers are advised to return `nil` as the sent_body,
  as the body can no longer be manipulated. However, the
  test implementation returns the actual body so it can
  be used during testing.
  """
  defcallback send_file(payload, Conn.status, Conn.headers, file :: binary,
                        offset :: integer, length :: integer | :all) ::
              {:ok, sent_body :: binary | nil, payload}

  @doc """
  Sends the given status, headers as the beginning of
  a chunked response to the client.

  Webservers are advised to return `nil` as the sent_body,
  as the body can no longer be manipulated. However, the
  test implementation returns the actual body so it can
  be used during testing.
  """
  defcallback send_chunked(payload, Conn.status, Conn.headers) ::
              {:ok, sent_body :: binary | nil, payload}

  @doc """
  Sends a chunk in the chunked response.

  If the request has method `"HEAD"`, the adapter should
  not send the response to the client.

  Webservers are advised to return `:ok` and not modify
  any further state for each chunk. However, the test
  implementation returns the actual body and payload so
  it can be used during testing.
  """
  defcallback chunk(payload, Conn.status) ::
              :ok | {:ok, sent_body :: binary, payload} | {:error, term}

  @doc """
  Reads the request body.

  Read the docs in `Plug.Conn.read_body/2` for the supported
  options and expected behaviour.
  """
  defcallback read_req_body(payload, options :: Keyword.t) ::
              {:ok, data :: binary, payload} |
              {:more, data :: binary, payload} |
              {:error, term}

  @doc """
  Parses a multipart request.

  This function receives the payload, the body limit and a callback.
  When parsing each multipart segment, the parser should invoke the
  given fallback passing the headers for that segment, before consuming
  the body. The callback will return one of the following values:

  * `{:binary, name}` - the current segment must be treated as a regular
                          binary value with the given `name`
  * `{:file, name, file, upload} - the current segment is a file upload with `name`
                                     and contents should be written to the given `file`
  * `:skip` - this multipart segment should be skipped

  This function may return a `:ok` or `:more` tuple. The first one is
  returned when there is no more multipart data to be processed.

  For the supported options, please read `Plug.Conn.read_body/2` docs.
  """
  defcallback parse_req_multipart(payload, options :: Keyword.t, fun) ::
              {:ok, Conn.params, payload} | {:more, Conn.params, payload}
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






































































































































































































Deleted deps/plug/lib/plug/conn/cookies.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
defmodule Plug.Conn.Cookies do
  @moduledoc """
  Conveniences for encoding and decoding cookies.
  """

  @doc """
  Decodes the given cookies as given in a request header.

  If a cookie is invalid, it is automatically discarded from the result.

  ## Examples

      iex> decode("key1=value1, key2=value2")
      %{"key1" => "value1", "key2" => "value2"}

  """
  def decode(cookie) do
    do_decode(:binary.split(cookie, [";", ","], [:global]), %{})
  end

  defp do_decode([], acc),
    do: acc
  defp do_decode([h|t], acc) do
    case decode_kv(h) do
      {k, v} -> do_decode(t, Map.put(acc, k, v))
      false  -> do_decode(t, acc)
    end
  end

  defp decode_kv(""),
    do: false
  defp decode_kv(<< ?$, _ :: binary >>),
    do: false
  defp decode_kv(<< h, t :: binary >>) when h in [?\s, ?\t],
    do: decode_kv(t)
  defp decode_kv(kv),
    do: decode_key(kv, "")

  defp decode_key("", _key),
    do: false
  defp decode_key(<< ?=, _ :: binary >>, ""),
    do: false
  defp decode_key(<< ?=, t :: binary >>, key),
    do: decode_value(t, "", key, "")
  defp decode_key(<< h, _ :: binary >>, _key) when h in [?\s, ?\t, ?\r, ?\n, ?\v, ?\f],
    do: false
  defp decode_key(<< h, t :: binary >>, key),
    do: decode_key(t, << key :: binary, h >>)

  defp decode_value("", _spaces, key, value),
    do: {key, value}
  defp decode_value(<< ?\s, t :: binary >>, spaces, key, value),
    do: decode_value(t, << spaces :: binary, ?\s >>, key, value)
  defp decode_value(<< h, _ :: binary >>, _spaces, _key, _value) when h in [?\t, ?\r, ?\n, ?\v, ?\f],
    do: false
  defp decode_value(<< h, t :: binary >>, spaces, key, value),
    do: decode_value(t, "", key, << value :: binary, spaces :: binary , h >>)

  @doc """
  Encodes the given cookies as expected in a response header.
  """
  def encode(key, opts \\ %{}) when is_map(opts) do
    value  = Map.get(opts, :value)
    path   = Map.get(opts, :path, "/")
    header = "#{key}=#{value}; path=#{path}"

    if domain = Map.get(opts, :domain) do
      header = header <> "; domain=#{domain}"
    end

    if max_age = Map.get(opts, :max_age) do
      time = Map.get(opts, :universal_time) || :calendar.universal_time
      time = add_seconds(time, max_age)
      header = header <> "; expires=" <> rfc2822(time) <> "; max-age=" <> Integer.to_string(max_age)
    end

    if Map.get(opts, :secure, false) do
      header = header <> "; secure"
    end

    if Map.get(opts, :http_only, true) do
      header = header <> "; HttpOnly"
    end

    header
  end

  defp pad(number) when number in 0..9, do: <<?0, ?0 + number>>
  defp pad(number), do: Integer.to_string(number)

  defp rfc2822({{year, month, day} = date, {hour, minute, second}}) do
    weekday_name  = weekday_name(:calendar.day_of_the_week(date))
    month_name    = month_name(month)
    padded_day    = pad(day)
    padded_hour   = pad(hour)
    padded_minute = pad(minute)
    padded_second = pad(second)
    binary_year   = Integer.to_string(year)

    weekday_name <> ", " <> padded_day <>
      " " <> month_name <> " " <> binary_year <>
      " " <> padded_hour <> ":" <> padded_minute <>
      ":" <> padded_second <> " GMT"
  end

  defp weekday_name(1), do: "Mon"
  defp weekday_name(2), do: "Tue"
  defp weekday_name(3), do: "Wed"
  defp weekday_name(4), do: "Thu"
  defp weekday_name(5), do: "Fri"
  defp weekday_name(6), do: "Sat"
  defp weekday_name(7), do: "Sun"

  defp month_name(1),  do: "Jan"
  defp month_name(2),  do: "Feb"
  defp month_name(3),  do: "Mar"
  defp month_name(4),  do: "Apr"
  defp month_name(5),  do: "May"
  defp month_name(6),  do: "Jun"
  defp month_name(7),  do: "Jul"
  defp month_name(8),  do: "Aug"
  defp month_name(9),  do: "Sep"
  defp month_name(10), do: "Oct"
  defp month_name(11), do: "Nov"
  defp month_name(12), do: "Dec"

  defp add_seconds(time, seconds_to_add) do
    time_seconds = :calendar.datetime_to_gregorian_seconds(time)
    :calendar.gregorian_seconds_to_datetime(time_seconds + seconds_to_add)
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






































































































































































































































































Deleted deps/plug/lib/plug/conn/query.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
defmodule Plug.Conn.Query do
  @moduledoc """
  Conveniences for decoding and encoding url encoded queries.

  Plug allows a developer to build query strings
  that map to Elixir structures in order to make
  manipulation of such structures easier on the server
  side. Here are some examples:

      iex> decode("foo=bar")["foo"]
      "bar"

  If a value is given more than once, the last value takes precedence:

      iex> decode("foo=bar&foo=baz")["foo"]
      "baz"

  Nested structures can be created via `[key]`:

      iex> decode("foo[bar]=baz")["foo"]["bar"]
      "baz"

  Lists are created with `[]`:

      iex> decode("foo[]=bar&foo[]=baz")["foo"]
      ["bar", "baz"]

  Dicts can be encoded:

      iex> encode(%{foo: "bar", baz: "bat"})
      "baz=bat&foo=bar"

  Encoding keyword lists preserves the order of the fields:

      iex> encode([foo: "bar", baz: "bat"])
      "foo=bar&baz=bat"

  When encoding keyword lists with duplicate keys, the key that comes first
  takes precedence:

      iex> encode([foo: "bar", foo: "bat"])
      "foo=bar"

  Encoding named lists:

      iex> encode(%{foo: ["bar", "baz"]})
      "foo[]=bar&foo[]=baz"

  Encoding nested structures:

      iex> encode(%{foo: %{bar: "baz"}})
      "foo[bar]=baz"

  """

  @doc """
  Decodes the given binary.
  """
  def decode(query, initial \\ %{})

  def decode("", initial) do
    initial
  end

  def decode(query, initial) do
    parts = :binary.split(query, "&", [:global])
    Enum.reduce(Enum.reverse(parts), initial, &decode_string_pair(&1, &2))
  end

  defp decode_string_pair(binary, acc) do
    current =
      case :binary.split(binary, "=") do
        [key, value] ->
          {URI.decode_www_form(key), URI.decode_www_form(value)}
        [key] ->
          {URI.decode_www_form(key), nil}
      end

    decode_pair(current, acc)
  end

  @doc """
  Decodes the given tuple and stores it in the accumulator.
  It parses the key and stores the value into the current
  accumulator.

  Parameter lists are added to the accumulator in reverse
  order, so be sure to pass the parameters in reverse order.
  """
  def decode_pair({key, value}, acc) do
    parts =
      if key != "" and :binary.last(key) == ?] do
        # Remove trailing ]
        subkey = :binary.part(key, 0, byte_size(key) - 1)

        # Split the first [ then split remaining ][.
        #
        #     users[address][street #=> [ "users", "address][street" ]
        #
        case :binary.split(subkey, "[") do
          [key, subpart] ->
            [key|:binary.split(subpart, "][", [:global])]
          _ ->
            [key]
        end
      else
        [key]
      end

    assign_parts parts, value, acc
  end

  # We always assign the value in the last segment.
  # `age=17` would match here.
  defp assign_parts([key], value, acc) do
    Map.put_new(acc, key, value)
  end

  # The current segment is a list. We simply prepend
  # the item to the list or create a new one if it does
  # not yet. This assumes that items are iterated in
  # reverse order.
  defp assign_parts([key,""|t], value, acc) do
    case Map.fetch(acc, key) do
      {:ok, current} when is_list(current) ->
        Map.put(acc, key, assign_list(t, current, value))
      :error ->
        Map.put(acc, key, assign_list(t, [], value))
      _ ->
        acc
    end
  end

  # The current segment is a parent segment of a
  # map. We need to create a map and then
  # continue looping.
  defp assign_parts([key|t], value, acc) do
    case Map.fetch(acc, key) do
      {:ok, %{} = current} ->
        Map.put(acc, key, assign_parts(t, value, current))
      :error ->
        Map.put(acc, key, assign_parts(t, value, %{}))
      _ ->
        acc
    end
  end

  defp assign_list(t, current, value) do
    if value = assign_list(t, value), do: [value|current], else: current
  end

  defp assign_list([], value), do: value
  defp assign_list(t, value),  do: assign_parts(t, value, %{})

  @doc """
  Encodes the given dict.
  """
  def encode(dict, encoder \\ &to_string/1) do
    IO.iodata_to_binary encode_pair("", dict, encoder)
  end

  # covers structs
  defp encode_pair(field, %{__struct__: struct} = map, encoder) when is_atom(struct) do
    [field, ?=|encode_value(map, encoder)]
  end

  # covers maps
  defp encode_pair(parent_field, %{} = map, encoder) do
    encode_dict(map, parent_field, encoder)
  end

  # covers keyword lists
  defp encode_pair(parent_field, list, encoder) when is_list(list) and is_tuple(hd(list)) do
    encode_dict(Enum.uniq(list, &elem(&1, 0)), parent_field, encoder)
  end

  # covers non-keyword lists
  defp encode_pair(parent_field, list, encoder) when is_list(list) do
    prune Enum.flat_map list, fn value ->
      [?&, encode_pair(parent_field <> "[]", value, encoder)]
    end
  end

  # covers nil
  defp encode_pair(field, nil, _encoder) do
    [field, ?=]
  end

  # encoder fallback
  defp encode_pair(field, value, encoder) do
    [field, ?=|encode_value(value, encoder)]
  end

  defp encode_dict(dict, parent_field, encoder) do
    prune Enum.flat_map dict, fn
      {_, value} when value in [%{}, []] ->
        []
      {field, value} ->
        field =
          if parent_field == "" do
            encode_key(field)
          else
            parent_field <> "[" <> encode_key(field) <> "]"
          end
        [?&, encode_pair(field, value, encoder)]
    end
  end

  defp encode_key(item) do
    item |> to_string |> URI.encode_www_form
  end

  defp encode_value(item, encoder) do
    item |> encoder.() |> URI.encode_www_form
  end

  defp prune([?&|t]), do: t
  defp prune([]), do: []
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






















































































































































































































































































































































































































































Deleted deps/plug/lib/plug/conn/status.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
defmodule Plug.Conn.Status do
  @moduledoc """
  Conveniences for working with status codes.
  """

  statuses = [
    continue: 100,
    switching_protocols: 101,
    processing: 102,
    ok: 200,
    created: 201,
    accepted: 202,
    non_authoritative_information: 203,
    no_content: 204,
    reset_content: 205,
    partial_content: 206,
    multi_status: 207,
    already_reported: 208,
    instance_manipulation_used: 226,
    multiple_choices: 300,
    moved_permanently: 301,
    found: 302,
    see_other: 303,
    not_modified: 304,
    use_proxy: 305,
    reserved: 306,
    temporary_redirect: 307,
    permanent_redirect: 308,
    bad_request: 400,
    unauthorized: 401,
    payment_required: 402,
    forbidden: 403,
    not_found: 404,
    method_not_allowed: 405,
    not_acceptable: 406,
    proxy_authentication_required: 407,
    request_timeout: 408,
    conflict: 409,
    gone: 410,
    length_required: 411,
    precondition_failed: 412,
    request_entity_too_large: 413,
    request_uri_too_long: 414,
    unsupported_media_type: 415,
    requested_range_not_satisfiable: 416,
    expectation_failed: 417,
    im_a_teapot: 418,
    misdirected_request: 421,
    unprocessable_entity: 422,
    locked: 423,
    failed_dependency: 424,
    upgrade_required: 426,
    precondition_required: 428,
    too_many_requests: 429,
    request_header_fields_too_large: 431,
    internal_server_error: 500,
    not_implemented: 501,
    bad_gateway: 502,
    service_unavailable: 503,
    gateway_timeout: 504,
    http_version_not_supported: 505,
    variant_also_negotiates: 506,
    insufficient_storage: 507,
    loop_detected: 508,
    not_extended: 510,
    network_authentication_required: 511
  ]

  doc = Enum.map(statuses, fn {atom, code} ->
    "  * `#{inspect atom}` - #{code}\n"
  end)

  @doc """
  Returns the status code given an integer or a known atom.

  ## Known status codes

  The following status codes can be given as atoms with their
  respective value shown next:

  #{doc}
  """
  @spec code(integer | atom) :: integer
  def code(integer_or_atom)

  def code(integer) when integer in 100..999 do
    integer
  end

  for {atom, code} <- statuses do
    def code(unquote(atom)), do: unquote(code)
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


























































































































































































Deleted deps/plug/lib/plug/conn/unfetched.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
defmodule Plug.Conn.Unfetched do
  @moduledoc """
  A struct used as default on unfetched fields.

  The `:aspect` key of the struct specifies what field is still unfetched.

  ## Examples

      unfetched = %Plug.Conn.Unfetched{aspect: :cookies}

  """

  defstruct [:aspect]
  @type t :: %__MODULE__{aspect: atom()}

  @doc false
  def get(%{aspect: aspect}, key, _value) do
    raise ArgumentError, "cannot get key #{inspect key} from conn.#{aspect} because they were not fetched"
  end

  @doc false
  def get_and_update(%{aspect: aspect}, key, _fun) do
    raise ArgumentError, "cannot get_and_update key #{inspect key} from conn.#{aspect} because they were not fetched"
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


















































Deleted deps/plug/lib/plug/conn/utils.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
defmodule Plug.Conn.Utils do
  @moduledoc """
  Utilities for working with connection data
  """

  @type params :: [{binary, binary}]

  @upper ?A..?Z
  @lower ?a..?z
  @alpha ?0..?9
  @other [?., ?-, ?+]
  @space [?\s, ?\t]
  @specials ~c|()<>@,;:\\"/[]?={}|

  @doc ~S"""
  Parses media types (with wildcards).

  Type and subtype are case insensitive while the
  sensitiveness of params depends on their keys and
  therefore are not handled by this parser.

  Returns:

    * `{:ok, type, subtype, map_of_params}` if everything goes fine
    * `:error` if the media type isn't valid

  ## Examples

      iex> media_type "text/plain"
      {:ok, "text", "plain", %{}}

      iex> media_type "APPLICATION/vnd.ms-data+XML"
      {:ok, "application", "vnd.ms-data+xml", %{}}

      iex> media_type "text/*; q=1.0"
      {:ok, "text", "*", %{"q" => "1.0"}}

      iex> media_type "*/*; q=1.0"
      {:ok, "*", "*", %{"q" => "1.0"}}

      iex> media_type "x y"
      :error

      iex> media_type "*/html"
      :error

      iex> media_type "/"
      :error

      iex> media_type "x/y z"
      :error

  """
  @spec media_type(binary) :: {:ok, type :: binary, subtype :: binary, params} | :error
  def media_type(binary) do
    case strip_spaces(binary) do
      "*/*" <> t -> mt_params(t, "*", "*")
      t -> mt_first(t, "")
    end
  end

  defp mt_first(<<?/, t :: binary>>, acc) when acc != "",
    do: mt_wildcard(t, acc)
  defp mt_first(<<h, t :: binary>>, acc) when h in @upper,
    do: mt_first(t, <<acc :: binary, downcase_char(h)>>)
  defp mt_first(<<h, t :: binary>>, acc) when h in @lower or h in @alpha or h == ?-,
    do: mt_first(t, <<acc :: binary, h>>)
  defp mt_first(_, _acc),
    do: :error

  defp mt_wildcard(<<?*, t :: binary>>, first),
    do: mt_params(t, first, "*")
  defp mt_wildcard(t, first),
    do: mt_second(t, "", first)

  defp mt_second(<<h, t :: binary>>, acc, first) when h in @upper,
    do: mt_second(t, <<acc :: binary, downcase_char(h)>>, first)
  defp mt_second(<<h, t :: binary>>, acc, first) when h in @lower or h in @alpha or h in @other,
    do: mt_second(t, <<acc :: binary, h>>, first)
  defp mt_second(t, acc, first),
    do: mt_params(t, first, acc)

  defp mt_params(t, first, second) do
    case strip_spaces(t) do
      ""       -> {:ok, first, second, %{}}
      ";" <> t -> {:ok, first, second, params(t)}
      _        -> :error
    end
  end

  @doc ~S"""
  Parses content type (without wildcards).

  It is similar to `media_type/1` except wildcards are
  not accepted in the type nor in the subtype.

  ## Examples

      iex> content_type "x-sample/json; charset=utf-8"
      {:ok, "x-sample", "json", %{"charset" => "utf-8"}}

      iex> content_type "x-sample/json  ; charset=utf-8  ; foo=bar"
      {:ok, "x-sample", "json", %{"charset" => "utf-8", "foo" => "bar"}}

      iex> content_type "\r\n text/plain;\r\n charset=utf-8\r\n"
      {:ok, "text", "plain", %{"charset" => "utf-8"}}

      iex> content_type "text/plain"
      {:ok, "text", "plain", %{}}

      iex> content_type "x/*"
      :error

      iex> content_type "*/*"
      :error

  """
  @spec content_type(binary) :: {:ok, type :: binary, subtype :: binary, params} | :error
  def content_type(binary) do
    case media_type(binary) do
      {:ok, _, "*", _}    -> :error
      {:ok, _, _, _} = ok -> ok
      :error              -> :error
    end
  end

  @doc """
  Parses headers parameters.

  Keys are case insensitive and downcased,
  invalid key-value pairs are discarded.

  ## Examples

      iex> params("foo=bar")
      %{"foo" => "bar"}

      iex> params("  foo=bar  ")
      %{"foo" => "bar"}

      iex> params("FOO=bar")
      %{"foo" => "bar"}

      iex> params("Foo=bar; baz=BOING")
      %{"foo" => "bar", "baz" => "BOING"}

      iex> params("foo=BAR ; wat")
      %{"foo" => "BAR"}

      iex> params("=")
      %{}

  """
  @spec params(binary) :: params
  def params(t) do
    t
    |> :binary.split(";", [:global])
    |> Enum.reduce(%{}, &params/2)
  end

  defp params(param, acc) do
    case params_key(strip_spaces(param), "") do
      {k, v} -> Map.put(acc, k, v)
      false  -> acc
    end
  end

  defp params_key(<<?=, t :: binary>>, acc) when acc != "",
    do: params_value(t, acc)
  defp params_key(<<h, _ :: binary>>, _acc) when h in @specials or h in @space or h < 32 or h === 127,
    do: false
  defp params_key(<<h, t :: binary>>, acc),
    do: params_key(t, <<acc :: binary, downcase_char(h)>>)
  defp params_key(<<>>, _acc),
    do: false

  defp params_value(token, key) do
    case token(token) do
      false -> false
      value -> {key, value}
    end
  end

  @doc ~S"""
  Parses a value as defined in [RFC-1341][1].
  For convenience, trims whitespace at the end of the token.
  Returns `false` if the token is invalid.

  [1]: http://www.w3.org/Protocols/rfc1341/4_Content-Type.html

  ## Examples

      iex> token("foo")
      "foo"

      iex> token("foo-bar")
      "foo-bar"

      iex> token("<foo>")
      false

      iex> token(~s["<foo>"])
      "<foo>"

      iex> token(~S["<f\oo>\"<b\ar>"])
      "<foo>\"<bar>"

      iex> token("foo  ")
      "foo"

      iex> token("foo bar")
      false

  """
  @spec token(binary) :: binary | false
  def token(""),
    do: false
  def token(<<?", quoted :: binary>>),
    do: quoted_token(quoted, "")
  def token(token),
    do: unquoted_token(token, "")

  defp quoted_token(<<>>, _acc),
    do: false
  defp quoted_token(<<?", t :: binary>>, acc),
    do: strip_spaces(t) == "" and acc
  defp quoted_token(<<?\\, h, t :: binary>>, acc),
    do: quoted_token(t, <<acc :: binary, h>>)
  defp quoted_token(<<h, t :: binary>>, acc),
    do: quoted_token(t, <<acc :: binary, h>>)

  defp unquoted_token(<<>>, acc),
    do: acc
  defp unquoted_token("\r\n" <> t, acc),
    do: strip_spaces(t) == "" and acc
  defp unquoted_token(<<h, t :: binary>>, acc) when h in @space,
    do: strip_spaces(t) == "" and acc
  defp unquoted_token(<<h, _ :: binary>>, _acc) when h in @specials or h < 32 or h === 127,
    do: false
  defp unquoted_token(<<h, t :: binary>>, acc),
    do: unquoted_token(t, <<acc :: binary, h>>)

  @doc """
  Parses a comma-separated list of header values.

  ## Examples

      iex> list("foo, bar")
      ["foo", "bar"]

      iex> list("foobar")
      ["foobar"]

      iex> list("")
      []

      iex> list("empties, , are,, filtered")
      ["empties", "are", "filtered"]

  """
  @spec list(binary) :: [binary]
  def list(binary) do
    for elem <- :binary.split(binary, ",", [:global]),
      (stripped = strip_spaces(elem)) != "",
      do: stripped
  end

  @doc """
  Validates the given binary is valid UTF-8.
  """
  @spec validate_utf8!(binary, binary) :: :ok | no_return
  def validate_utf8!(<<_ :: utf8, t :: binary>>, context) do
    validate_utf8!(t, context)
  end

  def validate_utf8!(<<h, _ :: binary>>, context) do
    raise Plug.Parsers.BadEncodingError,
      message: "invalid UTF-8 on #{context}, got byte #{h}"
  end

  def validate_utf8!(<<>>, _context) do
    :ok
  end

  ## Helpers

  defp strip_spaces("\r\n" <> t),
    do: strip_spaces(t)
  defp strip_spaces(<<h, t :: binary>>) when h in [?\s, ?\t],
    do: strip_spaces(t)
  defp strip_spaces(t),
    do: t

  defp downcase_char(char) when char in @upper, do: char + 32
  defp downcase_char(char), do: char
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
















































































































































































































































































































































































































































































































































































































Deleted deps/plug/lib/plug/conn/wrapper_error.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
defmodule Plug.Conn.WrapperError do
  @moduledoc """
  Wraps the connection in an error which is meant
  to be handled upper in the stack.

  Used by both `Plug.Debugger` and `Plug.ErrorHandler`.
  """
  defexception [:conn, :kind, :reason, :stack]

  def message(%{kind: kind, reason: reason, stack: stack}) do
    Exception.format_banner(kind, reason, stack)
  end

  @doc """
  Reraises an error or a wrapped one.
  """
  def reraise(_conn, :error, %__MODULE__{stack: stack} = reason) do
    :erlang.raise(:error, reason, stack)
  end

  def reraise(conn, kind, reason) do
    stack   = System.stacktrace
    wrapper = %__MODULE__{conn: conn, kind: kind, reason: reason, stack: stack}
    :erlang.raise(:error, wrapper, stack)
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




















































Deleted deps/plug/lib/plug/crypto.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
defmodule Plug.Crypto do
  @moduledoc """
  Namespace and module for crypto functionality.
  """

  use Bitwise

  @doc """
  Masks the token on the left with the token on the right.

  Both tokens are required to have the same size.
  """
  def mask(left, right) do
    mask(left, right, "")
  end

  defp mask(<<x, left::binary>>, <<y, right::binary>>, acc) do
    mask(left, right, <<acc::binary, x ^^^ y>>)
  end

  defp mask(<<>>, <<>>, acc) do
    acc
  end

  @doc """
  Compares the two binaries (one being masked) in constant-time to avoid
  timing attacks.

  It is assumed the right token is masked according to the given mask.
  """
  def masked_compare(left, right, mask) do
    if byte_size(left) == byte_size(right) do
      masked_compare(left, right, mask, 0) == 0
    else
      false
    end
  end

  defp masked_compare(<<x, left::binary>>, <<y, right::binary>>, <<z, mask::binary>>, acc) do
    masked_compare(left, right, mask, acc ||| (x ^^^ (y ^^^ z)))
  end

  defp masked_compare(<<>>, <<>>, <<>>, acc) do
    acc
  end

  @doc """
  Compares the two binaries in constant-time to avoid timing attacks.

  See: http://codahale.com/a-lesson-in-timing-attacks/
  """
  def secure_compare(left, right) do
    if byte_size(left) == byte_size(right) do
      secure_compare(left, right, 0) == 0
    else
      false
    end
  end

  defp secure_compare(<<x, left :: binary>>, <<y, right :: binary>>, acc) do
    secure_compare(left, right, acc ||| (x ^^^ y))
  end

  defp secure_compare(<<>>, <<>>, acc) do
    acc
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






































































































































Deleted deps/plug/lib/plug/crypto/key_generator.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
defmodule Plug.Crypto.KeyGenerator do
  @moduledoc """
  `KeyGenerator` implements PBKDF2 (Password-Based Key Derivation Function 2),
  part of PKCS #5 v2.0 (Password-Based Cryptography Specification).

  It can be used to derive a number of keys for various purposes from a given
  secret. This lets applications have a single secure secret, but avoid reusing
  that key in multiple incompatible contexts.

  see http://tools.ietf.org/html/rfc2898#section-5.2
  """

  use Bitwise
  @max_length bsl(1, 32) - 1

  @doc """
  Returns a derived key suitable for use.

  ## Options

    * `:iterations` - defaults to 1000 (increase to at least 2^16 if used for passwords);
    * `:length`     - a length in octets for the derived key. Defaults to 32;
    * `:digest`     - an hmac function to use as the pseudo-random function. Defaults to `:sha256`;
    * `:cache`      - an ETS table name to be used as cache.
      Only use an ETS table as cache if the secret and salt is a bound set of values.
      For example: `:ets.new(:your_name, [:named_table, :public, read_concurrency: true])`

  """
  def generate(secret, salt, opts \\ []) do
    iterations = Keyword.get(opts, :iterations, 1000)
    length = Keyword.get(opts, :length, 32)
    digest = Keyword.get(opts, :digest, :sha256)
    cache  = Keyword.get(opts, :cache)

    if length > @max_length do
      raise ArgumentError, "length must be less than or equal to #{@max_length}"
    else
      with_cache cache, {secret, salt, iterations, length, digest}, fn ->
        generate(mac_fun(digest, secret), salt, iterations, length, 1, [], 0)
      end
    end
  end

  defp with_cache(nil, _key, fun), do: fun.()
  defp with_cache(ets, key, fun) do
    case :ets.lookup(ets, key) do
      [{_key, value}] ->
        value
      [] ->
        value = fun.()
        :ets.insert(ets, [{key, value}])
        value
    end
  end

  defp generate(_fun, _salt, _iterations, max_length, _block_index, acc, length)
      when length >= max_length do
    key = acc |> Enum.reverse |> IO.iodata_to_binary
    <<bin::binary-size(max_length), _::binary>> = key
    bin
  end

  defp generate(fun, salt, iterations, max_length, block_index, acc, length) do
    initial = fun.(<<salt::binary, block_index::integer-size(32)>>)
    block   = iterate(fun, iterations - 1, initial, initial)
    generate(fun, salt, iterations, max_length, block_index + 1,
             [block | acc], byte_size(block) + length)
  end

  defp iterate(_fun, 0, _prev, acc), do: acc

  defp iterate(fun, iteration, prev, acc) do
    next = fun.(prev)
    iterate(fun, iteration - 1, next, :crypto.exor(next, acc))
  end

  defp mac_fun(digest, secret) do
    &:crypto.hmac(digest, secret, &1)
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
































































































































































Deleted deps/plug/lib/plug/crypto/message_encryptor.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
defmodule Plug.Crypto.MessageEncryptor do
  @moduledoc ~S"""
  `MessageEncryptor` is a simple way to encrypt values which get stored
  somewhere you don't trust.

  The cipher text and initialization vector are base64 encoded and
  returned to you.

  This can be used in situations similar to the `MessageVerifier`, but where
  you don't want users to be able to determine the value of the payload.

  ## Example

      secret_key_base = "072d1e0157c008193fe48a670cce031faa4e..."
      encrypted_cookie_salt = "encrypted cookie"
      encrypted_signed_cookie_salt = "signed encrypted cookie"

      secret = KeyGenerator.generate(secret_key_base, encrypted_cookie_salt)
      sign_secret = KeyGenerator.generate(secret_key_base, encrypted_signed_cookie_salt)

      data = "José"
      encrypted = MessageEncryptor.encrypt_and_sign(data, secret, sign_secret)
      decrypted = MessageEncryptor.verify_and_decrypt(encrypted, secret, sign_secret)
      decrypted # => "José"
  """

  alias Plug.Crypto.MessageVerifier

  @doc """
  Encrypts and signs a message.
  """
  def encrypt_and_sign(message, secret, sign_secret, cipher \\ :aes_cbc256)
      when is_binary(message) and is_binary(secret) and is_binary(sign_secret) do
    iv = :crypto.strong_rand_bytes(16)

    message
    |> pad_message
    |> encrypt(cipher, secret, iv)
    |> Base.encode64()
    |> Kernel.<>("--#{Base.encode64(iv)}")
    |> MessageVerifier.sign(sign_secret)
  end

  @doc """
  Decrypts and verifies a message.

  We need to verify the message in order to avoid padding attacks.
  Reference: http://www.limited-entropy.com/padding-oracle-attacks
  """
  def verify_and_decrypt(encrypted, secret, sign_secret, cipher \\ :aes_cbc256)
      when is_binary(encrypted) and is_binary(secret) and is_binary(sign_secret) do
    case MessageVerifier.verify(encrypted, sign_secret) do
      {:ok, verified} ->
        [encrypted, iv] = String.split(verified, "--")
        case Base.decode64(encrypted) do
          {:ok, encrypted} ->
            case Base.decode64(iv) do
              {:ok, iv} ->
                encrypted |> decrypt(cipher, secret, iv) |> unpad_message
              :error ->
                :error
            end
          :error ->
            :error
        end
      :error ->
        :error
    end
  end

  defp encrypt(message, cipher, secret, iv) do
    :crypto.block_encrypt(cipher, trim_secret(secret), iv, message)
  end

  defp decrypt(encrypted, cipher, secret, iv) do
    :crypto.block_decrypt(cipher, trim_secret(secret), iv, encrypted)
  end

  defp pad_message(msg) do
    bytes_remaining = rem(byte_size(msg), 16)
    padding_size = 16 - bytes_remaining
    msg <> :binary.copy(<<padding_size>>, padding_size)
  end

  defp unpad_message(msg) do
    padding_size = :binary.last(msg)
    if padding_size <= 16 do
      msg_size = byte_size(msg)
      if binary_part(msg, msg_size, -padding_size) == :binary.copy(<<padding_size>>, padding_size) do
        {:ok, binary_part(msg, 0, msg_size - padding_size)}
      else
        :error
      end
    else
      :error
    end
  end

  defp trim_secret(secret) do
    case byte_size(secret) do
      large when large > 32 -> :binary.part(secret, 0, 32)
      _ -> secret
    end
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


















































































































































































































Deleted deps/plug/lib/plug/crypto/message_verifier.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
defmodule Plug.Crypto.MessageVerifier do
  @moduledoc """
  `MessageVerifier` makes it easy to generate and verify messages
  which are signed to prevent tampering.

  For example, the cookie store uses this verifier to send data
  to the client. The data can be read by the client, but cannot be
  tampered with.
  """

  @doc """
  Decodes and verifies the encoded binary was not tampared with.
  """
  def verify(binary, secret) when is_binary(binary) and is_binary(secret) do
    case String.split(binary, "--") do
      [content, digest] when content != "" and digest != "" ->
        if Plug.Crypto.secure_compare(digest(secret, content), digest) do
          Base.decode64(content)
        else
          :error
        end
      _ ->
        :error
    end
  end

  @doc """
  Signs a binary according to the given secret.
  """
  def sign(binary, secret) when is_binary(binary) and is_binary(secret) do
    encoded = Base.encode64(binary)
    encoded <> "--" <> digest(secret, encoded)
  end

  defp digest(secret, data) do
    :crypto.hmac(:sha, secret, data) |> Base.encode64
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<












































































Deleted deps/plug/lib/plug/csrf_protection.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
defmodule Plug.CSRFProtection do
  @moduledoc """
  Plug to protect from cross-site request forgery.

  For this plug to work, it expects a session to have been
  previously fetched. It will then compare the plug stored
  in the session with the one sent by the request to determine
  the validity of the request. For an invaid request the action
  taken is based on the `:with` option.

  The token may be sent by the request either via the params
  with key "_csrf_token" or a header with name "x-csrf-token".

  GET requests are not protected, as they should not have any
  side-effect or change your application state. JavaScript
  requests are an exception: by using a script tag, external
  websites can embed server-side generated JavaScript, which
  can leak information. For this reason, this plug also forbids
  any GET JavaScript request that is not XHR (or AJAX).

  ## Token generation

  This plug won't generate tokens automatically. Instead,
  tokens will be generated only when required by calling
  `Plug.CSRFProtection.get_csrf_token/0`. The token is then
  stored in the process dictionary to be set in the request.

  One may wonder: why the process dictionary?

  The CSRF token is usually generated inside forms which may
  be isolated from the connection. Storing them in the process
  dictionary allows them to be generated as a side-effect,
  becoming one of those rare situations where using the process
  dictionary is useful.

  ## Options

    * `:with` - should be one of `:exception` or `:clear_session`. Defaults to
    `:exception`.
      * `:exception` -  for invalid requests, this plug will raise
      `Plug.CSRFProtection.InvalidCSRFTokenError`.
      * `:clear_session` -  for invalid requests, this plug will set an empty
      session for only this request. Also any changes to the session during this
      request will be ignored.

  ## Disabling

  You may disable this plug by doing
  `Plug.Conn.put_private(:plug_skip_csrf_protection, true)`. This was made
  available for disabling `Plug.CSRFProtection` in tests and not for dynamically
  skipping `Plug.CSRFProtection` in production code. If you want specific routes to
  skip `Plug.CSRFProtection`, then use a different stack of plugs for that route that
  does not include `Plug.CSRFProtection`.

  ## Examples

      plug Plug.Session, ...
      plug :fetch_session
      plug Plug.CSRFProtection

  """

  import Plug.Conn
  require Bitwise
  @unprotected_methods ~w(HEAD GET OPTIONS)

  defmodule InvalidCSRFTokenError do
    @moduledoc "Error raised when CSRF token is invalid."

    message =
      "invalid CSRF (Cross Site Forgery Protection) token, make sure all "
      <> "requests include a '_csrf_token' param or an 'x-csrf-token' header"

    defexception message: message, plug_status: 403
  end

  defmodule InvalidCrossOriginRequestError do
    @moduledoc "Error raised when non-XHR requests are used for Javascript responses."

    message =
      "security warning: an embedded <script> tag on another site requested "
      <> "protected JavaScript (if you know what you're doing, disable forgery "
      <> "protection for this route)"

    defexception message: message, plug_status: 403
  end

  ## API

  @doc """
  Gets the CSRF token.

  Generates a token and stores it in the process
  dictionary if one does not exists.
  """
  def get_csrf_token do
    if token = Process.get(:plug_masked_csrf_token) do
      token
    else
      token = mask(unmasked_csrf_token())
      Process.put(:plug_masked_csrf_token, token)
      token
    end
  end

  @doc """
  Deletes the CSRF token from the process dictionary.

  This will force the token to be deleted once the response is sent.
  """
  def delete_csrf_token do
    Process.delete(:plug_unmasked_csrf_token)
    Process.delete(:plug_masked_csrf_token)
  end

  ## Plug

  @behaviour Plug
  @token_size 16
  @encoded_token_size 24
  @double_encoded_token_size 32

  def init(opts), do: opts

  def call(conn, opts) do
    csrf_token = get_csrf_from_session(conn)
    Process.put(:plug_unmasked_csrf_token, csrf_token)

    if not verified_request?(conn, csrf_token) do
      conn = case Keyword.get(opts, :with, :exception) do
        :exception ->
          raise InvalidCSRFTokenError
        :clear_session ->
          conn |> configure_session(ignore: true) |> clear_session()
        other ->
          raise ArgumentError, "option :with should be one of :exception or :clear_session, got #{inspect other}"
      end
    end

    register_before_send(conn, &ensure_same_origin_and_csrf_token!(&1, csrf_token))
  end

  ## Verification

  defp get_csrf_from_session(conn) do
    if (csrf_token = get_session(conn, "_csrf_token")) &&
       byte_size(csrf_token) == @encoded_token_size do
      csrf_token
    end
  end

  defp verified_request?(conn, csrf_token) do
    conn.method in @unprotected_methods
      || valid_csrf_token?(csrf_token, conn.params["_csrf_token"])
      || valid_csrf_token?(csrf_token, get_req_header(conn, "x-csrf-token") |> List.first)
      || skip_csrf_protection?(conn)
  end

  defp valid_csrf_token?(<<csrf_token::@encoded_token_size-binary>>,
                         <<user_token::@double_encoded_token_size-binary, mask::@encoded_token_size-binary>>) do
    case Base.decode64(user_token) do
      {:ok, user_token} -> Plug.Crypto.masked_compare(csrf_token, user_token, mask)
      :error -> false
    end
  end

  defp valid_csrf_token?(_csrf_token, _user_token), do: false

  ## Before send

  defp ensure_same_origin_and_csrf_token!(conn, csrf_token) do
    if cross_origin_js?(conn) do
      raise InvalidCrossOriginRequestError
    end

    ensure_csrf_token(conn, csrf_token)
  end

  defp cross_origin_js?(%Plug.Conn{method: "GET"} = conn),
    do: not skip_csrf_protection?(conn) and not xhr?(conn) and js_content_type?(conn)
  defp cross_origin_js?(%Plug.Conn{}),
    do: false

  defp js_content_type?(conn) do
    conn
    |> get_resp_header("content-type")
    |> Enum.any?(&String.starts_with?(&1, ~w(text/javascript application/javascript)))
  end

  defp xhr?(conn) do
    "XMLHttpRequest" in get_req_header(conn, "x-requested-with")
  end

  defp ensure_csrf_token(conn, csrf_token) do
    Process.delete(:plug_masked_csrf_token)
    case Process.delete(:plug_unmasked_csrf_token) do
      ^csrf_token -> conn
      current     -> put_session(conn, "_csrf_token", current)
    end
  end

  ## Helpers

  defp skip_csrf_protection?(%Plug.Conn{private: %{plug_skip_csrf_protection: true}}), do: true
  defp skip_csrf_protection?(%Plug.Conn{}), do: false

  defp mask(token) do
    mask = generate_token()
    Base.encode64(Plug.Crypto.mask(token, mask)) <> mask
  end

  defp unmasked_csrf_token do
    if token = Process.get(:plug_unmasked_csrf_token) do
      token
    else
      token = generate_token()
      Process.put(:plug_unmasked_csrf_token, token)
      token
    end
  end

  defp generate_token do
    :crypto.strong_rand_bytes(@token_size) |> Base.encode64
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


































































































































































































































































































































































































































































Deleted deps/plug/lib/plug/debugger.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
# The debugger is based on Better Errors, under MIT LICENSE.
#
# Copyright (c) 2012 Charlie Somerville
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

defmodule Plug.Debugger do
  @moduledoc """
  A module (**not a plug**) for debugging in development.

  This module is commonly used within a `Plug.Builder` or a `Plug.Router`
  and it wraps the `call/2` function.

  Notice `Plug.Debugger` *does not* catch errors, as errors should still
  propagate so that the Elixir process finishes with the proper reason.
  This module does not perform any logging either, as all logging is done
  by the web server handler.

  **Note:** If this module is used with `Plug.ErrorHandler`, only one of
  them will effectively handle errors. For this reason, it is recommended
  that `Plug.Debugger` is used before `Plug.ErrorHandler` and only in
  particular environments, like `:dev`.

  ## Examples

      defmodule MyApp do
        use Plug.Builder

        if Mix.env == :dev do
          use Plug.Debugger, otp_app: :my_app
        end

        plug :boom

        def boom(conn, _) do
          # Error raised here will be caught and displayed in a debug page
          # complete with a stacktrace and other helpful info.
          raise "oops"
        end
      end

  ## Options

    * `:otp_app` - same as in `wrap/3`

  ## Links to the text editor

  If a `PLUG_EDITOR` environment variable is set, `Plug.Debugger` will
  use it to generate links to your text editor. The variable should be
  set with `__FILE__` and `__LINE__` placeholders which will be correctly
  replaced. For example (with the [TextMate](http://macromates.com) editor):

      txmt://open/?url=file://__FILE__&line=__LINE__

  """

  @already_sent {:plug_conn, :sent}
  import Plug.Conn

  @doc false
  defmacro __using__(opts) do
    quote do
      @plug_debugger unquote(opts)
      @before_compile Plug.Debugger
    end
  end

  @doc false
  defmacro __before_compile__(_) do
    quote location: :keep do
      defoverridable [call: 2]

      def call(conn, opts) do
        try do
          super(conn, opts)
        catch
          kind, reason ->
            Plug.Debugger.__catch__(conn, kind, reason, @plug_debugger)
        end
      end
    end
  end

  @doc false
  def __catch__(_conn, :error, %Plug.Conn.WrapperError{} = wrapper, opts) do
    %{conn: conn, kind: kind, reason: reason, stack: stack} = wrapper
    __catch__(conn, kind, reason, stack, opts)
  end

  def __catch__(conn, kind, reason, opts) do
    __catch__(conn, kind, reason, System.stacktrace, opts)
  end

  defp __catch__(conn, kind, reason, stack, opts) do
    receive do
      @already_sent ->
        send self(), @already_sent
        :erlang.raise kind, reason, stack
    after
      0 ->
        render conn, kind, reason, stack, opts
        :erlang.raise kind, reason, stack
    end
  end

  ## Rendering

  require EEx
  EEx.function_from_file :defp, :template, "lib/plug/templates/debugger.eex", [:assigns]

  # Made public with @doc false for testing.
  @doc false
  def render(conn, kind, reason, stack, opts) do
    session = maybe_fetch_session(conn)
    params  = maybe_fetch_query_params(conn)

    reason = Exception.normalize(kind, reason, stack)
    {status, title, message} = info(kind, reason)

    conn = put_resp_content_type(conn, "text/html")
    send_resp conn, status, template(conn: conn, frames: frames(stack, opts),
                                     title: title, message: message,
                                     session: session, params: params)
  end

  defp maybe_fetch_session(conn) do
    if conn.private[:plug_session_fetch] do
      fetch_session(conn).private[:plug_session]
    end
  end

  defp maybe_fetch_query_params(conn) do
    fetch_query_params(conn).params
  end

  defp info(:error, error) do
    {Plug.Exception.status(error), inspect(error.__struct__), Exception.message(error)}
  end

  defp info(:throw, thrown) do
    {500, "unhandled throw", inspect(thrown)}
  end

  defp info(:exit, reason) do
    {500, "unhandled exit", Exception.format_exit(reason)}
  end

  defp frames(stacktrace, opts) do
    app    = opts[:otp_app]
    editor = System.get_env("PLUG_EDITOR")

    stacktrace
    |> Enum.map_reduce(0, &each_frame(&1, &2, app, editor))
    |> elem(0)
  end

  defp each_frame(entry, index, root, editor) do
    {module, info, location, app, func, args} = get_entry(entry)
    {file, line} = {to_string(location[:file] || "nofile"), location[:line]}

    source  = get_source(module, file)
    context = get_context(root, app)
    snippet = get_snippet(source, line)

    {%{app: app,
       info: info,
       file: file,
       line: line,
       context: context,
       snippet: snippet,
       index: index,
       func: func,
       args: args,
       link: editor && get_editor(source, line, editor)
     }, index + 1}
  end

  # From :elixir_compiler_*
  defp get_entry({module, :__MODULE__, 0, location}) do
    {module, inspect(module) <> " (module)", location, get_app(module), nil, []}
  end

  # From :elixir_compiler_*
  defp get_entry({_module, :__MODULE__, 1, location}) do
    {nil, "(module)", location, nil, nil, []}
  end

  # From :elixir_compiler_*
  defp get_entry({_module, :__FILE__, 1, location}) do
    {nil, "(file)", location, nil, nil, []}
  end

  defp get_entry({module, fun, args, location}) when is_list(args) do
    {module, Exception.format_mfa(module, fun, length(args)), location, get_app(module), fun, args}
  end

  defp get_entry({module, fun, arity, location}) do
    {module, Exception.format_mfa(module, fun, arity), location, get_app(module), fun, []}
  end

  defp get_entry({fun, arity, location}) do
    {nil, Exception.format_fa(fun, arity), location, nil, fun, []}
  end

  defp get_app(module) do
    case :application.get_application(module) do
      {:ok, app} -> app
      :undefined -> nil
    end
  end

  defp get_context(app, app) when app != nil, do: :app
  defp get_context(_app1, _app2),             do: :all

  defp get_source(module, file) do
    cond do
      File.regular?(file) ->
        file
      Code.ensure_loaded?(module) &&
        (source = module.module_info(:compile)[:source]) ->
        to_string(source)
      true ->
        file
    end
  end

  defp get_editor(file, line, editor) do
    editor
    |> :binary.replace("__FILE__", URI.encode(Path.expand(file)))
    |> :binary.replace("__LINE__", to_string(line))
    |> h
  end

  @radius 5

  defp get_snippet(file, line) do
    if File.regular?(file) and is_integer(line) do
      to_discard = max(line - @radius - 1, 0)
      lines = File.stream!(file) |> Stream.take(line + 5) |> Stream.drop(to_discard)

      {first_five, lines} = Enum.split(lines, line - to_discard - 1)
      first_five = with_line_number first_five, to_discard + 1, false

      {center, last_five} = Enum.split(lines, 1)
      center = with_line_number center, line, true
      last_five = with_line_number last_five, line + 1, false

      first_five ++ center ++ last_five
    end
  end

  defp with_line_number(lines, initial, highlight) do
    Enum.map_reduce(lines, initial, fn(line, acc) ->
      {{acc, line, highlight}, acc + 1}
    end) |> elem(0)
  end

  ## Helpers

  defp method(%Plug.Conn{method: method}), do:
    method

  defp url(%Plug.Conn{scheme: scheme, host: host, port: port} = conn), do:
    "#{scheme}://#{host}:#{port}#{conn.request_path}"

  defp peer(%Plug.Conn{peer: {host, port}}), do:
    "#{:inet_parse.ntoa host}:#{port}"

  defp h(string) do
    for <<code <- to_string(string)>> do
      << case code do
           ?& -> "&amp;"
           ?< -> "&lt;"
           ?> -> "&gt;"
           ?" -> "&quot;"
           _  -> <<code>>
         end :: binary >>
    end
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


















































































































































































































































































































































































































































































































































































































Deleted deps/plug/lib/plug/error_handler.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
defmodule Plug.ErrorHandler do
  @moduledoc """
  A module to be used in your existing plugs in order to provide
  error handling.

      defmodule AppRouter do
        use Plug.Router
        use Plug.ErrorHandler

        plug :match
        plug :dispatch

        get "/hello" do
          send_resp(conn, 200, "world")
        end

        defp handle_errors(conn, %{kind: _kind, reason: _reason, stack: _stack}) do
          send_resp(conn, conn.status, "Something went wrong")
        end
      end

  Once this module is used, a callback named `handle_errors/2` should
  be defined in your plug. This callback should accept a connection and a map
  containing:

    * the exception kind (`:throw`, `:error` or `:exit`),
    * the reason (an exception for errors or a term for others)
    * the stacktrace

  After the callback is invoked, the error is re-raised.

  It is advised to do as little work as possible when handling errors
  and avoid accessing data like parameters and session, as the parsing
  of those is what could have led the error to trigger in the first place.

  Also notice that these pages are going to be shown in production. If
  you are looking for error handling to help during development, consider
  using `Plug.Debugger`.

  **Note:** If this module is used with `Plug.Debugger`, it must be used
  after `Plug.Debugger`.
  """
  @doc false
  defmacro __using__(_) do
    quote location: :keep do
      @before_compile Plug.ErrorHandler

      defp handle_errors(conn, assigns) do
        Plug.Conn.send_resp(conn, conn.status, "Something went wrong")
      end

      defoverridable [handle_errors: 2]
    end
  end

  @doc false
  defmacro __before_compile__(_env) do
    quote location: :keep do
      defoverridable [call: 2]

      def call(conn, opts) do
        try do
          super(conn, opts)
        catch
          kind, reason ->
            Plug.ErrorHandler.__catch__(conn, kind, reason, &handle_errors/2)
        end
      end
    end
  end

  @already_sent {:plug_conn, :sent}

  @doc false
  def __catch__(_conn, :error, %Plug.Conn.WrapperError{} = wrapper, handle_errors) do
    %{conn: conn, kind: kind, reason: reason, stack: stack} = wrapper
    __catch__(conn, kind, reason, stack, handle_errors)
  end

  def __catch__(conn, kind, reason, handle_errors) do
    __catch__(conn, kind, reason, System.stacktrace, handle_errors)
  end

  defp __catch__(conn, kind, reason, stack, handle_errors) do
    receive do
      @already_sent ->
        send self(), @already_sent
    after
      0 ->
        reason = Exception.normalize(kind, reason, stack)

        conn
        |> Plug.Conn.put_status(status(kind, reason))
        |> handle_errors.(%{kind: kind, reason: reason, stack: stack})
    end

    :erlang.raise(kind, reason, stack)
  end

  defp status(:error, error),  do: Plug.Exception.status(error)
  defp status(:throw, _throw), do: 500
  defp status(:exit, _exit),   do: 500
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<














































































































































































































Deleted deps/plug/lib/plug/exceptions.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# This file defines the Plug.Exception protocol and
# the exceptions that implement such protocol.

defprotocol Plug.Exception do
  @moduledoc """
  A protocol that extends exceptions to be status-code aware.

  By default, it looks for an implementation of the protocol,
  otherwise checks if the exception has the `:plug_status` field
  or simply returns 500.
  """

  @fallback_to_any true

  @doc """
  Receives an exception and returns its HTTP status code.
  """
  @spec status(t) :: Plug.Conn.status
  def status(exception)
end

defimpl Plug.Exception, for: Any do
  def status(%{plug_status: status}) when is_integer(status), do: status
  def status(_), do: 500
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


















































Deleted deps/plug/lib/plug/head.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
defmodule Plug.Head do
  @moduledoc """
  A Plug to convert `HEAD` requests to `GET` requests.

  ## Examples

      Plug.Head.call(conn, [])
  """

  @behaviour Plug

  alias Plug.Conn

  def init([]), do: []

  def call(%Conn{method: "HEAD"} = conn, []), do: %{conn | method: "GET"}
  def call(conn, []), do: conn
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




































Deleted deps/plug/lib/plug/html.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
defmodule Plug.HTML do
  @moduledoc """
  Conveniences for generating HTML.
  """

  @doc ~S"""
  Escapes the given HTML.

      iex> Plug.HTML.html_escape("<foo>")
      "&lt;foo&gt;"

      iex> Plug.HTML.html_escape("quotes: \" & \'")
      "quotes: &quot; &amp; &#39;"
  """
  def html_escape(data) when is_binary(data) do
    IO.iodata_to_binary(for <<char <- data>>, do: escape_char(char))
  end

  @compile {:inline, escape_char: 1}

  @escapes [
    {?<, "&lt;"},
    {?>, "&gt;"},
    {?&, "&amp;"},
    {?", "&quot;"},
    {?', "&#39;"}
  ]

  Enum.each @escapes, fn { match, insert } ->
    defp escape_char(unquote(match)), do: unquote(insert)
  end

  defp escape_char(char), do: char
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




































































Deleted deps/plug/lib/plug/logger.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
defmodule Plug.Logger do
  @moduledoc """
  A plug for logging basic request information in the format:

      GET /index.html
      Sent 200 in 572ms

  To use it, just plug it into the desired module.

      plug Plug.Logger, log: :debug

  ## Options

    * `:log` - The log level at which this plug should log its request info.
      Default is `:info`.
  """

  require Logger
  alias Plug.Conn
  @behaviour Plug

  def init(opts) do
    Keyword.get(opts, :log, :info)
  end

  def call(conn, level) do
    Logger.log level, fn ->
      [conn.method, ?\s, conn.request_path]
    end

    before_time = :os.timestamp()

    Conn.register_before_send(conn, fn conn ->
      Logger.log level, fn ->
        after_time = :os.timestamp()
        diff = :timer.now_diff(after_time, before_time)
        [connection_type(conn), ?\s, Integer.to_string(conn.status),
         " in ", formatted_diff(diff)]
      end
      conn
    end)
  end

  defp formatted_diff(diff) when diff > 1000, do: [diff |> div(1000) |> Integer.to_string, "ms"]
  defp formatted_diff(diff), do: [diff |> Integer.to_string, "µs"]

  defp connection_type(%{state: :chunked}), do: "Chunked"
  defp connection_type(_), do: "Sent"
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


































































































Deleted deps/plug/lib/plug/method_override.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
defmodule Plug.MethodOverride do
  @moduledoc """
  This plug overrides the request's `POST` method with the method defined in
  the `_method` request parameter.

  The `POST` method can be overridden only by these HTTP methods:

    * `PUT`
    * `PATCH`
    * `DELETE`

  This plug expects the body parameters to be already parsed and
  fetched. Those can be fetched with `Plug.Parsers`.

  This plug doesn't accept any options.

  ## Examples

      Plug.MethodOverride.call(conn, [])
  """

  @behaviour Plug

  @allowed_methods ~w(DELETE PUT PATCH)

  def init([]), do: []

  def call(%Plug.Conn{method: "POST", body_params: body_params} = conn, []),
    do: override_method(conn, body_params)
  def call(%Plug.Conn{} = conn, []),
    do: conn

  defp override_method(conn, %Plug.Conn.Unfetched{}) do
    # Just skip it because maybe it is a content-type that
    # we could not parse as parameters (for example, text/gps)
    conn
  end

  defp override_method(conn, body_params) do
    method = (body_params["_method"] || "") |> String.upcase

    cond do
      method in @allowed_methods -> %{conn | method: method}
      true                       -> conn
    end
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






























































































Deleted deps/plug/lib/plug/mime.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
defmodule Plug.MIME do
  @moduledoc """
  Maps MIME types to file extensions and vice versa.

  MIME types can be extended in your application configuration
  as follows:

      config :plug, :mimes, %{
        "application/vnd.api+json" => ["json-api"]
      }

  After adding the configuration, Plug needs to be recompiled.
  If you are using mix, it can be done with:

      $ touch deps/plug/mix.exs
      $ mix deps.compile plug
  """

  @compile :no_native
  @default_type "application/octet-stream"

  # Read all the MIME type mappings into the `mapping` variable.
  @external_resource "lib/plug/mime.types"
  stream = File.stream!("lib/plug/mime.types")

  mapping = Enum.flat_map(stream, fn (line) ->
    if String.starts_with?(line, ["#", "\n"]) do
      []
    else
      [type|exts] = line |> String.strip |> String.split
      [{type, exts}]
    end
  end)

  app = Application.get_env(:plug, :mimes, %{})

  mapping = Enum.reduce app, mapping, fn {k, v}, acc ->
    List.keystore(acc, k, 0, {k, v})
  end

  @doc """
  Returns whether a MIME type is registered.

  ## Examples

      iex> Plug.MIME.valid?("text/plain")
      true

      iex> Plug.MIME.valid?("foo/bar")
      false

  """
  @spec valid?(String.t) :: boolean
  def valid?(type) do
    is_list entry(type)
  end

  @doc """
  Returns the extensions associated with a given MIME type.

  ## Examples

      iex> Plug.MIME.extensions("text/html")
      ["html", "htm"]

      iex> Plug.MIME.extensions("application/json")
      ["json"]

      iex> Plug.MIME.extensions("foo/bar")
      []

  """
  @spec extensions(String.t) :: [String.t]
  def extensions(type) do
    entry(type) || []
  end

  @doc """
  Returns the MIME type associated with a file extension. If no MIME type is
  known for `file_extension`, `#{inspect @default_type}` is returned.

  ## Examples

      iex> Plug.MIME.type("txt")
      "text/plain"

      iex> Plug.MIME.type("foobarbaz")
      #{inspect @default_type}

  """
  @spec type(String.t) :: String.t
  def type(file_extension)

  for {type, exts} <- mapping, ext <- exts do
    def type(unquote(ext)), do: unquote(type)
  end

  def type(_ext), do: @default_type

  @doc """
  Guesses the MIME type based on the path's extension. See `type/1`.

  ## Examples

      iex> Plug.MIME.path("index.html")
      "text/html"

  """
  @spec path(Path.t) :: String.t
  def path(path) do
    case Path.extname(path) do
      "." <> ext -> type(downcase(ext, ""))
      _ -> @default_type
    end
  end

  defp downcase(<<h, t::binary>>, acc) when h in ?A..?Z, do: downcase(t, <<acc::binary, h+32>>)
  defp downcase(<<h, t::binary>>, acc), do: downcase(t, <<acc::binary, h>>)
  defp downcase(<<>>, acc), do: acc

  # entry/1
  @spec entry(String.t) :: list(String.t)

  for {type, exts} <- mapping do
    defp entry(unquote(type)), do: unquote(exts)
  end

  defp entry(_type), do: nil
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


































































































































































































































































Deleted deps/plug/lib/plug/mime.types.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
# https://git.fedorahosted.org/cgit/mailcap.git/plain/mime.types

# This file controls what Internet media types are sent to the client for
# given file extension(s).  Sending the correct media type to the client
# is important so they know how to handle the content of the file.
# Extra types can either be added here or by using an AddType directive
# in your config files. For more information about Internet media types,
# please read RFC 2045, 2046, 2047, 2048, and 2077.  The Internet media type
# registry is at <http://www.iana.org/assignments/media-types/>.

# IANA types

# MIME type         Extensions
application/1d-interleaved-parityfec
application/3gpdash-qoe-report+xml
application/3gpp-ims+xml
application/A2L         a2l
application/activemessage
application/alto-costmap+json
application/alto-costmapfilter+json
application/alto-directory+json
application/alto-endpointcost+json
application/alto-endpointcostparams+json
application/alto-endpointprop+json
application/alto-endpointpropparams+json
application/alto-error+json
application/alto-networkmap+json
application/alto-networkmapfilter+json
application/AML         aml
application/andrew-inset      ez
application/applefile
application/ATF         atf
application/ATFX        atfx
application/ATXML       atxml
application/atom+xml        atom
application/atomcat+xml       atomcat
application/atomdeleted+xml     atomdeleted
application/atomicmail
application/atomsvc+xml       atomsvc
application/auth-policy+xml     apxml
application/bacnet-xdd+zip      xdd
application/batch-SMTP
application/beep+xml
application/calendar+json
application/calendar+xml      xcs
application/call-completion
application/cals-1840
application/cbor        cbor
application/ccmp+xml        ccmp
application/ccxml+xml       ccxml
application/CDFX+XML        cdfx
application/cdmi-capability     cdmia
application/cdmi-container      cdmic
application/cdmi-domain       cdmid
application/cdmi-object       cdmio
application/cdmi-queue        cdmiq
application/CEA         cea
application/cea-2018+xml
application/cellml+xml        cellml cml
application/cfw
application/cms         cmsc
application/cnrp+xml
application/coap-group+json
application/commonground
application/conference-info+xml
application/cpl+xml       cpl
application/csrattrs        csrattrs
application/csta+xml
application/CSTAdata+xml
application/cybercash
application/dash+xml        mpd
application/dashdelta       mpdd
application/davmount+xml      davmount
application/dca-rft
application/DCD         dcd
application/dec-dx
application/dialog-info+xml
application/dicom       dcm
application/DII         dii
application/DIT         dit
application/dns
application/dskpp+xml       xmls
application/dssc+der        dssc
application/dssc+xml        xdssc
application/dvcs        dvc
application/ecmascript        es
application/EDI-Consent
application/EDI-X12
application/EDIFACT
application/emma+xml        emma
application/emotionml+xml     emotionml
application/encaprtp
application/epp+xml
application/epub+zip        epub
application/eshop
application/exi         exi
application/fastinfoset       finf
application/fastsoap
application/fdt+xml       fdt
# fits, fit, fts: image/fits
application/fits
application/font-sfnt       ttf
application/font-tdpfr        pfr
application/font-woff       woff
application/framework-attributes+xml
application/gzip        gz tgz
application/H224
application/held+xml
application/http
application/hyperstudio       stk
application/ibe-key-request+xml
application/ibe-pkg-reply+xml
application/ibe-pp-data
application/iges
application/im-iscomposing+xml
application/index
application/index.cmd
application/index.obj
application/index.response
application/index.vnd
application/inkml+xml       ink inkml
application/iotp
application/ipfix       ipfix
application/ipp
application/isup
application/its+xml       its
application/javascript        js
application/jose
application/jose+json
application/jrd+json        jrd
application/json        json
application/json-patch+json     json-patch
application/json-seq
application/jwk+json
application/jwk-set+json
application/jwt
application/kpml-request+xml
application/kpml-response+xml
application/ld+json       jsonld
application/link-format       wlnk
application/load-control+xml
application/lost+xml        lostxml
application/lostsync+xml      lostsyncxml
application/LXF         lxf
application/mac-binhex40      hqx
application/macwriteii
application/mads+xml        mads
application/marc        mrc
application/marcxml+xml       mrcx
application/mathematica       nb ma mb
application/mathml-content+xml
application/mathml-presentation+xml
application/mathml+xml        mml
application/mbms-associated-procedure-description+xml
application/mbms-deregister+xml
application/mbms-envelope+xml
application/mbms-msk-response+xml
application/mbms-msk+xml
application/mbms-protection-description+xml
application/mbms-reception-report+xml
application/mbms-register-response+xml
application/mbms-register+xml
application/mbms-schedule+xml
application/mbms-user-service-description+xml
application/mbox        mbox
application/media_control+xml
# mpf: text/vnd.ms-mediapackage
application/media-policy-dataset+xml
application/mediaservercontrol+xml
application/merge-patch+json
application/metalink4+xml     meta4
application/mets+xml        mets
application/MF4         mf4
application/mikey
application/mods+xml        mods
application/moss-keys
application/moss-signature
application/mosskey-data
application/mosskey-request
application/mp21        m21 mp21
# mp4, mpg4: video/mp4, see RFC 4337
application/mp4
application/mpeg4-generic
application/mpeg4-iod
application/mpeg4-iod-xmt
# xdf: application/xcap-diff+xml
application/mrb-consumer+xml
application/mrb-publish+xml
application/msc-ivr+xml
application/msc-mixer+xml
application/msword        doc
application/mxf         mxf
application/nasdata
application/news-checkgroups
application/news-groupinfo
application/news-transmission
application/nlsml+xml
application/nss
application/ocsp-request      orq
application/ocsp-response     ors
application/octet-stream    bin lha lzh exe class so dll img iso
application/oda         oda
application/ODX         odx
application/oebps-package+xml     opf
application/ogg         ogx
application/oxps        oxps
application/p2p-overlay+xml     relo
application/parityfec
# xer: application/xcap-error+xml
application/patch-ops-error+xml
application/pdf         pdf
application/PDX         pdx
application/pgp-encrypted     pgp
application/pgp-keys
application/pgp-signature     sig
application/pidf-diff+xml
application/pidf+xml
application/pkcs10        p10
application/pkcs7-mime        p7m p7c
application/pkcs7-signature     p7s
application/pkcs8       p8
# ac: application/vnd.nokia.n-gage.ac+xml
application/pkix-attr-cert
application/pkix-cert       cer
application/pkix-crl        crl
application/pkix-pkipath      pkipath
application/pkixcmp       pki
application/pls+xml       pls
application/poc-settings+xml
application/postscript        ps eps ai
application/provenance+xml      provx
application/prs.alvestrand.titrax-sheet
application/prs.cww       cw cww
application/prs.hpub+zip      hpub
application/prs.nprend        rnd rct
application/prs.plucker
application/prs.rdf-xml-crypt     rdf-crypt
application/prs.xsf+xml       xsf
application/pskc+xml        pskcxml
application/qsig
application/raptorfec
application/rdap+json
application/rdf+xml       rdf
application/reginfo+xml       rif
application/relax-ng-compact-syntax   rnc
application/remote-printing
application/reputon+json
application/resource-lists-diff+xml   rld
application/resource-lists+xml      rl
application/riscos
application/rlmi+xml
application/rls-services+xml      rs
application/rpki-ghostbusters     gbr
application/rpki-manifest     mft
application/rpki-roa        roa
application/rpki-updown
application/rtf         rtf
application/rtploopback
application/rtx
application/samlassertion+xml
application/samlmetadata+xml
application/sbml+xml
application/scaip+xml
application/scvp-cv-request     scq
application/scvp-cv-response      scs
application/scvp-vp-request     spq
application/scvp-vp-response      spp
application/sdp         sdp
application/sep+xml
application/sep-exi
application/session-info
application/set-payment
application/set-payment-initiation
application/set-registration
application/set-registration-initiation
application/sgml
application/sgml-open-catalog     soc
application/shf+xml       shf
application/sieve       siv sieve
application/simple-filter+xml     cl
application/simple-message-summary
application/simpleSymbolContainer
application/slate
# application/smil obsoleted by application/smil+xml
application/smil+xml        smil smi sml
application/smpte336m
application/soap+fastinfoset
application/soap+xml
application/sparql-query      rq
application/sparql-results+xml      srx
application/spirits-event+xml
application/sql         sql
application/srgs        gram
application/srgs+xml        grxml
application/sru+xml       sru
application/ssml+xml        ssml
application/tamp-apex-update      tau
application/tamp-apex-update-confirm    auc
application/tamp-community-update   tcu
application/tamp-community-update-confirm cuc
application/tamp-error        ter
application/tamp-sequence-adjust    tsa
application/tamp-sequence-adjust-confirm  sac
# tsq: application/timestamp-query
application/tamp-status-query
# tsr: application/timestamp-reply
application/tamp-status-response
application/tamp-update       tur
application/tamp-update-confirm     tuc
application/tei+xml       tei teiCorpus odd
application/thraud+xml        tfi
application/timestamp-query     tsq
application/timestamp-reply     tsr
application/timestamped-data      tsd
application/ttml+xml        ttml
application/tve-trigger
application/ulpfec
application/urc-grpsheet+xml      gsheet
application/urc-ressheet+xml      rsheet
application/urc-targetdesc+xml      td
application/urc-uisocketdesc+xml    uis
application/vcard+json
application/vcard+xml
application/vemmi
application/vnd.3gpp.bsf+xml
application/vnd.3gpp.pic-bw-large   plb
application/vnd.3gpp.pic-bw-small   psb
application/vnd.3gpp.pic-bw-var     pvb
# sms: application/vnd.3gpp2.sms
application/vnd.3gpp.sms
application/vnd.3gpp2.bcmcsinfo+xml
application/vnd.3gpp2.sms     sms
application/vnd.3gpp2.tcap      tcap
application/vnd.3M.Post-it-Notes    pwn
application/vnd.accpac.simply.aso   aso
application/vnd.accpac.simply.imp   imp
application/vnd.acucobol      acu
application/vnd.acucorp       atc acutc
# swf: application/x-shockwave-flash for now
application/vnd.adobe.flash.movie
application/vnd.adobe.formscentral.fcdt   fcdt
application/vnd.adobe.fxp     fxp fxpl
application/vnd.adobe.partial-upload
application/vnd.adobe.xdp+xml     xdp
application/vnd.adobe.xfdf      xfdf
application/vnd.aether.imp
application/vnd.ah-barcode
application/vnd.ahead.space     ahead
application/vnd.airzip.filesecure.azf   azf
application/vnd.airzip.filesecure.azs   azs
application/vnd.americandynamics.acc    acc
application/vnd.amiga.ami     ami
application/vnd.amundsen.maze+xml
application/vnd.anser-web-certificate-issue-initiation  cii
# Not in IANA listing, but is on FTP site?
application/vnd.anser-web-funds-transfer-initiation fti
# atx: audio/ATRAC-X
application/vnd.antix.game-component
application/vnd.apache.thrift.binary
application/vnd.apache.thrift.compact
application/vnd.apache.thrift.json
application/vnd.api+json
application/vnd.apple.installer+xml   dist distz pkg mpkg
# m3u: audio/x-mpegurl for now
application/vnd.apple.mpegurl     m3u8
# application/vnd.arastra.swi obsoleted by application/vnd.aristanetworks.swi
application/vnd.aristanetworks.swi    swi
application/vnd.artsquare
application/vnd.astraea-software.iota   iota
application/vnd.audiograph      aep
application/vnd.autopackage     package
application/vnd.avistar+xml
application/vnd.balsamiq.bmml+xml   bmml
application/vnd.bekitzur-stech+json
application/vnd.blueice.multipass   mpm
application/vnd.bluetooth.ep.oob    ep
application/vnd.bluetooth.le.oob    le
application/vnd.bmi       bmi
application/vnd.businessobjects     rep
application/vnd.cab-jscript
application/vnd.canon-cpdl
application/vnd.canon-lips
application/vnd.cendio.thinlinc.clientconf  tlclient
application/vnd.century-systems.tcp_stream
application/vnd.chemdraw+xml      cdxml
application/vnd.chipnuts.karaoke-mmd    mmd
application/vnd.cinderella      cdy
application/vnd.cirpack.isdn-ext
application/vnd.citationstyles.style+xml  csl
application/vnd.claymore      cla
application/vnd.cloanto.rp9     rp9
application/vnd.clonk.c4group     c4g c4d c4f c4p c4u
application/vnd.cluetrust.cartomobile-config  c11amc
application/vnd.cluetrust.cartomobile-config-pkg  c11amz
application/vnd.coffeescript      coffee
application/vnd.collection+json
application/vnd.collection.doc+json
application/vnd.collection.next+json
# icc: application/vnd.iccprofile
application/vnd.commerce-battelle ica icf icd ic0 ic1 ic2 ic3 ic4 ic5 ic6 ic7 ic8
application/vnd.commonspace     csp cst
application/vnd.contact.cmsg      cdbcmsg
application/vnd.cosmocaller     cmc
application/vnd.crick.clicker     clkx
application/vnd.crick.clicker.keyboard    clkk
application/vnd.crick.clicker.palette   clkp
application/vnd.crick.clicker.template    clkt
application/vnd.crick.clicker.wordbank    clkw
application/vnd.criticaltools.wbs+xml   wbs
application/vnd.ctc-posml     pml
application/vnd.ctct.ws+xml
application/vnd.cups-pdf
application/vnd.cups-postscript
application/vnd.cups-ppd      ppd
application/vnd.cups-raster
application/vnd.cups-raw
application/vnd.curl        curl
application/vnd.cyan.dean.root+xml
application/vnd.cybank
application/vnd.dart        dart
application/vnd.data-vision.rdz     rdz
application/vnd.debian.binary-package   deb udeb
application/vnd.dece.data     uvf uvvf uvd uvvd
application/vnd.dece.ttml+xml     uvt uvvt
application/vnd.dece.unspecified    uvx uvvx
application/vnd.dece.zip      uvz uvvz
application/vnd.denovo.fcselayout-link    fe_launch
application/vnd.desmume.movie     dsm
application/vnd.dir-bi.plate-dl-nosuffix
application/vnd.dm.delegation+xml
application/vnd.dna       dna
application/vnd.document+json     docjson
application/vnd.dolby.mobile.1
application/vnd.dolby.mobile.2
application/vnd.doremir.scorecloud-binary-document  scld
application/vnd.dpgraph       dpg mwc dpgraph
application/vnd.dreamfactory      dfac
application/vnd.dtg.local
application/vnd.dtg.local.flash     fla
application/vnd.dtg.local.html
application/vnd.dvb.ait       ait
# class: application/octet-stream
application/vnd.dvb.dvbj
application/vnd.dvb.esgcontainer
application/vnd.dvb.ipdcdftnotifaccess
application/vnd.dvb.ipdcesgaccess
application/vnd.dvb.ipdcesgaccess2
application/vnd.dvb.ipdcesgpdd
application/vnd.dvb.ipdcroaming
application/vnd.dvb.iptv.alfec-base
application/vnd.dvb.iptv.alfec-enhancement
application/vnd.dvb.notif-aggregate-root+xml
application/vnd.dvb.notif-container+xml
application/vnd.dvb.notif-generic+xml
application/vnd.dvb.notif-ia-msglist+xml
application/vnd.dvb.notif-ia-registration-request+xml
application/vnd.dvb.notif-ia-registration-response+xml
application/vnd.dvb.notif-init+xml
# pfr: application/font-tdpfr
application/vnd.dvb.pfr
application/vnd.dvb.service     svc
# dxr: application/x-director
application/vnd.dxr
application/vnd.dynageo       geo
application/vnd.dzr       dzr
application/vnd.easykaraoke.cdgdownload
application/vnd.ecdis-update
application/vnd.ecowin.chart      mag
application/vnd.ecowin.filerequest
application/vnd.ecowin.fileupdate
application/vnd.ecowin.series
application/vnd.ecowin.seriesrequest
application/vnd.ecowin.seriesupdate
application/vnd.enliven       nml
application/vnd.enphase.envoy
application/vnd.eprints.data+xml
application/vnd.epson.esf     esf
application/vnd.epson.msf     msf
application/vnd.epson.quickanime    qam
application/vnd.epson.salt      slt
application/vnd.epson.ssf     ssf
application/vnd.ericsson.quickcall    qcall qca
application/vnd.eszigno3+xml      es3 et3
application/vnd.etsi.aoc+xml
application/vnd.etsi.asic-e+zip     asice sce
# scs: application/scvp-cv-response
application/vnd.etsi.asic-s+zip     asics
application/vnd.etsi.cug+xml
application/vnd.etsi.iptvcommand+xml
application/vnd.etsi.iptvdiscovery+xml
application/vnd.etsi.iptvprofile+xml
application/vnd.etsi.iptvsad-bc+xml
application/vnd.etsi.iptvsad-cod+xml
application/vnd.etsi.iptvsad-npvr+xml
application/vnd.etsi.iptvservice+xml
application/vnd.etsi.iptvsync+xml
application/vnd.etsi.iptvueprofile+xml
application/vnd.etsi.mcid+xml
application/vnd.etsi.mheg5
application/vnd.etsi.overload-control-policy-dataset+xml
application/vnd.etsi.pstn+xml
application/vnd.etsi.sci+xml
application/vnd.etsi.simservs+xml
application/vnd.etsi.timestamp-token    tst
application/vnd.etsi.tsl.der
application/vnd.etsi.tsl+xml
application/vnd.eudora.data
application/vnd.ezpix-album     ez2
application/vnd.ezpix-package     ez3
application/vnd.f-secure.mobile
application/vnd.fastcopy-disk-image   dim
application/vnd.fdf       fdf
application/vnd.fdsn.mseed      msd mseed
application/vnd.fdsn.seed     seed dataless
application/vnd.ffsns
# all extensions: application/vnd.hbci
application/vnd.fints
application/vnd.FloGraphIt      gph
application/vnd.fluxtime.clip     ftc
application/vnd.font-fontforge-sfd    sfd
application/vnd.framemaker      fm
application/vnd.frogans.fnc     fnc
application/vnd.frogans.ltf     ltf
application/vnd.fsc.weblaunch     fsc
application/vnd.fujitsu.oasys     oas
application/vnd.fujitsu.oasys2      oa2
application/vnd.fujitsu.oasys3      oa3
application/vnd.fujitsu.oasysgp     fg5
application/vnd.fujitsu.oasysprs    bh2
application/vnd.fujixerox.ART-EX
application/vnd.fujixerox.ART4
application/vnd.fujixerox.ddd     ddd
application/vnd.fujixerox.docuworks   xdw
application/vnd.fujixerox.docuworks.binder  xbd
application/vnd.fujixerox.docuworks.container xct
application/vnd.fujixerox.HBPL
application/vnd.fut-misnet
application/vnd.fuzzysheet      fzs
application/vnd.genomatix.tuxedo    txd
application/vnd.geo+json      geojson
application/vnd.geocube+xml     g3 g³
application/vnd.geogebra.file     ggb
application/vnd.geogebra.tool     ggt
application/vnd.geometry-explorer   gex gre
application/vnd.geonext       gxt
application/vnd.geoplan       g2w
application/vnd.geospace      g3w
# gbr: application/rpki-ghostbusters
application/vnd.gerber
application/vnd.globalplatform.card-content-mgt
application/vnd.globalplatform.card-content-mgt-response
application/vnd.gmx       gmx
application/vnd.google-earth.kml+xml    kml
application/vnd.google-earth.kmz    kmz
application/vnd.gov.sk.e-form+xml
application/vnd.gov.sk.e-form+zip
application/vnd.gov.sk.xmldatacontainer+xml
application/vnd.grafeq        gqf gqs
application/vnd.gridmp
application/vnd.groove-account      gac
application/vnd.groove-help     ghf
application/vnd.groove-identity-message   gim
application/vnd.groove-injector     grv
application/vnd.groove-tool-message   gtm
application/vnd.groove-tool-template    tpl
application/vnd.groove-vcard      vcg
application/vnd.hal+json
application/vnd.hal+xml       hal
application/vnd.HandHeld-Entertainment+xml  zmm
application/vnd.hbci        hbci hbc kom upa pkd bpd
# rep: application/vnd.businessobjects
application/vnd.hcl-bireports
application/vnd.heroku+json
application/vnd.hhe.lesson-player   les
application/vnd.hp-HPGL       hpgl
application/vnd.hp-hpid       hpi hpid
application/vnd.hp-hps        hps
application/vnd.hp-jlyt       jlt
application/vnd.hp-PCL        pcl
application/vnd.hp-PCLXL
application/vnd.httphone
application/vnd.hydrostatix.sof-data    sfd-hdstx
application/vnd.hzn-3d-crossword    x3d
application/vnd.ibm.afplinedata
application/vnd.ibm.electronic-media    emm
application/vnd.ibm.MiniPay     mpy
application/vnd.ibm.modcap      list3820 listafp afp pseg3820
application/vnd.ibm.rights-management   irm
application/vnd.ibm.secure-container    sc
application/vnd.iccprofile      icc icm
application/vnd.ieee.1905     1905.1
application/vnd.igloader      igl
application/vnd.immervision-ivp     ivp
application/vnd.immervision-ivu     ivu
application/vnd.ims.imsccv1p1     imscc
application/vnd.ims.imsccv1p2
application/vnd.ims.imsccv1p3
application/vnd.ims.lis.v2.result+json
application/vnd.ims.lti.v2.toolconsumerprofile+json
application/vnd.ims.lti.v2.toolproxy.id+json
application/vnd.ims.lti.v2.toolproxy+json
application/vnd.ims.lti.v2.toolsettings+json
application/vnd.ims.lti.v2.toolsettings.simple+json
application/vnd.informedcontrol.rms+xml
# application/vnd.informix-visionary obsoleted by application/vnd.visionary
application/vnd.infotech.project
application/vnd.infotech.project+xml
application/vnd.innopath.wamp.notification
application/vnd.insors.igm      igm
application/vnd.intercon.formnet    xpw xpx
application/vnd.intergeo      i2g
application/vnd.intertrust.digibox
application/vnd.intertrust.nncp
application/vnd.intu.qbo      qbo
application/vnd.intu.qfx      qfx
application/vnd.iptc.g2.catalogitem+xml
application/vnd.iptc.g2.conceptitem+xml
application/vnd.iptc.g2.knowledgeitem+xml
application/vnd.iptc.g2.newsitem+xml
application/vnd.iptc.g2.newsmessage+xml
application/vnd.iptc.g2.packageitem+xml
application/vnd.iptc.g2.planningitem+xml
application/vnd.ipunplugged.rcprofile   rcprofile
application/vnd.irepository.package+xml   irp
application/vnd.is-xpr        xpr
application/vnd.isac.fcs      fcs
application/vnd.jam       jam
application/vnd.japannet-directory-service
application/vnd.japannet-jpnstore-wakeup
application/vnd.japannet-payment-wakeup
application/vnd.japannet-registration
application/vnd.japannet-registration-wakeup
application/vnd.japannet-setstore-wakeup
application/vnd.japannet-verification
application/vnd.japannet-verification-wakeup
application/vnd.jcp.javame.midlet-rms   rms
application/vnd.jisp        jisp
application/vnd.joost.joda-archive    joda
application/vnd.jsk.isdn-ngn
application/vnd.kahootz       ktz ktr
application/vnd.kde.karbon      karbon
application/vnd.kde.kchart      chrt
application/vnd.kde.kformula      kfo
application/vnd.kde.kivio     flw
application/vnd.kde.kontour     kon
application/vnd.kde.kpresenter      kpr kpt
application/vnd.kde.kspread     ksp
application/vnd.kde.kword     kwd kwt
application/vnd.kenameaapp      htke
application/vnd.kidspiration      kia
application/vnd.Kinar       kne knp sdf
application/vnd.koan        skp skd skm skt
application/vnd.kodak-descriptor    sse
application/vnd.las.las+xml     lasxml
application/vnd.liberty-request+xml
application/vnd.llamagraphics.life-balance.desktop  lbd
application/vnd.llamagraphics.life-balance.exchange+xml lbe
application/vnd.lotus-1-2-3     123 wk4 wk3 wk1
application/vnd.lotus-approach      apr vew
application/vnd.lotus-freelance     prz pre
application/vnd.lotus-notes     nsf ntf ndl ns4 ns3 ns2 nsh nsg
application/vnd.lotus-organizer     or3 or2 org
application/vnd.lotus-screencam     scm
application/vnd.lotus-wordpro     lwp sam
application/vnd.macports.portpkg    portpkg
application/vnd.marlin.drm.actiontoken+xml
application/vnd.marlin.drm.conftoken+xml
application/vnd.marlin.drm.license+xml
application/vnd.marlin.drm.mdcf     mdc
application/vnd.mason+json
application/vnd.maxmind.maxmind-db    mmdb
application/vnd.mcd       mcd
application/vnd.medcalcdata     mc1
application/vnd.mediastation.cdkey    cdkey
application/vnd.meridian-slingshot
application/vnd.MFER        mwf
application/vnd.mfmp        mfm
application/vnd.micrografx.flo      flo
application/vnd.micrografx.igx      igx
application/vnd.miele+json
application/vnd.mif       mif
application/vnd.minisoft-hp3000-save
application/vnd.mitsubishi.misty-guard.trustweb
application/vnd.Mobius.DAF      daf
application/vnd.Mobius.DIS      dis
application/vnd.Mobius.MBK      mbk
application/vnd.Mobius.MQY      mqy
application/vnd.Mobius.MSL      msl
application/vnd.Mobius.PLC      plc
application/vnd.Mobius.TXF      txf
application/vnd.mophun.application    mpn
application/vnd.mophun.certificate    mpc
application/vnd.motorola.flexsuite
application/vnd.motorola.flexsuite.adsi
application/vnd.motorola.flexsuite.fis
application/vnd.motorola.flexsuite.gotap
application/vnd.motorola.flexsuite.kmr
application/vnd.motorola.flexsuite.ttc
application/vnd.motorola.flexsuite.wem
application/vnd.motorola.iprm
application/vnd.mozilla.xul+xml     xul
application/vnd.ms-3mfdocument      3mf
application/vnd.ms-artgalry     cil
application/vnd.ms-asf        asf
application/vnd.ms-cab-compressed   cab
application/vnd.ms-excel      xls xlm xla xlc xlt xlw
application/vnd.ms-excel.template.macroEnabled.12 xltm
application/vnd.ms-excel.addin.macroEnabled.12  xlam
application/vnd.ms-excel.sheet.binary.macroEnabled.12 xlsb
application/vnd.ms-excel.sheet.macroEnabled.12  xlsm
application/vnd.ms-fontobject     eot
application/vnd.ms-htmlhelp     chm
application/vnd.ms-ims        ims
application/vnd.ms-lrm        lrm
application/vnd.ms-office.activeX+xml
application/vnd.ms-officetheme      thmx
application/vnd.ms-playready.initiator+xml
application/vnd.ms-powerpoint     ppt pps pot
application/vnd.ms-powerpoint.addin.macroEnabled.12 ppam
application/vnd.ms-powerpoint.presentation.macroEnabled.12  pptm
application/vnd.ms-powerpoint.slide.macroEnabled.12 sldm
application/vnd.ms-powerpoint.slideshow.macroEnabled.12 ppsm
application/vnd.ms-powerpoint.template.macroEnabled.12  potm
application/vnd.ms-project      mpp mpt
application/vnd.ms-tnef       tnef tnf
application/vnd.ms-windows.printerpairing
application/vnd.ms-wmdrm.lic-chlg-req
application/vnd.ms-wmdrm.lic-resp
application/vnd.ms-wmdrm.meter-chlg-req
application/vnd.ms-wmdrm.meter-resp
application/vnd.ms-word.document.macroEnabled.12  docm
application/vnd.ms-word.template.macroEnabled.12  dotm
application/vnd.ms-works      wcm wdb wks wps
application/vnd.ms-wpl        wpl
application/vnd.ms-xpsdocument      xps
application/vnd.msa-disk-image      msa
application/vnd.mseq        mseq
application/vnd.msign
application/vnd.multiad.creator     crtr
application/vnd.multiad.creator.cif   cif
application/vnd.music-niff
application/vnd.musician      mus
application/vnd.muvee.style     msty
application/vnd.mynfc       taglet
application/vnd.ncd.control
application/vnd.ncd.reference
application/vnd.nervana       entity request bkm kcm
application/vnd.netfpx
# ntf: application/vnd.lotus-notes
application/vnd.nitf        nitf
application/vnd.neurolanguage.nlu   nlu
application/vnd.nintendo.nitro.rom    nds
application/vnd.nintendo.snes.rom   sfc smc
application/vnd.noblenet-directory    nnd
application/vnd.noblenet-sealer     nns
application/vnd.noblenet-web      nnw
application/vnd.nokia.catalogs
application/vnd.nokia.conml+wbxml
application/vnd.nokia.conml+xml
application/vnd.nokia.iptv.config+xml
application/vnd.nokia.iSDS-radio-presets
application/vnd.nokia.landmark+wbxml
application/vnd.nokia.landmark+xml
application/vnd.nokia.landmarkcollection+xml
application/vnd.nokia.n-gage.ac+xml   ac
application/vnd.nokia.n-gage.data   ngdat
application/vnd.nokia.n-gage.symbian.install  n-gage
application/vnd.nokia.ncd
application/vnd.nokia.pcd+wbxml
application/vnd.nokia.pcd+xml
application/vnd.nokia.radio-preset    rpst
application/vnd.nokia.radio-presets   rpss
application/vnd.novadigm.EDM      edm
application/vnd.novadigm.EDX      edx
application/vnd.novadigm.EXT      ext
application/vnd.ntt-local.content-share
application/vnd.ntt-local.file-transfer
application/vnd.ntt-local.ogw_remote-access
application/vnd.ntt-local.sip-ta_remote
application/vnd.ntt-local.sip-ta_tcp_stream
application/vnd.oasis.opendocument.chart      odc
application/vnd.oasis.opendocument.chart-template   otc
application/vnd.oasis.opendocument.database     odb
application/vnd.oasis.opendocument.formula      odf
application/vnd.oasis.opendocument.formula-template   otf
application/vnd.oasis.opendocument.graphics     odg
application/vnd.oasis.opendocument.graphics-template    otg
application/vnd.oasis.opendocument.image      odi
application/vnd.oasis.opendocument.image-template   oti
application/vnd.oasis.opendocument.presentation     odp
application/vnd.oasis.opendocument.presentation-template  otp
application/vnd.oasis.opendocument.spreadsheet      ods
application/vnd.oasis.opendocument.spreadsheet-template   ots
application/vnd.oasis.opendocument.text       odt
application/vnd.oasis.opendocument.text-master      odm
application/vnd.oasis.opendocument.text-template    ott
application/vnd.oasis.opendocument.text-web     oth
application/vnd.obn
application/vnd.oftn.l10n+json
application/vnd.oipf.contentaccessdownload+xml
application/vnd.oipf.contentaccessstreaming+xml
application/vnd.oipf.cspg-hexbinary
application/vnd.oipf.dae.svg+xml
application/vnd.oipf.dae.xhtml+xml
application/vnd.oipf.mippvcontrolmessage+xml
application/vnd.oipf.pae.gem
application/vnd.oipf.spdiscovery+xml
application/vnd.oipf.spdlist+xml
application/vnd.oipf.ueprofile+xml
application/vnd.olpc-sugar      xo
application/vnd.oma.bcast.associated-procedure-parameter+xml
application/vnd.oma.bcast.drm-trigger+xml
application/vnd.oma.bcast.imd+xml
application/vnd.oma.bcast.ltkm
application/vnd.oma.bcast.notification+xml
application/vnd.oma.bcast.provisioningtrigger
application/vnd.oma.bcast.sgboot
application/vnd.oma.bcast.sgdd+xml
application/vnd.oma.bcast.sgdu
application/vnd.oma.bcast.simple-symbol-container
application/vnd.oma.bcast.smartcard-trigger+xml
application/vnd.oma.bcast.sprov+xml
application/vnd.oma.bcast.stkm
application/vnd.oma.cab-address-book+xml
application/vnd.oma.cab-feature-handler+xml
application/vnd.oma.cab-pcc+xml
application/vnd.oma.cab-subs-invite+xml
application/vnd.oma.cab-user-prefs+xml
application/vnd.oma.dcd
application/vnd.oma.dcdc
application/vnd.oma.dd2+xml     dd2
application/vnd.oma.drm.risd+xml
application/vnd.oma.group-usage-list+xml
application/vnd.oma.pal+xml
application/vnd.oma.poc.detailed-progress-report+xml
application/vnd.oma.poc.final-report+xml
application/vnd.oma.poc.groups+xml
application/vnd.oma.poc.invocation-descriptor+xml
application/vnd.oma.poc.optimized-progress-report+xml
application/vnd.oma.push
application/vnd.oma.scidm.messages+xml
application/vnd.oma.xcap-directory+xml
application/vnd.oma-scws-config
application/vnd.oma-scws-http-request
application/vnd.oma-scws-http-response
application/vnd.omads-email+xml
application/vnd.omads-file+xml
application/vnd.omads-folder+xml
application/vnd.omaloc-supl-init
application/vnd.openeye.oeb     oeb
application/vnd.openofficeorg.extension   oxt
application/vnd.openxmlformats-officedocument.custom-properties+xml
application/vnd.openxmlformats-officedocument.customXmlProperties+xml
application/vnd.openxmlformats-officedocument.drawing+xml
application/vnd.openxmlformats-officedocument.drawingml.chart+xml
application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml
application/vnd.openxmlformats-officedocument.drawingml.diagramColors+xml
application/vnd.openxmlformats-officedocument.drawingml.diagramData+xml
application/vnd.openxmlformats-officedocument.drawingml.diagramLayout+xml
application/vnd.openxmlformats-officedocument.drawingml.diagramStyle+xml
application/vnd.openxmlformats-officedocument.extended-properties+xml
application/vnd.openxmlformats-officedocument.presentationml.commentAuthors+xml
application/vnd.openxmlformats-officedocument.presentationml.comments+xml
application/vnd.openxmlformats-officedocument.presentationml.handoutMaster+xml
application/vnd.openxmlformats-officedocument.presentationml.notesMaster+xml
application/vnd.openxmlformats-officedocument.presentationml.notesSlide+xml
application/vnd.openxmlformats-officedocument.presentationml.presProps+xml
application/vnd.openxmlformats-officedocument.presentationml.presentation pptx
application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml
application/vnd.openxmlformats-officedocument.presentationml.slide  sldx
application/vnd.openxmlformats-officedocument.presentationml.slide+xml
application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml
application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml
application/vnd.openxmlformats-officedocument.presentationml.slideUpdateInfo+xml
application/vnd.openxmlformats-officedocument.presentationml.slideshow  ppsx
application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml
application/vnd.openxmlformats-officedocument.presentationml.tableStyles+xml
application/vnd.openxmlformats-officedocument.presentationml.tags+xml
application/vnd.openxmlformats-officedocument.presentationml.template potx
application/vnd.openxmlformats-officedocument.presentationml.template.main+xml
application/vnd.openxmlformats-officedocument.presentationml.viewProps+xml
application/vnd.openxmlformats-officedocument.spreadsheetml.calcChain+xml
application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml
application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml
application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml
application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml
application/vnd.openxmlformats-officedocument.spreadsheetml.externalLink+xml
application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml
application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml
application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml
application/vnd.openxmlformats-officedocument.spreadsheetml.queryTable+xml
application/vnd.openxmlformats-officedocument.spreadsheetml.revisionHeaders+xml
application/vnd.openxmlformats-officedocument.spreadsheetml.revisionLog+xml
application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml
application/vnd.openxmlformats-officedocument.spreadsheetml.sheetMetadata+xml
application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml
application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml
application/vnd.openxmlformats-officedocument.spreadsheetml.tableSingleCells+xml
application/vnd.openxmlformats-officedocument.spreadsheetml.template  xltx
application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml
application/vnd.openxmlformats-officedocument.spreadsheetml.userNames+xml
application/vnd.openxmlformats-officedocument.spreadsheetml.volatileDependencies+xml
application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml
application/vnd.openxmlformats-officedocument.theme+xml
application/vnd.openxmlformats-officedocument.themeOverride+xml
application/vnd.openxmlformats-officedocument.vmlDrawing
application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml
application/vnd.openxmlformats-officedocument.wordprocessingml.document docx
application/vnd.openxmlformats-officedocument.wordprocessingml.document.glossary+xml
application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml
application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml
application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml
application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml
application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml
application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml
application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml
application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml
application/vnd.openxmlformats-officedocument.wordprocessingml.template dotx
application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml
application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml
application/vnd.openxmlformats-package.core-properties+xml
application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml
application/vnd.openxmlformats-package.relationships+xml
application/vnd.oracle.resource+json
application/vnd.orange.indata
application/vnd.osa.netdeploy     ndc
application/vnd.osgeo.mapguide.package    mgp
# jar: application/x-java-archive
application/vnd.osgi.bundle
application/vnd.osgi.dp       dp
application/vnd.osgi.subsystem      esa
application/vnd.otps.ct-kip+xml
application/vnd.palm        prc pdb pqa oprc
application/vnd.panoply       plp
application/vnd.paos+xml
application/vnd.pawaafile     paw
application/vnd.pcos
application/vnd.pg.format         str
application/vnd.pg.osasli     ei6
application/vnd.piaccess.application-license  pil
application/vnd.picsel        efif
application/vnd.pmi.widget      wg
application/vnd.poc.group-advertisement+xml
application/vnd.pocketlearn     plf
application/vnd.powerbuilder6     pbd
application/vnd.powerbuilder6-s
application/vnd.powerbuilder7
application/vnd.powerbuilder7-s
application/vnd.powerbuilder75
application/vnd.powerbuilder75-s
application/vnd.preminet      preminet
application/vnd.previewsystems.box    box vbox
application/vnd.proteus.magazine    mgz
application/vnd.publishare-delta-tree   qps
# pti: image/prs.pti
application/vnd.pvi.ptid1     ptid
application/vnd.pwg-multiplexed
application/vnd.pwg-xhtml-print+xml
application/vnd.qualcomm.brew-app-res   bar
application/vnd.Quark.QuarkXPress   qxd qxt qwd qwt qxl qxb
application/vnd.quobject-quoxdocument   quox quiz
application/vnd.radisys.moml+xml
application/vnd.radisys.msml-audit-conf+xml
application/vnd.radisys.msml-audit-conn+xml
application/vnd.radisys.msml-audit-dialog+xml
application/vnd.radisys.msml-audit-stream+xml
application/vnd.radisys.msml-audit+xml
application/vnd.radisys.msml-conf+xml
application/vnd.radisys.msml-dialog-base+xml
application/vnd.radisys.msml-dialog-fax-detect+xml
application/vnd.radisys.msml-dialog-fax-sendrecv+xml
application/vnd.radisys.msml-dialog-group+xml
application/vnd.radisys.msml-dialog-speech+xml
application/vnd.radisys.msml-dialog-transform+xml
application/vnd.radisys.msml-dialog+xml
application/vnd.radisys.msml+xml
application/vnd.rainstor.data     tree
application/vnd.rapid
application/vnd.realvnc.bed     bed
application/vnd.recordare.musicxml    mxl
application/vnd.recordare.musicxml+xml
application/vnd.RenLearn.rlprint
application/vnd.rig.cryptonote      cryptonote
application/vnd.route66.link66+xml    link66
# gbr: application/rpki-ghostbusters
application/vnd.rs-274x
application/vnd.ruckus.download
application/vnd.s3sms
application/vnd.sailingtracker.track    st
application/vnd.sbm.cid
application/vnd.sbm.mid2
application/vnd.scribus       scd sla slaz
application/vnd.sealed.3df      s3df
application/vnd.sealed.csf      scsf
application/vnd.sealed.doc      sdoc sdo s1w
application/vnd.sealed.eml      seml sem
application/vnd.sealed.mht      smht smh
application/vnd.sealed.net
# spp: application/scvp-vp-response
application/vnd.sealed.ppt      sppt s1p
application/vnd.sealed.tiff     stif
application/vnd.sealed.xls      sxls sxl s1e
# stm: audio/x-stm
application/vnd.sealedmedia.softseal.html stml s1h
application/vnd.sealedmedia.softseal.pdf  spdf spd s1a
application/vnd.seemail       see
application/vnd.sema        sema
application/vnd.semd        semd
application/vnd.semf        semf
application/vnd.shana.informed.formdata   ifm
application/vnd.shana.informed.formtemplate itp
application/vnd.shana.informed.interchange  iif
application/vnd.shana.informed.package    ipk
application/vnd.SimTech-MindMapper    twd twds
application/vnd.siren+json
application/vnd.smaf        mmf
application/vnd.smart.notebook      notebook
application/vnd.smart.teacher     teacher
application/vnd.software602.filler.form+xml fo
application/vnd.software602.filler.form-xml-zip zfo
application/vnd.solent.sdkm+xml     sdkm sdkd
application/vnd.spotfire.dxp      dxp
application/vnd.spotfire.sfs      sfs
application/vnd.sss-cod
application/vnd.sss-dtf
application/vnd.sss-ntf
application/vnd.stepmania.package   smzip
application/vnd.stepmania.stepchart   sm
application/vnd.street-stream
application/vnd.sun.wadl+xml      wadl
application/vnd.sus-calendar      sus susp
application/vnd.svd
application/vnd.swiftview-ics
application/vnd.syncml+xml      xsm
application/vnd.syncml.dm+wbxml     bdm
application/vnd.syncml.dm+xml     xdm
application/vnd.syncml.dm.notification
application/vnd.syncml.dmddf+wbxml
application/vnd.syncml.dmddf+xml    ddf
application/vnd.syncml.dmtnds+wbxml
application/vnd.syncml.dmtnds+xml
application/vnd.syncml.ds.notification
application/vnd.tao.intent-module-archive tao
application/vnd.tcpdump.pcap      pcap cap dmp
application/vnd.theqvd        qvd
application/vnd.tmd.mediaflex.api+xml
application/vnd.tmobile-livetv      tmo
application/vnd.trid.tpt      tpt
application/vnd.triscape.mxs      mxs
application/vnd.trueapp       tra
application/vnd.truedoc
# cab: application/vnd.ms-cab-compressed
application/vnd.ubisoft.webplayer
application/vnd.ufdl        ufdl ufd frm
application/vnd.uiq.theme     utz
application/vnd.umajin        umj
application/vnd.unity       unityweb
application/vnd.uoml+xml      uoml uo
application/vnd.uplanet.alert
application/vnd.uplanet.alert-wbxml
application/vnd.uplanet.bearer-choice
application/vnd.uplanet.bearer-choice-wbxml
application/vnd.uplanet.cacheop
application/vnd.uplanet.cacheop-wbxml
application/vnd.uplanet.channel
application/vnd.uplanet.channel-wbxml
application/vnd.uplanet.list
application/vnd.uplanet.list-wbxml
application/vnd.uplanet.listcmd
application/vnd.uplanet.listcmd-wbxml
application/vnd.uplanet.signal
application/vnd.valve.source.material   vmt
application/vnd.vcx       vcx
# sxi: application/vnd.sun.xml.impress
application/vnd.vd-study      mxi study-inter model-inter
# mcd: application/vnd.mcd
application/vnd.vectorworks     vwx
application/vnd.verimatrix.vcas
application/vnd.vidsoft.vidconference   vsc
application/vnd.visio       vsd vst vsw vss
application/vnd.visionary     vis
# vsc: application/vnd.vidsoft.vidconference
application/vnd.vividence.scriptfile
application/vnd.vsf       vsf
application/vnd.wap.sic       sic
application/vnd.wap.slc       slc
application/vnd.wap.wbxml     wbxml
application/vnd.wap.wmlc      wmlc
application/vnd.wap.wmlscriptc      wmlsc
application/vnd.webturbo      wtb
application/vnd.wfa.p2p       p2p
application/vnd.wfa.wsc       wsc
application/vnd.windows.devicepairing
application/vnd.wmc       wmc
application/vnd.wmf.bootstrap
# nb: application/mathematica for now
application/vnd.wolfram.mathematica
application/vnd.wolfram.mathematica.package m
application/vnd.wolfram.player      nbp
application/vnd.wordperfect     wpd
application/vnd.wqd       wqd
application/vnd.wrq-hp3000-labelled
application/vnd.wt.stf        stf
application/vnd.wv.csp+xml
application/vnd.wv.csp+wbxml      wv
application/vnd.wv.ssp+xml
application/vnd.xacml+json
application/vnd.xara        xar
application/vnd.xfdl        xfdl xfd
application/vnd.xfdl.webform
application/vnd.xmi+xml
application/vnd.xmpie.cpkg      cpkg
application/vnd.xmpie.dpkg      dpkg
# dpkg: application/vnd.xmpie.dpkg
application/vnd.xmpie.plan
application/vnd.xmpie.ppkg      ppkg
application/vnd.xmpie.xlim      xlim
application/vnd.yamaha.hv-dic     hvd
application/vnd.yamaha.hv-script    hvs
application/vnd.yamaha.hv-voice     hvp
application/vnd.yamaha.openscoreformat    osf
application/vnd.yamaha.openscoreformat.osfpvg+xml
application/vnd.yamaha.remote-setup
application/vnd.yamaha.smaf-audio   saf
application/vnd.yamaha.smaf-phrase    spf
application/vnd.yamaha.through-ngn
application/vnd.yamaha.tunnel-udpencap
application/vnd.yaoweme       yme
application/vnd.yellowriver-custom-menu   cmp
application/vnd.zul       zir zirz
application/vnd.zzazz.deck+xml      zaz
application/voicexml+xml      vxml
application/vq-rtcp-xr
application/watcherinfo+xml     wif
application/whoispp-query
application/whoispp-response
application/widget        wgt
application/wita
application/wordperfect5.1
application/wsdl+xml        wsdl
application/wspolicy+xml      wspolicy
# yes, this *is* IANA registered despite of x-
application/x-www-form-urlencoded
application/x400-bp
application/xacml+xml
application/xcap-att+xml      xav
application/xcap-caps+xml     xca
application/xcap-diff+xml     xdf
application/xcap-el+xml       xel
application/xcap-error+xml      xer
application/xcap-ns+xml       xns
application/xcon-conference-info-diff+xml
application/xcon-conference-info+xml
application/xenc+xml
application/xhtml+xml       xhtml xhtm xht
# application/xhtml-voice+xml obsoleted by application/xv+xml
# xml, xsd, rng: text/xml
application/xml
# mod: audio/x-mod
application/xml-dtd       dtd
# ent: text/xml-external-parsed-entity
application/xml-external-parsed-entity
application/xml-patch+xml
application/xmpp+xml
application/xop+xml       xop
application/xslt+xml        xsl xslt
application/xv+xml        mxml xhvml xvml xvm
application/yang        yang
application/yin+xml       yin
application/zip         zip
application/zlib
audio/1d-interleaved-parityfec
audio/32kadpcm          726
# 3gp, 3gpp: video/3gpp
audio/3gpp
# 3g2, 3gpp2: video/3gpp2
audio/3gpp2
audio/ac3         ac3
audio/AMR         amr
audio/AMR-WB          awb
audio/amr-wb+
audio/aptx
audio/asc         acn
# aa3, omg: audio/ATRAC3
audio/ATRAC-ADVANCED-LOSSLESS     aal
# aa3, omg: audio/ATRAC3
audio/ATRAC-X         atx
audio/ATRAC3          at3 aa3 omg
audio/basic         au snd
audio/BV16
audio/BV32
audio/clearmode
audio/CN
audio/DAT12
audio/dls         dls
audio/dsr-es201108
audio/dsr-es202050
audio/dsr-es202211
audio/dsr-es202212
audio/DV
audio/DVI4
audio/eac3
audio/encaprtp
audio/EVRC          evc
# qcp: audio/qcelp
audio/EVRC-QCP
audio/EVRC0
audio/EVRC1
audio/EVRCB         evb
audio/EVRCB0
audio/EVRCB1
audio/EVRCNW          enw
audio/EVRCNW0
audio/EVRCNW1
audio/EVRCWB          evw
audio/EVRCWB0
audio/EVRCWB1
audio/example
audio/fwdred
audio/G719
audio/G722
audio/G7221
audio/G723
audio/G726-16
audio/G726-24
audio/G726-32
audio/G726-40
audio/G728
audio/G729
audio/G7291
audio/G729D
audio/G729E
audio/GSM
audio/GSM-EFR
audio/GSM-HR-08
audio/iLBC          lbc
audio/ip-mr_v2.5
# wav: audio/x-wav
audio/L16         l16
audio/L20
audio/L24
audio/L8
audio/LPC
audio/mobile-xmf        mxmf
# mp4, mpg4: video/mp4, see RFC 4337
audio/mp4         m4a
audio/MP4A-LATM
audio/MPA
audio/mpa-robust
audio/mpeg          mp3 mpga mp1 mp2
audio/mpeg4-generic
audio/ogg         oga ogg opus spx
audio/parityfec
audio/PCMA
audio/PCMA-WB
audio/PCMU
audio/PCMU-WB
audio/prs.sid         sid psid
audio/qcelp         qcp
audio/raptorfec
audio/RED
audio/rtp-enc-aescm128
audio/rtp-midi
audio/rtploopback
audio/rtx
audio/SMV         smv
# qcp: audio/qcelp, see RFC 3625
audio/SMV-QCP
audio/SMV0
# mid: audio/midi
audio/sp-midi
audio/speex
audio/t140c
audio/t38
audio/telephone-event
audio/tone
audio/UEMCLIP
audio/ulpfec
audio/VDVI
audio/VMR-WB
audio/vnd.3gpp.iufp
audio/vnd.4SB
audio/vnd.audikoz       koz
audio/vnd.CELP
audio/vnd.cisco.nse
audio/vnd.cmles.radio-events
audio/vnd.cns.anp1
audio/vnd.cns.inf1
audio/vnd.dece.audio        uva uvva
audio/vnd.digital-winds       eol
audio/vnd.dlna.adts
audio/vnd.dolby.heaac.1
audio/vnd.dolby.heaac.2
audio/vnd.dolby.mlp       mlp
audio/vnd.dolby.mps
audio/vnd.dolby.pl2
audio/vnd.dolby.pl2x
audio/vnd.dolby.pl2z
audio/vnd.dolby.pulse.1
audio/vnd.dra
# wav: audio/x-wav, cpt: application/mac-compactpro
audio/vnd.dts         dts
audio/vnd.dts.hd        dtshd
# dvb: video/vnd.dvb.file
audio/vnd.dvb.file
audio/vnd.everad.plj        plj
# rm: audio/x-pn-realaudio
audio/vnd.hns.audio
audio/vnd.lucent.voice        lvp
audio/vnd.ms-playready.media.pya    pya
# mxmf: audio/mobile-xmf
audio/vnd.nokia.mobile-xmf
audio/vnd.nortel.vbk        vbk
audio/vnd.nuera.ecelp4800     ecelp4800
audio/vnd.nuera.ecelp7470     ecelp7470
audio/vnd.nuera.ecelp9600     ecelp9600
audio/vnd.octel.sbc
# audio/vnd.qcelp deprecated in favour of audio/qcelp
audio/vnd.rhetorex.32kadpcm
audio/vnd.rip         rip
audio/vnd.sealedmedia.softseal.mpeg   smp3 smp s1m
audio/vnd.vmx.cvsd
audio/vorbis
audio/vorbis-config
image/cgm         cgm
image/example
image/fits          fits fit fts
image/g3fax
image/gif         gif
image/ief         ief
image/jp2         jp2 jpg2
image/jpeg          jpg jpeg jpe jfif
image/jpm         jpm jpgm
image/jpx         jpx jpf
image/ktx         ktx
image/naplps
image/png         png
image/prs.btif          btif btf
image/prs.pti         pti
image/pwg-raster
image/svg+xml         svg svgz
image/t38         t38
image/tiff          tiff tif
image/tiff-fx         tfx
image/vnd.adobe.photoshop     psd
image/vnd.airzip.accelerator.azv    azv
image/vnd.cns.inf2
image/vnd.dece.graphic        uvi uvvi uvg uvvg
image/vnd.djvu          djvu djv
# sub: text/vnd.dvb.subtitle
image/vnd.dvb.subtitle
image/vnd.dwg         dwg
image/vnd.dxf         dxf
image/vnd.fastbidsheet        fbs
image/vnd.fpx         fpx
image/vnd.fst         fst
image/vnd.fujixerox.edmics-mmr      mmr
image/vnd.fujixerox.edmics-rlc      rlc
image/vnd.globalgraphics.pgb      pgb
image/vnd.microsoft.icon      ico
image/vnd.mix
image/vnd.ms-modi       mdi
image/vnd.net-fpx
image/vnd.radiance        hdr rgbe xyze
image/vnd.sealed.png        spng spn s1n
image/vnd.sealedmedia.softseal.gif    sgif sgi s1g
image/vnd.sealedmedia.softseal.jpg    sjpg sjp s1j
image/vnd.svf
image/vnd.tencent.tap       tap
image/vnd.valve.source.texture      vtf
image/vnd.wap.wbmp        wbmp
image/vnd.xiff          xif
message/CPIM
message/delivery-status
message/disposition-notification
message/example
message/external-body
message/feedback-report
message/global          u8msg
message/global-delivery-status      u8dsn
message/global-disposition-notification   u8mdn
message/global-headers        u8hdr
message/http
# cl: application/simple-filter+xml
message/imdn+xml
# message/news obsoleted by message/rfc822
message/partial
message/rfc822          eml mail art
message/s-http
message/sip
message/sipfrag
message/tracking-status
message/vnd.si.simp
# wsc: application/vnd.wfa.wsc
message/vnd.wfa.wsc
model/example
model/iges          igs iges
model/mesh          msh mesh silo
model/vnd.collada+xml       dae
model/vnd.dwf         dwf
# 3dml, 3dm: text/vnd.in3d.3dml
model/vnd.flatland.3dml
model/vnd.gdl         gdl gsm win dor lmp rsm msm ism
model/vnd.gs-gdl
model/vnd.gtw         gtw
model/vnd.moml+xml        moml
model/vnd.mts         mts
model/vnd.opengex       ogex
model/vnd.parasolid.transmit.binary   x_b xmt_bin
model/vnd.parasolid.transmit.text   x_t xmt_txt
model/vnd.valve.source.compiled-map   bsp
model/vnd.vtu         vtu
model/vrml          wrl vrml
# x3db: model/x3d+xml
model/x3d+fastinfoset
# x3d: application/vnd.hzn-3d-crossword
model/x3d+xml         x3db
model/x3d-vrml          x3dv x3dvz
multipart/alternative
multipart/appledouble
multipart/byteranges
multipart/digest
multipart/encrypted
multipart/form-data
multipart/header-set
multipart/mixed
multipart/parallel
multipart/related
multipart/report
multipart/signed
multipart/voice-message       vpm
multipart/x-mixed-replace
text/1d-interleaved-parityfec
text/cache-manifest       appcache manifest
text/calendar         ics ifb
text/css          css
text/csv          csv
text/csv-schema         csvs
text/directory
text/dns          soa zone
text/encaprtp
# text/ecmascript obsoleted by application/ecmascript
text/enriched
text/example
text/fwdred
text/grammar-ref-list
text/html         html htm
# text/javascript obsoleted by application/javascript
text/jcr-cnd          cnd
text/markdown         markdown md
text/mizar          miz
text/n3           n3
text/parameters
text/parityfec
text/plain    txt asc text pm el c h cc hh cxx hxx f90 conf log
text/provenance-notation      provn
text/prs.fallenstein.rst      rst
text/prs.lines.tag        tag dsc
text/raptorfec
text/RED
text/rfc822-headers
text/richtext         rtx
# rtf: application/rtf
text/rtf
text/rtp-enc-aescm128
text/rtploopback
text/rtx
text/sgml         sgml sgm
text/t140
text/tab-separated-values     tsv
text/troff          t tr roff
text/turtle         ttl
text/ulpfec
text/uri-list         uris uri
text/vcard          vcf vcard
text/vnd.a          a
text/vnd.abc          abc
# curl: application/vnd.curl
text/vnd.curl
text/vnd.debian.copyright     copyright
text/vnd.DMClientScript       dms
text/vnd.dvb.subtitle       sub
text/vnd.esmertec.theme-descriptor    jtd
text/vnd.fly          fly
text/vnd.fmi.flexstor       flx
text/vnd.graphviz       gv dot
text/vnd.in3d.3dml        3dml 3dm
text/vnd.in3d.spot        spot spo
text/vnd.IPTC.NewsML
text/vnd.IPTC.NITF
text/vnd.latex-z
text/vnd.motorola.reflex
text/vnd.ms-mediapackage      mpf
text/vnd.net2phone.commcenter.command   ccc
text/vnd.radisys.msml-basic-layout
text/vnd.si.uricatalogue      uric
text/vnd.sun.j2me.app-descriptor    jad
text/vnd.trolltech.linguist     ts
text/vnd.wap.si         si
text/vnd.wap.sl         sl
text/vnd.wap.wml        wml
text/vnd.wap.wmlscript        wmls
text/xml          xml xsd rng
text/xml-external-parsed-entity     ent
video/1d-interleaved-parityfec
video/3gpp          3gp 3gpp
video/3gpp2         3g2 3gpp2
video/3gpp-tt
video/BMPEG
video/BT656
video/CelB
video/DV
video/encaprtp
video/example
video/H261
video/H263
video/H263-1998
video/H263-2000
video/H264
video/H264-RCDO
video/H264-SVC
video/iso.segment       m4s
video/JPEG
video/jpeg2000
video/mj2         mj2 mjp2
video/MP1S
video/MP2P
video/MP2T
video/mp4         mp4 mpg4 m4v
video/MP4V-ES
video/mpeg          mpeg mpg mpe m1v m2v
video/mpeg4-generic
video/MPV
video/nv
video/ogg         ogv
video/parityfec
video/pointer
video/quicktime         mov qt
video/raptorfec
video/raw
video/rtp-enc-aescm128
video/rtploopback
video/rtx
video/SMPTE292M
video/ulpfec
video/vc1
video/vnd.CCTV
video/vnd.dece.hd       uvh uvvh
video/vnd.dece.mobile       uvm uvvm
video/vnd.dece.mp4        uvu uvvu
video/vnd.dece.pd       uvp uvvp
video/vnd.dece.sd       uvs uvvs
video/vnd.dece.video        uvv uvvv
video/vnd.directv.mpeg
video/vnd.directv.mpeg-tts
video/vnd.dlna.mpeg-tts
video/vnd.dvb.file        dvb
video/vnd.fvt         fvt
# rm: audio/x-pn-realaudio
video/vnd.hns.video
video/vnd.iptvforum.1dparityfec-1010
video/vnd.iptvforum.1dparityfec-2005
video/vnd.iptvforum.2dparityfec-1010
video/vnd.iptvforum.2dparityfec-2005
video/vnd.iptvforum.ttsavc
video/vnd.iptvforum.ttsmpeg2
video/vnd.motorola.video
video/vnd.motorola.videop
video/vnd.mpegurl       mxu m4u
video/vnd.ms-playready.media.pyv    pyv
video/vnd.nokia.interleaved-multimedia    nim
video/vnd.nokia.videovoip
# mp4: video/mp4
video/vnd.objectvideo
video/vnd.radgamettools.bink      bik bk2
video/vnd.radgamettools.smacker     smk
video/vnd.sealed.mpeg1        smpg s11
# smpg: video/vnd.sealed.mpeg1
video/vnd.sealed.mpeg4        s14
video/vnd.sealed.swf        sswf ssw
video/vnd.sealedmedia.softseal.mov    smov smo s1q
# uvu, uvvu: video/vnd.dece.mp4
video/vnd.uvvu.mp4
video/vnd.vivo          viv

# Non-IANA types

application/mac-compactpro      cpt
application/metalink+xml      metalink
application/owl+xml       owx
application/rss+xml       rss
application/vnd.android.package-archive   apk
application/vnd.oma.dd+xml      dd
application/vnd.oma.drm.content     dcf
# odf: application/vnd.oasis.opendocument.formula
application/vnd.oma.drm.dcf     o4a o4v
application/vnd.oma.drm.message     dm
application/vnd.oma.drm.rights+wbxml    drc
application/vnd.oma.drm.rights+xml    dr
application/vnd.sun.xml.calc      sxc
application/vnd.sun.xml.calc.template   stc
application/vnd.sun.xml.draw      sxd
application/vnd.sun.xml.draw.template   std
application/vnd.sun.xml.impress     sxi
application/vnd.sun.xml.impress.template  sti
application/vnd.sun.xml.math      sxm
application/vnd.sun.xml.writer      sxw
application/vnd.sun.xml.writer.global   sxg
application/vnd.sun.xml.writer.template   stw
application/vnd.symbian.install     sis
application/vnd.wap.mms-message     mms
application/x-annodex       anx
application/x-bcpio       bcpio
application/x-bittorrent      torrent
application/x-bzip2       bz2
application/x-cdlink        vcd
application/x-chess-pgn       pgn
application/x-chrome-extension      crx
application/x-cpio        cpio
application/x-csh       csh
application/x-director        dcr dir dxr
application/x-dvi       dvi
application/x-futuresplash      spl
application/x-gtar        gtar
application/x-hdf       hdf
application/x-java-archive      jar
application/x-java-jnlp-file      jnlp
application/x-java-pack200      pack
application/x-killustrator      kil
application/x-latex       latex
application/x-netcdf        nc cdf
application/x-perl        pl
application/x-rpm       rpm
application/x-sh        sh
application/x-shar        shar
application/x-shockwave-flash     swf
application/x-stuffit       sit
application/x-sv4cpio       sv4cpio
application/x-sv4crc        sv4crc
application/x-tar       tar
application/x-tcl       tcl
application/x-tex       tex
application/x-texinfo       texinfo texi
application/x-troff-man       man 1 2 3 4 5 6 7 8
application/x-troff-me        me
application/x-troff-ms        ms
application/x-ustar       ustar
application/x-wais-source     src
application/x-xpinstall       xpi
application/x-xspf+xml        xspf
application/x-xz        xz
audio/midi          mid midi kar
audio/x-aiff          aif aiff aifc
audio/x-annodex         axa
audio/x-flac          flac
audio/x-matroska        mka
audio/x-mod         mod ult uni m15 mtm 669 med
audio/x-mpegurl         m3u
audio/x-ms-wax          wax
audio/x-ms-wma          wma
audio/x-pn-realaudio        ram rm
audio/x-realaudio       ra
audio/x-s3m         s3m
audio/x-stm         stm
audio/x-wav         wav
chemical/x-xyz          xyz
image/bmp         bmp
image/webp          webp
image/x-cmu-raster        ras
image/x-portable-anymap       pnm
image/x-portable-bitmap       pbm
image/x-portable-graymap      pgm
image/x-portable-pixmap       ppm
image/x-rgb         rgb
image/x-targa         tga
image/x-xbitmap         xbm
image/x-xpixmap         xpm
image/x-xwindowdump       xwd
text/html-sandboxed       sandboxed
text/x-pod          pod
text/x-setext         etx
video/webm          webm
video/x-annodex         axv
video/x-flv         flv
video/x-javafx          fxm
video/x-matroska        mkv
video/x-matroska-3d       mk3d
video/x-ms-asf          asx
video/x-ms-wm         wm
video/x-ms-wmv          wmv
video/x-ms-wmx          wmx
video/x-ms-wvx          wvx
video/x-msvideo         avi
video/x-sgi-movie       movie
x-conference/x-cooltalk       ice
x-epoc/x-sisx-app       sisx
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted deps/plug/lib/plug/parsers.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
defmodule Plug.Parsers do
  defmodule RequestTooLargeError do
    @moduledoc """
    Error raised when the request is too large.
    """

    defexception message: "the request is too large. If you are willing to process " <>
                          "larger requests, please give a :length to Plug.Parsers",
                 plug_status: 413
  end

  defmodule UnsupportedMediaTypeError do
    @moduledoc """
    Error raised when the request body cannot be parsed.
    """

    defexception media_type: nil, plug_status: 415

    def message(exception) do
      "unsupported media type #{exception.media_type}"
    end
  end

  defmodule BadEncodingError do
    @moduledoc """
    Raised when the request body contains bad encoding.
    """

    defexception message: nil, plug_status: 415
  end

  defmodule ParseError do
    @moduledoc """
    Error raised when the request body is malformed.
    """

    defexception exception: nil, plug_status: 400

    def message(exception) do
      exception = exception.exception
      "malformed request, got #{inspect exception.__struct__} " <>
        "with message #{Exception.message(exception)}"
    end
  end

  @moduledoc """
  A plug for parsing the request body.

  This module also specifies a behaviour that all the parsers to be used with
  Plug should adopt.

  ## Options

    * `:parsers` - a set of modules to be invoked for parsing.
      These modules need to implement the behaviour outlined in
      this module.

    * `:pass` - an optional list of MIME type strings that are allowed
      to pass through. Any mime not handled by a parser and not explicitly
      listed in `:pass` will `raise UnsupportedMediaTypeError`. For example:

        * `["*/*"]` - never raises
        * `["text/html", "application/*"]` - doesn't raise for those values
        * `[]` - always raises (default)

  All options supported by `Plug.Conn.read_body/2` are also supported here (for
  example the `:length` option which specifies the max body length to read).

  ## Examples

      plug Plug.Parsers, parsers: [:urlencoded, :multipart]
      plug Plug.Parsers, parsers: [:urlencoded, :json],
                         pass:  ["text/*"],
                         json_decoder: Poison

  ## Built-in parsers

  Plug ships with the following parsers:

  * `Plug.Parsers.URLENCODED` - parses `application/x-www-form-urlencoded`
    requests
  * `Plug.Parsers.MULTIPART` - parses `multipart/form-data` and
    `multipart/mixed` requests
  * `Plug.Parsers.JSON` - parses `application/json` requests with the given
    `:json_decoder`

  This plug will raise `Plug.Parsers.UnsupportedMediaTypeError` by default if
  the request cannot be parsed by any of the given types and the MIME type has
  not been explicity accepted with the `:accept` option.

  `Plug.Parsers.RequestTooLargeError` will be raised if the request goes over
  the given limit.

  Parsers may raise a `Plug.Parsers.ParseError` if the request has a malformed
  body.

  ## File handling

  If a file is uploaded via any of the parsers, Plug will
  stream the uploaded contents to a file in a temporary directory in order to
  avoid loading the whole file into memory. For such, the `:plug` application
  needs to be started in order for file uploads to work. More details on how the
  uploaded file is handled can be found in the documentation for `Plug.Upload`.

  When a file is uploaded, the request parameter that identifies that file will
  be a `Plug.Upload` struct with information about the uploaded file (e.g.
  filename and content type) and about where the file is stored.

  The temporary directory where files are streamed to can be customized by
  setting the `PLUG_TMPDIR` environment variable on the host system. If
  `PLUG_TMPDIR` isn't set, Plug will look at some environment
  variables which usually hold the value of the system's temporary directory
  (like `TMPDIR` or `TMP`). If no value is found in any of those variables,
  `/tmp` is used as a default.
  """

  alias Plug.Conn
  use Behaviour

  @doc """
  Attempts to parse the connection's request body given the content-type type
  and subtype and the headers. Returns:

    * `{:ok, conn}` if the parser is able to handle the given content-type
    * `{:next, conn}` if the next parser should be invoked
    * `{:error, :too_large, conn}` if the request goes over the given limit

  """
  defcallback parse(Conn.t, type :: binary, subtype :: binary,
                    headers :: Keyword.t, opts :: Keyword.t) ::
                    {:ok, Conn.params, Conn.t} |
                    {:error, :too_large, Conn.t} |
                    {:next, Conn.t}

  @behaviour Plug
  @methods ~w(POST PUT PATCH DELETE)

  def init(opts) do
    parsers = Keyword.get(opts, :parsers) || raise_missing_parsers

    opts
    |> Keyword.put(:parsers, convert_parsers(parsers))
    |> Keyword.put_new(:length, 8_000_000)
    |> Keyword.put_new(:pass, [])
  end

  defp raise_missing_parsers do
    raise ArgumentError, "Plug.Parsers expects a set of parsers to be given in :parsers"
  end

  defp convert_parsers(parsers) do
    for parser <- parsers do
      case Atom.to_string(parser) do
        "Elixir." <> _ -> parser
        reference      -> Module.concat(Plug.Parsers, String.upcase(reference))
      end
    end
  end

  def call(%Conn{req_headers: req_headers, method: method,
                 body_params: %Plug.Conn.Unfetched{}} = conn, opts) when method in @methods do
    conn = Conn.fetch_query_params(conn)
    case List.keyfind(req_headers, "content-type", 0) do
      {"content-type", ct} ->
        case Conn.Utils.content_type(ct) do
          {:ok, type, subtype, headers} ->
            reduce(conn, Keyword.fetch!(opts, :parsers), type, subtype, headers, opts)
          :error ->
            %{conn | body_params: %{}}
        end
      nil ->
        %{conn | body_params: %{}}
    end
  end

  def call(%Conn{body_params: %Plug.Conn.Unfetched{}} = conn, _opts) do
    conn = Conn.fetch_query_params(conn)
    %{conn | body_params: %{}}
  end

  def call(%Conn{} = conn, _opts) do
    Conn.fetch_query_params(conn)
  end

  defp reduce(conn, [h|t], type, subtype, headers, opts) do
    case h.parse(conn, type, subtype, headers, opts) do
      {:ok, body, %Conn{params: %Plug.Conn.Unfetched{}, query_params: query} = conn} ->
        %{conn | body_params: body, params: query |> Map.merge(body)}
      {:ok, body, %Conn{params: params, query_params: query} = conn} ->
        %{conn | body_params: body, params: params |> Map.merge(query) |> Map.merge(body)}
      {:next, conn} ->
        reduce(conn, t, type, subtype, headers, opts)
      {:error, :too_large, _conn} ->
        raise RequestTooLargeError
    end
  end

  defp reduce(conn, [], type, subtype, _headers, opts) do
    ensure_accepted_mimes(conn, type, subtype, Keyword.fetch!(opts, :pass))
  end

  defp ensure_accepted_mimes(conn, _type, _subtype, ["*/*"]), do: conn
  defp ensure_accepted_mimes(conn, type, subtype, pass) do
    if "#{type}/#{subtype}" in pass || "#{type}/*" in pass do
      %{conn | body_params: %{}}
    else
      raise UnsupportedMediaTypeError, media_type: "#{type}/#{subtype}"
    end
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




































































































































































































































































































































































































































Deleted deps/plug/lib/plug/parsers/json.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
defmodule Plug.Parsers.JSON do
  @moduledoc """
  Parses JSON request body.

  JSON arrays are parsed into a `"_json"` key to allow
  proper param merging.

  An empty request body is parsed as an empty map.
  """

  @behaviour Plug.Parsers
  import Plug.Conn

  def parse(conn, "application", subtype, _headers, opts) do
    if subtype == "json" || String.ends_with?(subtype, "+json") do
      decoder = Keyword.get(opts, :json_decoder) ||
                  raise ArgumentError, "JSON parser expects a :json_decoder option"
      conn
      |> read_body(opts)
      |> decode(decoder)
    else
      {:next, conn}
    end
  end

  def parse(conn, _type, _subtype, _headers, _opts) do
    {:next, conn}
  end

  defp decode({:more, _, conn}, _decoder) do
    {:error, :too_large, conn}
  end

  defp decode({:ok, "", conn}, _decoder) do
    {:ok, %{}, conn}
  end

  defp decode({:ok, body, conn}, decoder) do
    case decoder.decode!(body) do
      terms when is_map(terms) ->
        {:ok, terms, conn}
      terms ->
        {:ok, %{"_json" => terms}, conn}
    end
  rescue
    e -> raise Plug.Parsers.ParseError, exception: e
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
































































































Deleted deps/plug/lib/plug/parsers/multipart.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
defmodule Plug.Parsers.MULTIPART do
  @moduledoc """
  Parses multipart request body.
  """

  @behaviour Plug.Parsers

  def parse(conn, "multipart", subtype, _headers, opts) when subtype in ["form-data", "mixed"] do
    {adapter, state} = conn.adapter

    try do
      adapter.parse_req_multipart(state, opts, &handle_headers/1)
    rescue
      e -> raise Plug.Parsers.ParseError, exception: e
    else
      {:ok, params, state} ->
        {:ok, params, %{conn | adapter: {adapter, state}}}
      {:more, _params, state} ->
        {:error, :too_large, %{conn | adapter: {adapter, state}}}
    end
  end

  def parse(conn, _type, _subtype, _headers, _opts) do
    {:next, conn}
  end

  defp handle_headers(headers) do
    case List.keyfind(headers, "content-disposition", 0) do
      {_, disposition} -> handle_disposition(disposition, headers)
      nil -> :skip
    end
  end

  defp handle_disposition(disposition, headers) do
    case :binary.split(disposition, ";") do
      [_, params] ->
        params = Plug.Conn.Utils.params(params)
        if name = Map.get(params, "name") do
          handle_disposition_params(name, params, headers)
        else
          :skip
        end
      [_] ->
        :skip
    end
  end

  defp handle_disposition_params(name, params, headers) do
    case Map.get(params, "filename") do
      nil      -> {:binary, name}
      ""       -> :skip
      filename ->
        path = Plug.Upload.random_file!("multipart")
        {:file, name, path, %Plug.Upload{filename: filename, path: path,
                                         content_type: get_header(headers, "content-type")}}
    end
  end

  defp get_header(headers, key) do
    case List.keyfind(headers, key, 0) do
      {^key, value} -> value
      nil -> nil
    end
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


































































































































Deleted deps/plug/lib/plug/parsers/urlencoded.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
defmodule Plug.Parsers.URLENCODED do
  @moduledoc """
  Parses urlencoded request body.
  """

  @behaviour Plug.Parsers
  alias Plug.Conn

  def parse(conn, "application", "x-www-form-urlencoded", _headers, opts) do
    case Conn.read_body(conn, opts) do
      {:ok, body, conn} ->
        Plug.Conn.Utils.validate_utf8!(body, "urlencoded body")
        {:ok, Plug.Conn.Query.decode(body), conn}
      {:more, _data, conn} ->
        {:error, :too_large, conn}
    end
  end

  def parse(conn, _type, _subtype, _headers, _opts) do
    {:next, conn}
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<












































Deleted deps/plug/lib/plug/request_id.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
defmodule Plug.RequestId do
  @moduledoc """
  A plug for generating a unique request id for each request. A generated
  request id will in the format "uq8hs30oafhj5vve8ji5pmp7mtopc08f".

  If a request id already exists as the "x-request-id" HTTP request header,
  then that value will be used assuming it is between 20 and 200 characters.
  If it is not, a new request id will be generated.

  The request id is added to the Logger metadata as `:request_id` and the response as
  the "x-request-id" HTTP header. To see the request id in your log output,
  configure your logger backends to include the `:request_id` metadata:

      config :logger, :console, metadata: [:request_id]

  It is recommended to include this metadata configuration in your production
  configuration file.

  To use it, just plug it into the desired module:

      plug Plug.RequestId

  ## Options

    * `:http_header` - The name of the HTTP *request* header to check for
    existing request ids. This is also the HTTP *response* header that will be
    set with the request id. Default value is "x-request-id"

        plug Plug.RequestId, http_header: "custom-request-id"
  """

  require Logger
  alias Plug.Conn
  @behaviour Plug

  def init(opts) do
    Keyword.get(opts, :http_header, "x-request-id")
  end

  def call(conn, req_id_header) do
    conn
    |> get_request_id(req_id_header)
    |> set_request_id(req_id_header)
  end

  defp get_request_id(conn, header) do
    case Conn.get_req_header(conn, header) do
      []      -> {conn, generate_request_id}
      [val|_] -> if valid_request_id?(val), do: {conn, val}, else: {conn, generate_request_id}
    end
  end

  defp set_request_id({conn, request_id}, header) do
    Logger.metadata(request_id: request_id)
    conn |> Conn.put_resp_header(header, request_id)
  end

  defp generate_request_id do
    :crypto.rand_bytes(20) |> Base.hex_encode32(case: :lower)
  end

  defp valid_request_id?(s), do: byte_size(s) in 20..200
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






























































































































Deleted deps/plug/lib/plug/router.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
defmodule Plug.Router do
  @moduledoc ~S"""
  A DSL to define a routing algorithm that works with Plug.

  It provides a set of macros to generate routes. For example:

      defmodule AppRouter do
        use Plug.Router

        plug :match
        plug :dispatch

        get "/hello" do
          send_resp(conn, 200, "world")
        end

        match _ do
          send_resp(conn, 404, "oops")
        end
      end

  Each route needs to return a connection, as per the Plug spec.
  A catch-all `match` is recommended to be defined as in the example
  above, otherwise routing fails with a function clause error.

  The router is itself a plug, which means it can be invoked as:

      AppRouter.call(conn, AppRouter.init([]))

  Notice the router contains a plug pipeline and by default it requires
  two plugs: `match` and `dispatch`. `match` is responsible for
  finding a matching route which is then forwarded to `dispatch`.
  This means users can easily hook into the router mechanism and add
  behaviour before match, before dispatch or after both.

  To specify private options on `match` that can be used by plugs 
  before `dispatch` pass an option with key `:private` containing a map.
  Example:

      get "/hello", private: %{an_option: :a_value} do
        send_resp(conn, 200, "world")
      end

  These options are assigned to `:private` in the call's `Plug.Conn`.

  ## Routes

      get "/hello" do
        send_resp(conn, 200, "world")
      end

  In the example above, a request will only match if it is a `GET` request and
  the route is "/hello". The supported HTTP methods are `get`, `post`, `put`,
  `patch`, `delete` and `options`.

  A route can also specify parameters which will then be
  available in the function body:

      get "/hello/:name" do
        send_resp(conn, 200, "hello #{name}")
      end

  Routes allow for globbing which will match the remaining parts
  of a route and can be available as a parameter in the function
  body. Also note that a glob can't be followed by other segments:

      get "/hello/*_rest" do
        send_resp(conn, 200, "matches all routes starting with /hello")
      end

      get "/hello/*glob" do
        send_resp(conn, 200, "route after /hello: #{inspect glob}")
      end

  Finally, a general `match` function is also supported:

      match "/hello" do
        send_resp(conn, 200, "world")
      end

  A `match` will match any route regardless of the HTTP method.
  Check `match/3` for more information on how route compilation
  works and a list of supported options.

  ## Error handling

  In case something goes wrong in a request, the router by default
  will crash, without returning any response to the client. This
  behaviour can be configured in two ways, by using two different
  modules:

  * `Plug.ErrorHandler` - allows the developer to customize exactly
    which page is sent to the client via the `handle_errors/2` function;

  * `Plug.Debugger` - automatically shows debugging and request information
    about the failure. This module is recommended to be used only in a
    development environment.

  Here is an example of how both modules could be used in an application:

      defmodule AppRouter do
        use Plug.Router

        if Mix.env == :dev do
          use Plug.Debugger
        end

        use Plug.ErrorHandler

        plug :match
        plug :dispatch

        get "/hello" do
          send_resp(conn, 200, "world")
        end

        defp handle_errors(conn, %{kind: _kind, reason: _reason, stack: _stack}) do
          send_resp(conn, conn.status, "Something went wrong")
        end
      end

  ## Routes compilation

  All routes are compiled to a match function that receives
  three arguments: the method, the request path split on `/`
  and the connection. Consider this example:

      match "/foo/bar", via: :get do
        send_resp(conn, 200, "hello world")
      end

  It is compiled to:

      defp match("GET", ["foo", "bar"], conn) do
        send_resp(conn, 200, "hello world")
      end

  This opens up a few possibilities. First, guards can be given
  to `match`:

      match "/foo/:bar" when size(bar) <= 3, via: :get do
        send_resp(conn, 200, "hello world")
      end

  Second, a list of split paths (which is the compiled result) is
  also allowed:

      match ["foo", bar], via: :get do
        send_resp(conn, 200, "hello world")
      end

  After a match is found, the block given as `do/end` is stored
  as a function in the connection. This function is then retrieved
  and invoked in the `dispatch` plug.

  ## Options

  When used, the following options are accepted by `Plug.Router`:

    * `:log_on_halt` - accepts the level to log whenever the request is halted
  """

  @doc false
  defmacro __using__(opts) do
    quote location: :keep do
      import Plug.Router
      @before_compile Plug.Router

      use Plug.Builder, unquote(opts)

      defp match(conn, _opts) do
        do_match(conn, conn.method, Enum.map(conn.path_info, &URI.decode/1), conn.host)
      end

      defp dispatch(%Plug.Conn{assigns: assigns} = conn, _opts) do
        Map.get(conn.private, :plug_route).(conn)
      end

      defoverridable [match: 2, dispatch: 2]
    end
  end

  @doc false
  defmacro __before_compile__(_env) do
    quote do
      import Plug.Router, only: []
    end
  end

  ## Match

  @doc """
  Main API to define routes.

  It accepts an expression representing the path and many options
  allowing the match to be configured.

  ## Examples

      match "/foo/bar", via: :get do
        send_resp(conn, 200, "hello world")
      end

  ## Options

  `match/3` and the other route macros accept the following options:

    * `:host` - the host which the route should match. Defaults to `nil`,
      meaning no host match, but can be a string like "example.com" or a
      string ending with ".", like "subdomain." for a subdomain match.

    * `:via` - matches the route against some specific HTTP method (specified as
      an atom, like `:get` or `:put`.

    * `:do` - contains the implementation to be invoked in case
      the route matches.

  """
  defmacro match(path, options, contents \\ []) do
    compile(nil, path, options, contents)
  end

  @doc """
  Dispatches to the path only if the request is a GET request.
  See `match/3` for more examples.
  """
  defmacro get(path, options, contents \\ []) do
    compile(:get, path, options, contents)
  end

  @doc """
  Dispatches to the path only if the request is a POST request.
  See `match/3` for more examples.
  """
  defmacro post(path, options, contents \\ []) do
    compile(:post, path, options, contents)
  end

  @doc """
  Dispatches to the path only if the request is a PUT request.
  See `match/3` for more examples.
  """
  defmacro put(path, options, contents \\ []) do
    compile(:put, path, options, contents)
  end

  @doc """
  Dispatches to the path only if the request is a PATCH request.
  See `match/3` for more examples.
  """
  defmacro patch(path, options, contents \\ []) do
    compile(:patch, path, options, contents)
  end

  @doc """
  Dispatches to the path only if the request is a DELETE request.
  See `match/3` for more examples.
  """
  defmacro delete(path, options, contents \\ []) do
    compile(:delete, path, options, contents)
  end

  @doc """
  Dispatches to the path only if the request is an OPTIONS request.
  See `match/3` for more examples.
  """
  defmacro options(path, options, contents \\ []) do
    compile(:options, path, options, contents)
  end

  @doc """
  Forwards requests to another Plug. The `path_info` of the forwarded
  connection will exclude the portion of the path specified in the
  call to `forward`.

  ## Options

  `forward` accepts the following options:

    * `:to` - a Plug the requests will be forwarded to.
    * `:host` - a string representing the host or subdomain, exactly like in
      `match/3`.

  All remaining options are passed to the target plug.

  ## Examples

      forward "/users", to: UserRouter

  Assuming the above code, a request to `/users/sign_in` will be forwarded to
  the `UserRouter` plug, which will receive what it will see as a request to
  `/sign_in`.

  Some other examples:

      forward "/foo/bar", to: :foo_bar_plug, host: "foobar."
      forward "/api", to: ApiRouter, plug_specific_option: true
  """
  defmacro forward(path, options) when is_binary(path) do
    quote bind_quoted: [path: path, options: options] do
      {target, options}       = Keyword.pop(options, :to)
      {options, plug_options} = Keyword.split(options, [:host, :private])

      if is_nil(target) or !is_atom(target) do
        raise ArgumentError, message: "expected :to to be an alias or an atom"
      end

      @plug_forward_target target
      @plug_forward_opts   target.init(plug_options)

      # Delegate the matching to the match/3 macro along with the options
      # specified by Keyword.split/2.
      match path <> "/*glob", options do
        Plug.Router.Utils.forward(
          var!(conn),
          var!(glob),
          @plug_forward_target,
          @plug_forward_opts
        )
      end
    end
  end

  ## Match Helpers

  @doc false
  def __route__(method, path, guards, options) do
    {method, guards} = build_methods(List.wrap(method || options[:via]), guards)
    {_vars, match}   = Plug.Router.Utils.build_path_match(path)
    private    = extract_private_merger(options)
    host_match = Plug.Router.Utils.build_host_match(options[:host])
    {quote(do: conn), method, match, host_match, guards, private}
  end

  # Entry point for both forward and match that is actually
  # responsible to compile the route.
  defp compile(method, expr, options, contents) do
    {body, options} =
      cond do
        b = contents[:do] ->
          {b, options}
        options[:do] ->
          Keyword.pop(options, :do)
        true ->
          raise ArgumentError, message: "expected :do to be given as option"
      end

    {path, guards} = extract_path_and_guards(expr)

    quote bind_quoted: [method: method,
                        path: path,
                        options: options,
                        guards: Macro.escape(guards, unquote: true),
                        body: Macro.escape(body, unquote: true)] do
      route = Plug.Router.__route__(method, path, guards, options)
      {conn, method, match, host, guards, private} = route

      defp do_match(unquote(conn), unquote(method), unquote(match), unquote(host)) when unquote(guards) do
        unquote(private)
        Plug.Conn.put_private(unquote(conn), :plug_route, fn var!(conn) -> unquote(body) end)
      end
    end
  end

  defp extract_private_merger(options) when is_list(options) do
    if private = Keyword.get(options, :private) do
      quote do
        conn = update_in conn.private, &Map.merge(&1, unquote(Macro.escape(private)))
      end
    end
  end

  # Convert the verbs given with `:via` into a variable and guard set that can
  # be added to the dispatch clause.
  defp build_methods([], guards) do
    {quote(do: _), guards}
  end

  defp build_methods([method], guards) do
    {Plug.Router.Utils.normalize_method(method), guards}
  end

  defp build_methods(methods, guards) do
    methods = Enum.map methods, &Plug.Router.Utils.normalize_method(&1)
    var     = quote do: method
    guards  = join_guards(quote(do: unquote(var) in unquote(methods)), guards)
    {var, guards}
  end

  defp join_guards(fst, true), do: fst
  defp join_guards(fst, snd),  do: (quote do: unquote(fst) and unquote(snd))

  # Extract the path and guards from the path.
  defp extract_path_and_guards({:when, _, [path, guards]}), do: {extract_path(path), guards}
  defp extract_path_and_guards(path), do: {extract_path(path), true}

  defp extract_path({:_, _, var}) when is_atom(var), do: "/*_path"
  defp extract_path(path), do: path
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






























































































































































































































































































































































































































































































































































































































































































































































































































Deleted deps/plug/lib/plug/router/utils.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
defmodule Plug.Router.InvalidSpecError do
  defexception message: "invalid route specification"
end

defmodule Plug.Router.Utils do
  @moduledoc false

  @doc """
  Converts a given method to its connection representation.

  The request method is stored in the `Plug.Conn` struct as an uppercase string
  (like `"GET"` or `"POST"`). This function converts `method` to that
  representation.

  ## Examples

      iex> Plug.Router.Utils.normalize_method(:get)
      "GET"

  """
  def normalize_method(method) do
    method |> to_string |> String.upcase
  end

  @doc ~S"""
  Builds the pattern that will be used to match against the request's host
  (provided via the `:host`) option.

  If `host` is `nil`, a wildcard match (`_`) will be returned. If `host` ends
  with a dot, a match like `"host." <> _` will be returned.

  ## Examples

      iex> Plug.Router.Utils.build_host_match(nil)
      {:_, [], Plug.Router.Utils}

      iex> Plug.Router.Utils.build_host_match("foo.com")
      "foo.com"

      iex> Plug.Router.Utils.build_host_match("api.") |> Macro.to_string
      "\"api.\" <> _"

  """
  def build_host_match(host) do
    cond do
      is_nil host              -> quote do: _
      String.last(host) == "." -> quote do: unquote(host) <> _
      is_binary host           -> host
    end
  end

  @doc """
  Generates a representation that will only match routes
  according to the given `spec`.

  If a non-binary spec is given, it is assumed to be
  custom match arguments and they are simply returned.

  ## Examples

      iex> Plug.Router.Utils.build_path_match("/foo/:id")
      {[:id], ["foo", {:id, [], nil}]}

  """
  def build_path_match(spec, context \\ nil) when is_binary(spec) do
    build_path_match split(spec), context, [], []
  end

  @doc """
  Forwards requests to another Plug at a new path.
  """
  def forward(%Plug.Conn{path_info: path, script_name: script} = conn, new_path, target, opts) do
    {base, ^new_path} = Enum.split(path, length(path) - length(new_path))
    conn = %{conn | path_info: new_path, script_name: script ++ base} |> target.call(opts)
    %{conn | path_info: path, script_name: script}
  end

  @doc """
  Splits the given path into several segments.
  It ignores both leading and trailing slashes in the path.

  ## Examples

      iex> Plug.Router.Utils.split("/foo/bar")
      ["foo", "bar"]

      iex> Plug.Router.Utils.split("/:id/*")
      [":id", "*"]

      iex> Plug.Router.Utils.split("/foo//*_bar")
      ["foo", "*_bar"]

  """
  def split(bin) do
    for segment <- String.split(bin, "/"), segment != "", do: segment
  end

  ## Helpers

  # Loops each segment checking for matches.

  defp build_path_match([h|t], context, vars, acc) do
    handle_segment_match segment_match(h, "", context), t, context, vars, acc
  end

  defp build_path_match([], _context, vars, acc) do
    {vars |> Enum.uniq |> Enum.reverse, Enum.reverse(acc)}
  end

  # Handle each segment match. They can either be a
  # :literal ("foo"), an :identifier (":bar") or a :glob ("*path")

  defp handle_segment_match({:literal, literal}, t, context, vars, acc) do
    build_path_match t, context, vars, [literal|acc]
  end

  defp handle_segment_match({:identifier, identifier, expr}, t, context, vars, acc) do
    build_path_match t, context, [identifier|vars], [expr|acc]
  end

  defp handle_segment_match({:glob, _identifier, _expr}, t, _context, _vars, _acc) when t != [] do
    raise Plug.Router.InvalidSpecError,
      message: "cannot have a *glob followed by other segments"
  end

  defp handle_segment_match({:glob, identifier, expr}, _t, context, vars, [hs|ts]) do
    acc = [{:|, [], [hs, expr]} | ts]
    build_path_match([], context, [identifier|vars], acc)
  end

  defp handle_segment_match({:glob, identifier, expr}, _t, context, vars, _) do
    {vars, expr} = build_path_match([], context, [identifier|vars], [expr])
    {vars, hd(expr)}
  end

  # In a given segment, checks if there is a match.

  defp segment_match(":" <> argument, buffer, context) do
    identifier = binary_to_identifier(":", argument)
    expr = quote_if_buffer identifier, buffer, context, fn var ->
      quote do: unquote(buffer) <> unquote(var)
    end
    {:identifier, identifier, expr}
  end

  defp segment_match("*" <> argument, buffer, context) do
    underscore = {:_, [], context}
    identifier = binary_to_identifier("*", argument)
    expr = quote_if_buffer identifier, buffer, context, fn var ->
      quote do: [unquote(buffer) <> unquote(underscore)|unquote(underscore)] = unquote(var)
    end
    {:glob, identifier, expr}
  end

  defp segment_match(<<h, t::binary>>, buffer, context) do
    segment_match t, buffer <> <<h>>, context
  end

  defp segment_match(<<>>, buffer, _context) do
    {:literal, buffer}
  end

  defp quote_if_buffer(identifier, "", context, _fun) do
    {identifier, [], context}
  end

  defp quote_if_buffer(identifier, _buffer, context, fun) do
    fun.({identifier, [], context})
  end

  defp binary_to_identifier(prefix, <<letter, _::binary>> = binary)
      when letter in ?a..?z or letter == ?_ do
    if binary =~ ~r/^\w+$/ do
      String.to_atom(binary)
    else
      raise Plug.Router.InvalidSpecError,
        message: "#{prefix}identifier in routes must be made of letters, numbers and underscore"
    end
  end

  defp binary_to_identifier(prefix, _) do
    raise Plug.Router.InvalidSpecError,
      message: "#{prefix} in routes must be followed by lowercase letters or underscore"
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


















































































































































































































































































































































































Deleted deps/plug/lib/plug/session.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
defmodule Plug.Session do
  @moduledoc """
  A plug to handle session cookies and session stores.

  The session is accessed via functions on `Plug.Conn`. Cookies and
  session have to be fetched with `Plug.Conn.fetch_session/1` before the
  session can be accessed.

  Consider using `Plug.CSRFProtection` when using `Plug.Session`.

  ## Session stores

  See `Plug.Session.Store` for the specification session stores are required to
  implement.

  Plug ships with the following session stores:

    * `Plug.Session.ETS`
    * `Plug.Session.COOKIE`

  ## Options

    * `:store` - session store module (required);
    * `:key` - session cookie key (required);
    * `:domain` - see `Plug.Conn.put_resp_cookie/4`;
    * `:max_age` - see `Plug.Conn.put_resp_cookie/4`;
    * `:path` - see `Plug.Conn.put_resp_cookie/4`;
    * `:secure` - see `Plug.Conn.put_resp_cookie/4`;

  Additional options can be given to the session store, see the store's
  documentation for the options it accepts.

  ## Examples

      plug Plug.Session, store: :ets, key: "_my_app_session", table: :session
  """

  alias Plug.Conn
  @behaviour Plug

  @cookie_opts [:domain, :max_age, :path, :secure]

  def init(opts) do
    store        = Keyword.fetch!(opts, :store) |> convert_store
    key          = Keyword.fetch!(opts, :key)
    cookie_opts  = Keyword.take(opts, @cookie_opts)
    store_opts   = Keyword.drop(opts, [:store, :key] ++ @cookie_opts)
    store_config = store.init(store_opts)

    %{store: store,
      store_config: store_config,
      key: key,
      cookie_opts: cookie_opts}
  end

  def call(conn, config) do
    Conn.put_private(conn, :plug_session_fetch, fetch_session(config))
  end

  defp convert_store(store) do
    case Atom.to_string(store) do
      "Elixir." <> _ -> store
      reference      -> Module.concat(Plug.Session, String.upcase(reference))
    end
  end

  defp fetch_session(config) do
    %{store: store, store_config: store_config, key: key} = config

    fn conn ->
      {sid, session} =
        if cookie = conn.cookies[key] do
          store.get(conn, cookie, store_config)
        else
          {nil, %{}}
        end

      conn
      |> Conn.put_private(:plug_session, session)
      |> Conn.put_private(:plug_session_fetch, :done)
      |> Conn.register_before_send(before_send(sid, config))
    end
  end

  defp before_send(sid, config) do
    fn conn ->
      case Map.get(conn.private, :plug_session_info) do
        :write ->
          value = put_session(sid, conn, config)
          put_cookie(value, conn, config)
        :drop ->
          if sid do
            delete_session(sid, conn, config)
            delete_cookie(conn, config)
          else
            conn
          end
        :renew ->
          if sid, do: delete_session(sid, conn, config)
          value = put_session(nil, conn, config)
          put_cookie(value, conn, config)
        :ignore ->
          conn
        nil ->
          conn
      end
    end
  end

  defp put_session(sid, conn, %{store: store, store_config: store_config}),
    do: store.put(conn, sid, conn.private[:plug_session], store_config)

  defp delete_session(sid, conn, %{store: store, store_config: store_config}),
    do: store.delete(conn, sid, store_config)

  defp put_cookie(value, conn, %{cookie_opts: cookie_opts, key: key}),
    do: Conn.put_resp_cookie(conn, key, value, cookie_opts)

  defp delete_cookie(conn, %{cookie_opts: cookie_opts, key: key}),
    do: Conn.delete_resp_cookie(conn, key, cookie_opts)
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


















































































































































































































































Deleted deps/plug/lib/plug/session/cookie.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
defmodule Plug.Session.COOKIE do
  @moduledoc """
  Stores the session in a cookie.

  This cookie store is based on `Plug.Crypto.MessageVerifier`
  and `Plug.Crypto.Message.Encryptor` which encrypts and signs
  each cookie to ensure they can't be read nor tampered with.

  Since this store uses crypto features, it requires you to
  set the `:secret_key_base` field in your connection. This
  can be easily achieved with a plug:

      plug :put_secret_key_base

      def put_secret_key_base(conn, _) do
        put_in conn.secret_key_base, "-- LONG STRING WITH AT LEAST 64 BYTES --"
      end

  ## Options

    * `:encryption_salt` - a salt used with `conn.secret_key_base` to generate
      a key for encrypting/decrypting a cookie.

    * `:signing_salt` - a salt used with `conn.secret_key_base` to generate a
      key for signing/verifying a cookie;

    * `:key_iterations` - option passed to `Plug.Crypto.KeyGenerator`
      when generating the encryption and signing keys. Defaults to 1000;

    * `:key_length` - option passed to `Plug.Crypto.KeyGenerator`
      when generating the encryption and signing keys. Defaults to 32;

    * `:key_digest` - option passed to `Plug.Crypto.KeyGenerator`
      when generating the encryption and signing keys. Defaults to `:sha256';

    * `:serializer` - cookie serializer module that defines `encode/1` and
      `decode/1` returning an `{:ok, value}` tuple. Defaults to
      `:external_term_format`.

  ## Examples

      # Use the session plug with the table name
      plug Plug.Session, store: :cookie,
                         key: "_my_app_session",
                         encryption_salt: "cookie store encryption salt",
                         signing_salt: "cookie store signing salt",
                         key_length: 64
  """

  @behaviour Plug.Session.Store

  alias Plug.Crypto.KeyGenerator
  alias Plug.Crypto.MessageVerifier
  alias Plug.Crypto.MessageEncryptor

  def init(opts) do
    encryption_salt = opts[:encryption_salt]
    signing_salt = check_signing_salt(opts)

    iterations = Keyword.get(opts, :key_iterations, 1000)
    length = Keyword.get(opts, :key_length, 32)
    digest = Keyword.get(opts, :key_digest, :sha256)
    key_opts = [iterations: iterations,
                length: length,
                digest: digest,
                cache: Plug.Keys]

    serializer = check_serializer(opts[:serializer] || :external_term_format)

    %{encryption_salt: encryption_salt,
      signing_salt: signing_salt,
      key_opts: key_opts,
      serializer: serializer}
  end

  def get(conn, cookie, opts) do
    key_opts = opts.key_opts
    if key = opts.encryption_salt do
      MessageEncryptor.verify_and_decrypt(cookie,
                                          derive(conn, key, key_opts),
                                          derive(conn, opts.signing_salt, key_opts))
    else
      MessageVerifier.verify(cookie, derive(conn, opts.signing_salt, key_opts))
    end |> decode(opts.serializer)
  end

  def put(conn, _sid, term, opts) do
    binary = encode(term, opts.serializer)
    key_opts = opts.key_opts
    if key = opts.encryption_salt do
      MessageEncryptor.encrypt_and_sign(binary,
                                        derive(conn, key, key_opts),
                                        derive(conn, opts.signing_salt, key_opts))
    else
      MessageVerifier.sign(binary, derive(conn, opts.signing_salt, key_opts))
    end
  end

  def delete(_conn, _sid, _opts) do
    :ok
  end

  defp encode(term, :external_term_format) do
    :erlang.term_to_binary(term)
  end

  defp encode(term, serializer) do
    {:ok, binary} = serializer.encode(term)
    binary
  end

  defp decode({:ok, binary}, :external_term_format) do
    {:term,
      try do
        :erlang.binary_to_term(binary)
      rescue
        _ -> %{}
      end}
  end

  defp decode({:ok, binary}, serializer) do
    case serializer.decode(binary) do
      {:ok, term} -> {:custom, term}
      _           -> {:custom, %{}}
    end
  end

  defp decode(:error, _serializer) do
    {nil, %{}}
  end

  defp derive(conn, key, key_opts) do
    conn.secret_key_base
    |> validate_secret_key_base()
    |> KeyGenerator.generate(key, key_opts)
  end

  defp validate_secret_key_base(nil), do:
    raise(ArgumentError, "cookie store expects conn.secret_key_base to be set")
  defp validate_secret_key_base(secret_key_base) when byte_size(secret_key_base) < 64, do:
    raise(ArgumentError, "cookie store expects conn.secret_key_base to be at least 64 bytes")
  defp validate_secret_key_base(secret_key_base), do:
    secret_key_base

  defp check_signing_salt(opts) do
    case opts[:signing_salt] do
      nil  -> raise ArgumentError, "cookie store expects :signing_salt as option"
      salt -> salt
    end
  end

  defp check_serializer(serializer) when is_atom(serializer), do: serializer
  defp check_serializer(_), do:
    raise(ArgumentError, "cookie store expects :serializer option to be a module")
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






















































































































































































































































































































Deleted deps/plug/lib/plug/session/ets.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
defmodule Plug.Session.ETS do
  @moduledoc """
  Stores the session in an in-memory ETS table.

  This store does not create the ETS table; it expects that an
  existing named table with public properties is passed as an
  argument.

  We don't recommend using this store in production as every
  session will be stored in ETS and never cleaned until you
  create a task responsible for cleaning up old entries.

  Also, since the store is in-memory, it means sessions are
  not shared between servers. If you deploy to more than one
  machine, using this store is again not recommended.

  This store, however, can be used as an example for creating
  custom storages, based on Redis, Memcached, or a database
  itself.

  ## Options

    * `:table` - ETS table name (required)

  For more information on ETS tables, visit the Erlang documentation at
  http://www.erlang.org/doc/man/ets.html.

  ## Storage

  The data is stored in ETS in the following format:

      {sid :: String.t, data :: map, timestamp :: :erlang.timestamp}

  The timestamp is updated whenever there is a read or write to the
  table and it may be used to detect if a session is still active.

  ## Examples

      # Create an ETS table when the application starts
      :ets.new(:session, [:named_table, :public, read_concurrency: true])

      # Use the session plug with the table name
      plug Plug.Session, store: :ets, key: "sid", table: :session

  """

  @behaviour Plug.Session.Store

  @max_tries 100

  def init(opts) do
    Keyword.fetch!(opts, :table)
  end

  def get(_conn, sid, table) do
    case :ets.lookup(table, sid) do
      [{^sid, data, _timestamp}] ->
        :ets.update_element(table, sid, {3, now()})
        {sid, data}
      [] ->
        {nil, %{}}
    end
  end

  def put(_conn, nil, data, table) do
    put_new(data, table)
  end

  def put(_conn, sid, data, table) do
    :ets.insert(table, {sid, data, now()})
    sid
  end

  def delete(_conn, sid, table) do
    :ets.delete(table, sid)
    :ok
  end

  defp put_new(data, table, counter \\ 0)
      when counter < @max_tries do
    sid = :crypto.strong_rand_bytes(96) |> Base.encode64

    if :ets.insert_new(table, {sid, data, now()}) do
      sid
    else
      put_new(data, table, counter + 1)
    end
  end

  defp now() do
    :os.timestamp()
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


























































































































































































Deleted deps/plug/lib/plug/session/store.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
defmodule Plug.Session.Store do
  @moduledoc """
  Specification for session stores.
  """

  use Behaviour

  @type sid :: term | nil
  @type cookie :: binary
  @type session :: map

  @doc """
  Initializes the store.

  The options returned from this function will be given
  to `get/3`, `put/4` and `delete/3`.
  """
  defcallback init(Plug.opts) :: Plug.opts

  @doc """
  Parses the given cookie.

  Returns a session id and the session contents. The session id is any
  value that can be used to identify the session by the store.

  The session id may be nil in case the cookie does not identify any
  value in the store. The session contents must be a map.
  """
  defcallback get(Plug.Conn.t, cookie, Plug.opts) :: {sid, session}

  @doc """
  Stores the session associated with given session id.

  If `nil` is given as id, a new session id should be
  generated and returned.
  """
  defcallback put(Plug.Conn.t, sid, any, Plug.opts) :: cookie

  @doc """
  Removes the session associated with given session id from the store.
  """
  defcallback delete(Plug.Conn.t, sid, Plug.opts) :: :ok
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






















































































Deleted deps/plug/lib/plug/ssl.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
defmodule Plug.SSL do
  @moduledoc """
  A plug to force SSL connections.

  If the scheme of a request is https, it'll add a `strict-transport-security`
  header to enable HTTP Strict Transport Security.

  Otherwise, the request will be redirected to a corresponding location
  with the `https` scheme by setting the `location` header of the response.
  The status code will be 301 if the method of `conn` is `GET` or `HEAD`,
  or 307 in other situations.

  ## x-forwaded-proto

  If your Plug application is behind a proxy that handles HTTPS, you will
  need to tell Plug to parse the proper protocol from the "x-forwarded-proto"
  header. This can be done using the `:rewrite_on` option:

      use Plug.SSL, rewrite_on: [:x_forwarded_proto]

  The command above will effectively change the value of `conn.scheme` by
  the one sent in "x-forwarded-proto".

  Since rewriting the scheme based on "x-forwarded-proto" can open up
  security vulnerabilities, only provide the option above if:

      * Your app is behind a proxy
      * Your proxy strips "x-forwarded-proto" headers from all incoming requests
      * Your proxy sets the "x-forwarded-proto" and sends it to Plug

  ## Options

    * `:rewrite_on` - rewrites the scheme to https based on the given headers
    * `:hsts` - a boolean on enabling HSTS or not, defaults to true.
    * `:expires` - seconds to expires for HSTS, defaults to 31536000 (a year).
    * `:subdomains` - a boolean on including subdomains or not in HSTS,
      defaults to false.
    * `:host` - a new host to redirect to if the request's scheme is `http`.

  ## Port

  It is not possible to directly configure the port in `Plug.SSL` because
  HSTS expects the port to be 443 for SSL. If you are not using HSTS and
  wants to redirect to HTTPS on another port, you can sneak it alongside
  the host, for example: `host: "example.com:443"`.
  """
  @behaviour Plug

  import Plug.Conn
  alias Plug.Conn

  def init(opts) do
    {hsts_header(opts), Keyword.get(opts, :host), Keyword.get(opts, :rewrite_on, [])}
  end

  def call(conn, {hsts, host, rewrites}) do
    conn = rewrite_on(conn, rewrites)
    if conn.scheme == :https do
      put_hsts_header(conn, hsts)
    else
      redirect_to_https(conn, host)
    end
  end

  defp rewrite_on(conn, rewrites) do
    Enum.reduce rewrites, conn, fn
      :x_forwarded_proto, acc ->
        if get_req_header(acc, "x-forwarded-proto") == ["https"] do
          %{acc | scheme: :https}
        else
          acc
        end
      other, _acc ->
        raise "unknown rewrite: #{inspect other}"
    end
  end

  # http://tools.ietf.org/html/draft-hodges-strict-transport-sec-02
  defp hsts_header(opts) do
    if Keyword.get(opts, :hsts, true) do
      expires    = Keyword.get(opts, :expires, 31536000)
      subdomains = Keyword.get(opts, :subdomains, false)

      "max-age=#{expires}" <>
        if(subdomains, do: "; includeSubDomains", else: "")
    end
  end

  defp put_hsts_header(conn, hsts_header) when is_binary(hsts_header) do
    put_resp_header(conn, "strict-transport-security", hsts_header)
  end
  defp put_hsts_header(conn, _), do: conn

  defp redirect_to_https(%Conn{host: host} = conn, custom_host) do
    status = if conn.method in ~w(HEAD GET), do: 301, else: 307

    location = "https://" <> (custom_host || host) <>
                             conn.request_path <> qs(conn.query_string)

    conn
    |> put_resp_header("location", location)
    |> send_resp(status, "")
    |> halt
  end

  defp qs(""), do: ""
  defp qs(qs), do: "?" <> qs
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
























































































































































































































Deleted deps/plug/lib/plug/static.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
defmodule Plug.Static do
  @moduledoc """
  A plug for serving static assets.

  It requires two options on initialization:

    * `:at` - the request path to reach for static assets.
      It must be a string.

    * `:from` - the filesystem path to read static assets from.
      It must be a string, containing a file system path, an
      atom representing the application name, where assets will
      be served from the priv/static, or a tuple containing the
      application name and directory to serve them besides
      priv/static.

  The preferred form is to use `:from` with an atom or tuple,
  since it will make your application independent from the
  starting directory.

  If a static asset cannot be found, `Plug.Static` simply forwards
  the connection to the rest of the pipeline.

  ## Cache mechanisms

  `Plug.Static` uses etags for HTTP caching. This means browsers/clients
  should cache assets on the first request and validate the cache on
  following requests, not downloading the static asset once again if it
  has not changed. The cache-control for etags is specified by the
  `cache_control_for_etags` option and defaults to "public".

  However, `Plug.Static` also supports direct cache control by using
  versioned query strings. If the request query string starts with
  "?vsn=", `Plug.Static` assumes the application is versioning assets
  and does not set the `ETag` header, meaning the cache behaviour will
  be specified solely by the `cache_control_for_vsn_requests` config,
  which defaults to "public, max-age=31536000".

  ## Options

    * `:gzip` - given a request for `FILE`, serves `FILE.gz` if it exists
      in the static directory and if the `accept-encoding` header is set
      to allow gzipped content (defaults to `false`).

    * `:cache_control_for_etags` - sets the cache header for requests
      that use etags. Defaults to `"public"`.

    * `:cache_control_for_vsn_requests` - sets the cache header for
      requests starting with "?vsn=" in the query string. Defaults to
      `"public, max-age=31536000"`.

    * `:only` - filters which paths to look up. This is useful to avoid
      file system traversals on every request when this plug is mounted
      at `"/"`. Defaults to `nil` (no filtering).

    * `:headers` - other headers to be set when serving static assets.

  ## Examples

  This plug can be mounted in a `Plug.Builder` pipeline as follows:

      defmodule MyPlug do
        use Plug.Builder

        plug Plug.Static, at: "/public", from: :my_app
        plug :not_found

        def not_found(conn, _) do
          send_resp(conn, 404, "not found")
        end
      end

  """

  @behaviour Plug
  @allowed_methods ~w(GET HEAD)

  import Plug.Conn
  alias Plug.Conn

  # In this module, the `:prim_info` Erlang module along with the `:file_info`
  # record are used instead of the more common and Elixir-y `File` module and
  # `File.Stat` struct, respectively. The reason behind this is performance: all
  # the `File` operations pass through a single process in order to support node
  # operations that we simply don't need when serving assets.

  require Record
  Record.defrecordp :file_info, Record.extract(:file_info, from_lib: "kernel/include/file.hrl")

  defmodule InvalidPathError do
    defexception message: "invalid path for static asset", plug_status: 400
  end

  def init(opts) do
    at    = Keyword.fetch!(opts, :at)
    from  = Keyword.fetch!(opts, :from)
    gzip  = Keyword.get(opts, :gzip, false)
    only  = Keyword.get(opts, :only, nil)

    qs_cache = Keyword.get(opts, :cache_control_for_vsn_requests, "public, max-age=31536000")
    et_cache = Keyword.get(opts, :cache_control_for_etags, "public")
    headers  = Keyword.get(opts, :headers, %{})

    from =
      case from do
        {_, _} -> from
        _ when is_atom(from) -> {from, "priv/static"}
        _ when is_binary(from) -> from
        _ -> raise ArgumentError, ":from must be an atom, a binary or a tuple"
      end

    {Plug.Router.Utils.split(at), from, gzip, qs_cache, et_cache, only, headers}
  end

  def call(conn = %Conn{method: meth}, {at, from, gzip, qs_cache, et_cache, only, headers})
      when meth in @allowed_methods do
    # subset/2 returns the segments in `conn.path_info` without the
    # segments at the beginning that are shared with `at`.
    segments = subset(at, conn.path_info) |> Enum.map(&URI.decode/1)

    cond do
      not allowed?(only, segments) ->
        conn
      invalid_path?(segments) ->
        raise InvalidPathError
      true ->
        path = path(from, segments)
        serve_static(file_encoding(conn, path, gzip), segments, gzip, qs_cache, et_cache, headers)
    end
  end

  def call(conn, _opts) do
    conn
  end

  defp allowed?(_only, []),   do: false
  defp allowed?(nil, _list),  do: true
  defp allowed?(only, [h|_]), do: h in only

  defp serve_static({:ok, conn, file_info, path}, segments, gzip, qs_cache, et_cache, headers) do
    case put_cache_header(conn, qs_cache, et_cache, file_info) do
      {:stale, conn} ->
        content_type = segments |> List.last |> Plug.MIME.path

        conn
        |> maybe_add_vary(gzip)
        |> put_resp_header("content-type", content_type)
        |> merge_resp_headers(headers)
        |> send_file(200, path)
        |> halt
      {:fresh, conn} ->
        conn
        |> send_resp(304, "")
        |> halt
    end
  end

  defp serve_static({:error, conn}, _segments, _gzip, _qs_cache, _et_cache, _headers) do
    conn
  end

  defp maybe_add_vary(conn, true) do
    # If we serve gzip at any moment, we need to set the proper vary
    # header regardless of whether we are serving gzip content right now.
    # See: http://www.fastly.com/blog/best-practices-for-using-the-vary-header/
    update_in conn.resp_headers, &[{"vary", "Accept-Encoding"}|&1]
  end

  defp maybe_add_vary(conn, false) do
    conn
  end

  defp put_cache_header(%Conn{query_string: "vsn=" <> _} = conn, qs_cache, _et_cache, _file_info)
      when is_binary(qs_cache) do
    {:stale, put_resp_header(conn, "cache-control", qs_cache)}
  end

  defp put_cache_header(conn, _qs_cache, et_cache, file_info) when is_binary(et_cache) do
    etag = etag_for_path(file_info)

    conn =
      conn
      |> put_resp_header("cache-control", et_cache)
      |> put_resp_header("etag", etag)

    if etag in get_req_header(conn, "if-none-match") do
      {:fresh, conn}
    else
      {:stale, conn}
    end
  end

  defp put_cache_header(conn, _, _, _) do
    {:stale, conn}
  end

  defp etag_for_path(file_info) do
    file_info(size: size, mtime: mtime) = file_info
    {size, mtime} |> :erlang.phash2() |> Integer.to_string(16)
  end

  defp file_encoding(conn, path, gzip) do
    path_gz = path <> ".gz"

    cond do
      gzip && gzip?(conn) && (file_info = regular_file_info(path_gz)) ->
        {:ok, put_resp_header(conn, "content-encoding", "gzip"), file_info, path_gz}
      file_info = regular_file_info(path) ->
        {:ok, conn, file_info, path}
      true ->
        {:error, conn}
    end
  end

  defp regular_file_info(path) do
    case :prim_file.read_file_info(path) do
      {:ok, file_info(type: :regular) = file_info} ->
        file_info
      _ ->
        nil
    end
  end

  defp gzip?(conn) do
    gzip_header? = &String.contains?(&1, ["gzip", "*"])
    Enum.any? get_req_header(conn, "accept-encoding"), fn accept ->
      accept |> Plug.Conn.Utils.list() |> Enum.any?(gzip_header?)
    end
  end

  defp path({app, from}, segments) when is_atom(app) and is_binary(from),
    do: Path.join([Application.app_dir(app), from|segments])
  defp path(from, segments),
    do: Path.join([from|segments])

  defp subset([h|expected], [h|actual]),
    do: subset(expected, actual)
  defp subset([], actual),
    do: actual
  defp subset(_, _),
    do: []

  defp invalid_path?([h|_]) when h in [".", "..", ""], do: true
  defp invalid_path?([h|t]), do: String.contains?(h, ["/", "\\", ":"]) or invalid_path?(t)
  defp invalid_path?([]), do: false
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<












































































































































































































































































































































































































































































































Deleted deps/plug/lib/plug/supervisor.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
defmodule Plug.Supervisor do
  @moduledoc false

  def start_link() do
    Supervisor.start_link(__MODULE__, :ok, name: __MODULE__)
  end

  def init(:ok) do
    import Supervisor.Spec

    children = [
      worker(Plug.Upload, [])
    ]

    Plug.Keys = :ets.new(Plug.Keys, [:named_table, :public, read_concurrency: true])
    supervise(children, strategy: :one_for_one)
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




































Deleted deps/plug/lib/plug/templates/debugger.eex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
<!DOCTYPE html>
<html>
<head>
    <title><%= @title %> at <%= method(@conn) %> <%= @conn.request_path %></title>
    <style>
    /* Basic reset */
    * {
        margin: 0;
        padding: 0;
    }

    table {
        width: 100%;
        border-collapse: collapse;
    }

    th, td {
        vertical-align: top;
        text-align: left;
    }

    textarea {
        resize: none;
    }

    body {
        font-size: 10pt;
    }

    body, td, input, textarea {
        font-family: helvetica neue, lucida grande, sans-serif;
        line-height: 1.5;
        color: #333;
        text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6);
    }

    html {
        background: #f0f0f5;
    }

    /* ---------------------------------------------------------------------
     * Basic layout
     * --------------------------------------------------------------------- */

    /* Small */
    @media screen and (max-width: 1100px) {
        html {
            overflow-y: scroll;
        }

        body {
            margin: 0 20px;
        }

        header.exception {
            margin: 0 -20px;
        }

        nav.sidebar {
            padding: 0;
            margin: 20px 0;
        }

        ul.frames {
            max-height: 200px;
            overflow: auto;
        }
    }

    /* Wide */
    @media screen and (min-width: 1100px) {
        header.exception {
           position: fixed;
           top: 0;
           left: 0;
           right: 0;
        }

        nav.sidebar,
        .conn_info {
            position: fixed;
            top: 95px;
            bottom: 0;

            box-sizing: border-box;

            overflow-y: auto;
            overflow-x: hidden;
        }

        nav.sidebar {
            width: 40%;
            left: 20px;
            top: 115px;
            bottom: 20px;
        }

        .conn_info {
            right: 0;
            left: 40%;

            padding: 20px;
            padding-left: 10px;
            margin-left: 30px;
        }
    }

    nav.sidebar {
        background: #d3d3da;
        border-top: solid 3px #4E2A8E;
        border-bottom: solid 3px #4E2A8E;
        border-radius: 4px;
        box-shadow: 0 0 6px rgba(0, 0, 0, 0.2), inset 0 0 0 1px rgba(0, 0, 0, 0.1);
    }

    /* ---------------------------------------------------------------------
     * Header
     * --------------------------------------------------------------------- */

    header.exception {
        padding: 18px 20px;

        height: 59px;
        min-height: 59px;

        overflow: hidden;

        background-color: #20202a;
        color: #aaa;
        text-shadow: 0 1px 0 rgba(0, 0, 0, 0.3);
        font-weight: 200;
        box-shadow: inset 0 -5px 3px -3px rgba(0, 0, 0, 0.05), inset 0 -1px 0 rgba(0, 0, 0, 0.05);

        -webkit-text-smoothing: antialiased;
    }

    /* Heading */
    header.exception h2 {
        font-weight: 200;
        font-size: 11pt;
        padding-bottom: 2pt;
    }

    header.exception h2,
    header.exception p {
        line-height: 1.4em;
        height: 1.4em;
        overflow: hidden;
        white-space: pre;
        text-overflow: ellipsis;
    }

    header.exception h2 strong {
        font-weight: 700;
        color: #7E5ABE;
    }

    header.exception p {
        font-weight: 200;
        font-size: 18pt;
        color: white;
    }

    header.exception:hover {
        height: auto;
        overflow-y: auto;
        max-height: 60%;
        z-index: 2;
    }

    header.exception:hover h2,
    header.exception:hover p {
        padding-right: 20px;
        word-wrap: break-word;
        height: auto;
    }

    @media screen and (max-width: 1100px) {
        header.exception {
            height: auto;
        }

        header.exception h2,
        header.exception p {
            padding-right: 20px;
            overflow-y: auto;
            word-wrap: break-word;
            height: auto;
            max-height: 7em;
        }
    }

    /* ---------------------------------------------------------------------
     * Navigation
     * --------------------------------------------------------------------- */

    nav.tabs {
        border-bottom: solid 1px #ddd;

        background-color: #eee;
        text-align: center;

        padding: 6px;

        box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
    }

    nav.tabs a {
        display: inline-block;

        height: 22px;
        line-height: 22px;
        padding: 0 10px;

        text-decoration: none;
        font-size: 8pt;
        font-weight: bold;

        color: #999;
        text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6);
    }

    nav.tabs a.selected {
        color: white;
        background: rgba(0, 0, 0, 0.5);
        border-radius: 16px;
        box-shadow: 1px 1px 0 rgba(255, 255, 255, 0.1);
        text-shadow: 0 0 4px rgba(0, 0, 0, 0.4), 0 1px 0 rgba(0, 0, 0, 0.4);
    }

    /* ---------------------------------------------------------------------
     * Sidebar
     * --------------------------------------------------------------------- */

    ul.frames {
        box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    }

    /* Each item */
    ul.frames li {
        background-color: #f8f8f8;
        background: -webkit-linear-gradient(top, #f8f8f8 80%, #f0f0f0);
        background: -moz-linear-gradient(top, #f8f8f8 80%, #f0f0f0);
        background: linear-gradient(top, #f8f8f8 80%, #f0f0f0);
        box-shadow: inset 0 -1px 0 #e2e2e2;
        padding: 7px 20px;

        cursor: pointer;
        overflow: hidden;
    }

    ul.frames .name,
    ul.frames .location {
        overflow: hidden;
        height: 1.5em;

        white-space: nowrap;
        word-wrap: none;
        text-overflow: ellipsis;
    }

    ul.frames .app {
        color: #4E2A8E;
    }

    ul.frames .location {
        font-size: 0.85em;
        font-weight: 400;
        color: #999;
    }

    ul.frames .line {
        font-weight: bold;
    }

    /* Selected frame */
    ul.frames li.selected {
        background: #88A;
        box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.1), inset 0 2px 0 rgba(255, 255, 255, 0.01), inset 0 -1px 0 rgba(0, 0, 0, 0.1);
    }

    ul.frames li.selected .name,
    ul.frames li.selected .function,
    ul.frames li.selected .location {
        color: white;
        text-shadow: 0 1px 0 rgba(0, 0, 0, 0.2);
    }

    ul.frames li.selected .location {
        opacity: 0.6;
    }

    /* Iconography */
    ul.frames li {
        padding-left: 60px;
        position: relative;
    }

    ul.frames li .icon {
        display: block;
        width: 20px;
        height: 20px;
        line-height: 20px;
        border-radius: 15px;

        text-align: center;

        background: white;
        border: solid 2px #ccc;

        font-size: 9pt;
        font-weight: 200;
        font-style: normal;

        position: absolute;
        top: 14px;
        left: 20px;
    }

    ul.frames .icon.app {
        background: #808090;
        border-color: #555;
    }

    ul.frames .icon.app:before {
        content: 'A';
        color: white;
        text-shadow: 0 0 3px rgba(0, 0, 0, 0.2);
    }

    /* Responsiveness -- flow to single-line mode */
    @media screen and (max-width: 1100px) {
        ul.frames li {
            padding-top: 6px;
            padding-bottom: 6px;
            padding-left: 36px;
            line-height: 1.3;
        }

        ul.frames li .icon {
            width: 11px;
            height: 11px;
            line-height: 11px;

            top: 7px;
            left: 10px;
            font-size: 5pt;
        }

        ul.frames .name,
        ul.frames .location {
            display: inline-block;
            line-height: 1.3;
            height: 1.3em;
        }

        ul.frames .name {
            margin-right: 10px;
        }
    }

    /* ---------------------------------------------------------------------
     * Monospace
     * --------------------------------------------------------------------- */

    pre, code, textarea {
        font-family: menlo, lucida console, monospace;
        font-size: 8pt;
    }

    /* ---------------------------------------------------------------------
     * Display area
     * --------------------------------------------------------------------- */

    .trace_info {
        background: #fff;
        padding: 6px;
        border-radius: 3px;
        margin-bottom: 2px;
        box-shadow: 0 0 10px rgba(0, 0, 0, 0.03), 1px 1px 0 rgba(0, 0, 0, 0.05), -1px 1px 0 rgba(0, 0, 0, 0.05), 0 0 0 4px rgba(0, 0, 0, 0.04);
    }

    /* Titlebar */
    .trace_info .title {
        background: #f1f1f1;

        box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.3);
        overflow: hidden;
        padding: 6px 10px;

        border: solid 1px #ccc;
        border-bottom: 0;

        border-top-left-radius: 2px;
        border-top-right-radius: 2px;
    }

    .trace_info .title .name,
    .trace_info .title .location {
        font-size: 9pt;
        line-height: 26px;
        height: 26px;
        overflow: hidden;
    }

    .trace_info .title .location {
        float: left;
        font-weight: bold;
        font-size: 10pt;
    }

    .trace_info .title .location a {
        color:inherit;
        text-decoration:none;
        border-bottom:1px solid #aaaaaa;
    }

    .trace_info .title .location a:hover {
        border-color:#666666;
    }

    .trace_info .title .name {
        float: right;
        font-weight: 200;
    }

    .code, .unavailable {
        background: #fff;
        padding: 5px;

        box-shadow: inset 3px 3px 3px rgba(0, 0, 0, 0.1), inset 0 0 0 1px rgba(0, 0, 0, 0.1);
    }

    .code {
        margin-bottom: -1px;
        padding: 10px 0;
        overflow: auto;
    }

    .code .ln {
        width: 35px;
        margin-right: 15px;
        text-align: right;
        display: inline-block;
    }

    .code .no_snippet {
        padding: 5px 15px 2px;
        font-size: 9pt;
    }

    /* Source unavailable */
    p.unavailable {
        padding: 20px 0 40px 0;
        text-align: center;
        color: #b99;
        font-weight: bold;
    }

    p.unavailable:before {
        content: '\00d7';
        display: block;
        color: #daa;
        text-align: center;
        font-size: 40pt;
        font-weight: normal;
        margin-bottom: -10px;
    }

    @-webkit-keyframes highlight {
        0%   { background: rgba(220, 30, 30, 0.3); }
        100% { background: rgba(220, 30, 30, 0.1); }
    }
    @-moz-keyframes highlight {
        0%   { background: rgba(220, 30, 30, 0.3); }
        100% { background: rgba(220, 30, 30, 0.1); }
    }
    @keyframes highlight {
        0%   { background: rgba(220, 30, 30, 0.3); }
        100% { background: rgba(220, 30, 30, 0.1); }
    }

    .code .highlight {
        background: rgba(220, 30, 30, 0.1);
        -webkit-animation: highlight 400ms linear 1;
        -moz-animation: highlight 400ms linear 1;
        animation: highlight 400ms linear 1;
    }

    /* ---------------------------------------------------------------------
     * Variable infos
     * --------------------------------------------------------------------- */

    .sub {
        padding: 10px 0;
        margin: 10px 0;
    }

    .sub:before {
        content: '';
        display: block;
        width: 100%;
        height: 4px;

        border-radius: 2px;
        background: rgba(0, 150, 200, 0.05);
        box-shadow: 1px 1px 0 rgba(255, 255, 255, 0.7), inset 0 0 0 1px rgba(0, 0, 0, 0.04), inset 2px 2px 2px rgba(0, 0, 0, 0.07);
    }

    .sub h3 {
        color: #4E2A8E;
        font-size: 1.1em;
        margin: 10px 0;
        text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6);

        -webkit-font-smoothing: antialiased;
    }

    .sub .inset {
        overflow-y: auto;
    }

    .sub table {
        table-layout: fixed;
    }

    .sub table td {
        border-top: dotted 1px #ddd;
        padding: 7px 1px;
    }

    .sub table td.name {
        width: 150px;

        font-weight: bold;
        font-size: 0.8em;
        padding-right: 20px;

        word-wrap: break-word;
    }

    .sub table td pre {
        max-height: 15em;
        overflow-y: auto;
    }

    .sub table td pre {
        width: 100%;

        word-wrap: break-word;
        white-space: normal;
    }

    /* "(object doesn't support inspect)" */
    .sub .unsupported {
      font-family: sans-serif;
      color: #777;
    }

    /* ---------------------------------------------------------------------
     * Scrollbar
     * --------------------------------------------------------------------- */

    nav.sidebar::-webkit-scrollbar,
    .inset pre::-webkit-scrollbar,
    .code::-webkit-scrollbar {
        width: 10px;
        height: 10px;
    }

    .inset pre::-webkit-scrollbar-thumb,
    .code::-webkit-scrollbar-thumb {
        background: #ccc;
        border-radius: 5px;
    }

    nav.sidebar::-webkit-scrollbar-thumb {
        background: rgba(0, 0, 0, 0.0);
        border-radius: 5px;
    }

    nav.sidebar:hover::-webkit-scrollbar-thumb {
        background-color: #999;
        background: -webkit-linear-gradient(left, #aaa, #999);
    }

    .code:hover::-webkit-scrollbar-thumb {
        background: #888;
    }
    </style>
</head>
<body>
    <div class="top">
        <header class="exception">
            <h2><strong><%= h(@title) %></strong> <span>at <%= method(@conn) %> <%= @conn.request_path %></span></h2>
            <p><%= h(@message) %></p>
        </header>
    </div>

    <section class="backtrace">
        <nav class="sidebar">
            <nav class="tabs">
                <a href="#" id="app_frames">App frames</a>
                <a href="#" id="all_frames">All frames</a>
            </nav>
            <ul class="frames">
                <%= for frame <- @frames do %>
                    <li class="<%= frame.context %>" style="display: none" data-context="<%= frame.context %>" data-index="<%= frame.index %>">
                        <span class='stroke'></span>
                        <i class="icon <%= frame.context %>"></i>
                        <div class="info">
                            <div class="name">
                                <strong><%= h(frame.info) %></strong>

                                <%= if app = frame.app do %>
                                    <span class="app">(<%= app %>)</span>
                                <% end %>
                            </div>
                            <div class="location">
                                <span class="filename"><%= frame.file %></span>
                                <%= if frame.line do %>
                                  (line <span class="line"><%= frame.line %></span>)
                                <% end %>
                            </div>
                        </div>
                    </li>
                <% end %>
            </ul>
        </nav>

        <div class="conn_info">
            <%= for frame <- @frames do %>
                <div class="frame_info" id="frame_info_<%= frame.index %>" style="display: none;">
                    <header class="trace_info">
                        <div class="title">
                            <h2 class="name">
                              <%= frame.info %>

                              <%= if app = frame.app do %>
                                  <span class="app">(<%= app %>)</span>
                              <% end %>
                            </h2>
                            <div class="location"><span class="filename"><a href="<%= frame.link %>"><%= frame.file %></a></span></div>
                        </div>

                        <div class="code">
                          <%= if snippet = frame.snippet do %>
                            <%= for {index, line, highlight} <- snippet do %>
                              <pre class="<%= if highlight, do: "highlight" %>"><span class="ln"><%= index %></span><span><%=h line %></span></pre>
                            <% end %>
                          <% else %>
                            <p class="no_snippet">No code snippets available.</p>
                          <% end %>
                        </div>
                    </header>

                    <div class="sub">
                        <h3>Frame</h3>
                        <div class="inset variables">
                            <table class="var_table">
                                <tr>
                                    <td class="name">Function</td>
                                    <td><%= h(frame.info) %> (<%= h(frame.app) %>)</td>
                                </tr>
                                <%= if frame.args != [] do %>
                                    <tr>
                                        <td class="name">Args</td>
                                        <td><%= h(inspect frame.args) %></td>
                                    </tr>
                                <% end %>
                            </table>
                        </div>
                    </div>
                </div>

            <% end %>

            <div class="sub">
                <h3>Request info</h3>
                <div class="inset variables">
                    <table class="var_table">
                        <tr>
                            <td class="name">URI</td>
                            <td><%= url(@conn) %></td>
                        </tr>
                        <tr>
                            <td class="name">Query String</td>
                            <td><%= @conn.query_string %></td>
                        </tr>
                        <tr>
                            <td class="name">Peer</td>
                            <td><%= peer(@conn) %></td>
                        </tr>
                    </table>
                </div>
            </div>

            <div class="sub">
                <h3>Headers</h3>
                <div class="inset variables">
                    <table class="var_table">
                        <%= for {key, value} <- Enum.sort(@conn.req_headers) do %>
                            <tr>
                                <td class="name"><%= key %></td>
                                <td><%= value %></td>
                            </tr>
                        <% end %>
                    </table>
                </div>
            </div>

            <%= if @params do %>
            <div class="sub">
                <h3>Params</h3>
                <div class="inset variables">
                    <table class="var_table">
                        <%= for {key, value} <- @params do %>
                            <tr>
                                <td class="name"><%= key %></td>
                                <td><pre><%= inspect value %></pre></td>
                            </tr>
                        <% end %>
                    </table>
                </div>
            </div>
            <% end %>

            <%= if @session do %>
            <div class="sub">
                <h3>Session</h3>
                <div class="inset variables">
                    <table class="var_table">
                        <%= for {key, value} <- @session do %>
                            <tr>
                                <td class="name"><%= key %></td>
                                <td><pre><%= inspect value %></pre></td>
                            </tr>
                        <% end %>
                    </table>
                </div>
            </div>
            <% end %>
        </div>
    </section>
</body>
<script>

var previousFrame = null;
var previousFrameInfo = null;
var allFrames = document.querySelectorAll("ul.frames li");
var allFrameInfos = document.querySelectorAll(".frame_info");

for(var i = 0; i < allFrames.length; i++) {
    (function(i, el) {
        var el = allFrames[i];
        el.onclick = function() {
            if(previousFrame) previousFrame.className = "";
            el.className = "selected";
            previousFrame = el;
            selectFrameInfo(el.attributes["data-index"].value);
        };
    })(i);
};

function selectFrameInfo(index) {
    var el = allFrameInfos[index];
     if(previousFrameInfo) previousFrameInfo.style.display = "none";
     previousFrameInfo = el;
     el.style.display = "block";
};

(
  document.querySelector(".frames li.app") ||
  document.querySelector(".frames li")
).onclick();

var appFramesButton = document.getElementById("app_frames");
var allFramesButton = document.getElementById("all_frames");

appFramesButton.onclick = function() {
    allFramesButton.className = "";
    appFramesButton.className = "selected";
    for(var i = 0; i < allFrames.length; i++) {
        if(allFrames[i].attributes["data-context"].value == "app") {
            allFrames[i].style.display = "block";
            allFrames[i].setAttribute("data-display", "block");
        } else {
            allFrames[i].style.display = "none";
            allFrames[i].setAttribute("data-display", "none");
        };
    };
    return false;
};

allFramesButton.onclick = function() {
    appFramesButton.className = "";
    allFramesButton.className = "selected";
    for(var i = 0; i < allFrames.length; i++) {
        allFrames[i].style.display = "block";
        allFrames[i].setAttribute("data-display", "block");
    };
    return false;
};

appFramesButton.onclick();
if(document.querySelectorAll("ul.frames li[data-display=\"block\"]").length == 0) {
    allFramesButton.onclick();
}
</script>
</html>
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted deps/plug/lib/plug/test.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
defmodule Plug.Test do
  @moduledoc """
  Conveniences for testing plugs.

  This module can be used in your test cases, like this:

      use ExUnit.Case, async: true
      use Plug.Test

  Using this module will:

    * import all the functions from this module
    * import all the functions from the `Plug.Conn` module
  """

  @doc false
  defmacro __using__(_) do
    quote do
      import Plug.Test
      import Plug.Conn
    end
  end

  alias Plug.Conn
  @typep params :: binary | list | map | nil

  @doc """
  Creates a test connection.

  The request `method` and `path` are required arguments. `method` may be any
  value that implements `to_string/1` and it will properly converted and
  normalized (e.g., `:get` or `"post"`).

  The `params_or_body` field must be one of:

  * `nil` - meaning there is no body;
  * a binary - containing a request body. For such cases, `:headers`
    must be given as option with a content-type;
  * a map or list - containing the parameters which will automatically
    set the content-type to multipart. The map or list may contain
    other lists or maps and all entries will be normalized to string
    keys;

  ## Examples

      conn(:get, "/foo", "bar=10")
      conn(:post, "/")
      conn("patch", "/", "") |> put_req_header("content-type", "application/json")

  """
  @spec conn(String.Chars.t, binary, params) :: Conn.t
  def conn(method, path, params_or_body \\ nil) do
    Plug.Adapters.Test.Conn.conn(%Plug.Conn{}, method, path, params_or_body)
  end

  @doc """
  Returns the sent response.

  This function is useful when the code being invoked crashes and
  there is a need to verify a particular response was sent even with
  the crash. It returns a tuple with `{status, headers, body}`.
  """
  def sent_resp(%Conn{adapter: {Plug.Adapters.Test.Conn, %{ref: ref}}}) do
    case receive_resp(ref) do
      :no_resp ->
        raise "no sent response available for the given connection. " <>
              "Maybe the application did not send anything?"
      response ->
        case receive_resp(ref) do
          :no_resp ->
            send(self, {ref, response})
            response
          _otherwise ->
            raise "a response for the given connection has been sent more than once"
        end
    end
  end

  defp receive_resp(ref) do
    receive do
      {^ref, response} -> response
    after
      0 -> :no_resp
    end
  end

  @doc """
  Puts a request cookie.
  """
  @spec put_req_cookie(Conn.t, binary, binary) :: Conn.t
  def put_req_cookie(conn, key, value) when is_binary(key) and is_binary(value) do
    conn = delete_req_cookie(conn, key)
    %{conn | req_headers: [{"cookie", "#{key}=#{value}"}|conn.req_headers]}
  end

  @doc """
  Deletes a request cookie.
  """
  @spec delete_req_cookie(Conn.t, binary) :: Conn.t
  def delete_req_cookie(%Conn{req_cookies: %Plug.Conn.Unfetched{}} = conn, key)
      when is_binary(key) do
    key  = "#{key}="
    size = byte_size(key)
    fun  = &match?({"cookie", value} when binary_part(value, 0, size) == key, &1)
    %{conn | req_headers: Enum.reject(conn.req_headers, fun)}
  end

  def delete_req_cookie(_conn, key) when is_binary(key) do
    raise ArgumentError,
      message: "cannot put/delete request cookies after cookies were fetched"
  end

  @doc """
  Moves cookies from a connection into a new connection for subsequent requests.

  This function copies the cookie information in `old_conn` into `new_conn`,
  emulating multiple requests done by clients where cookies are always passed
  forward, and returns the new version of `new_conn`.
  """
  @spec recycle_cookies(Conn.t, Conn.t) :: Conn.t
  def recycle_cookies(new_conn, old_conn) do
    Enum.reduce Plug.Conn.fetch_cookies(old_conn).cookies, new_conn, fn
      {key, value}, acc -> put_req_cookie(acc, to_string(key), value)
    end
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




























































































































































































































































Deleted deps/plug/lib/plug/upload.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
defmodule Plug.Upload do
  @moduledoc """
  A server (a `GenServer` specifically) that manages uploaded files.

  Uploaded files are stored in a temporary directory
  and removed from that directory after the process that
  requested the file dies.

  During the request, files are represented with
  a `Plug.Upload` struct that contains three fields:

  * `:path` - the path to the uploaded file on the filesystem
  * `:content_type` - the content type of the uploaded file
  * `:filename` - the filename of the uploaded file given in the request

  **Note**: as mentioned in the documentation for `Plug.Parsers`, the `:plug`
  application has to be started in order to upload files and use the
  `Plug.Upload` module.
  """

  defstruct [:path, :content_type, :filename]
  @type t :: %__MODULE__{
    path: Path.t,
    filename: binary,
    content_type: binary | nil
  }

  @doc """
  Requests a random file to be created in the upload directory
  with the given prefix.
  """
  @spec random_file(binary) ::
        {:ok, binary} |
        {:too_many_attempts, binary, pos_integer} |
        {:no_tmp, [binary]}
  def random_file(prefix) do
    GenServer.call(plug_server, {:random, prefix})
  end

  @doc """
  Requests a random file to be created in the upload directory
  with the given prefix. Raises on failure.
  """
  @spec random_file!(binary) :: binary | no_return
  def random_file!(prefix) do
    case random_file(prefix) do
      {:ok, path} ->
        path
      {:too_many_attempts, tmp, attempts} ->
        raise "tried #{attempts} times to create an uploaded file at #{tmp} but failed. What gives?"
      {:no_tmp, _tmps} ->
        raise "could not create a tmp directory to store uploads. Set PLUG_TMPDIR to a directory with write permission"
    end
  end

  defp plug_server do
    Process.whereis(__MODULE__) ||
      raise "could not find process Plug.Upload. Have you started the :plug application?"
  end

  use GenServer

  @doc """
  Starts the upload handling server.
  """
  def start_link() do
    GenServer.start_link(__MODULE__, :ok, [name: __MODULE__])
  end

  ## Callbacks

  @temp_env_vars ~w(PLUG_TMPDIR TMPDIR TMP TEMP)s
  @max_attempts 10

  @doc false
  def init(:ok) do
    tmp = Enum.find_value @temp_env_vars, "/tmp", &System.get_env/1
    cwd = Path.join(File.cwd!, "tmp")
    ets = :ets.new(:plug_uploads, [:private])
    {:ok, {[tmp, cwd], ets}}
  end

  @doc false
  def handle_call({:random, prefix}, {pid, _ref}, {tmps, ets} = state) do
    case find_tmp_dir(pid, tmps, ets) do
      {:ok, tmp, paths} ->
        {:reply, open_random_file(prefix, tmp, 0, pid, ets, paths), state}
      {:no_tmp, _} = error ->
        {:reply, error, state}
    end
  end

  def handle_call(msg, from, state) do
    super(msg, from, state)
  end

  @doc false
  def handle_info({:DOWN, _ref, :process, pid, _reason}, {_, ets} = state) do
    case :ets.lookup(ets, pid) do
      [{pid, _tmp, paths}] ->
        :ets.delete(ets, pid)
        Enum.each paths, &:file.delete/1
      [] ->
        :ok
    end
    {:noreply, state}
  end

  def handle_info(msg, state) do
    super(msg, state)
  end

  ## Helpers

  defp find_tmp_dir(pid, tmps, ets) do
    case :ets.lookup(ets, pid) do
      [{^pid, tmp, paths}] ->
        {:ok, tmp, paths}
      [] ->
        if tmp = ensure_tmp_dir(tmps) do
          :erlang.monitor(:process, pid)
          :ets.insert(ets, {pid, tmp, []})
          {:ok, tmp, []}
        else
          {:no_tmp, tmps}
        end
    end
  end

  defp ensure_tmp_dir(tmps) do
    {mega, _, _} = :os.timestamp
    subdir = "/plug-" <> i(mega)
    Enum.find_value(tmps, &write_tmp_dir(&1 <> subdir))
  end

  defp write_tmp_dir(path) do
    case File.mkdir_p(path) do
      :ok -> path
      {:error, _} -> nil
    end
  end

  defp open_random_file(prefix, tmp, attempts, pid, ets, paths) when attempts < @max_attempts do
    path = path(prefix, tmp)

    case :file.write_file(path, "", [:write, :raw, :exclusive, :binary]) do
      :ok ->
        :ets.update_element(ets, pid, {3, [path|paths]})
        {:ok, path}
      {:error, reason} when reason in [:eexist, :eaccess] ->
        open_random_file(prefix, tmp, attempts + 1, pid, ets, paths)
    end
  end

  defp open_random_file(_prefix, tmp, attempts, _pid, _ets, _paths) do
    {:too_many_attempts, tmp, attempts}
  end

  @compile {:inline, i: 1}

  defp i(integer), do: Integer.to_string(integer)

  defp path(prefix, tmp) do
    {_mega, sec, micro} = :os.timestamp
    scheduler_id = :erlang.system_info(:scheduler_id)
    tmp <> "/" <> prefix <> "-" <> i(sec) <> "-" <> i(micro) <> "-" <> i(scheduler_id)
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
















































































































































































































































































































































Deleted deps/plug/mix.exs.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
defmodule Plug.Mixfile do
  use Mix.Project

  @version "1.0.3"

  def project do
    [app: :plug,
     version: @version,
     elixir: "~> 1.0",
     deps: deps,
     package: package,
     description: "A specification and conveniences for composable " <>
                  "modules between web applications",
     name: "Plug",
     docs: [extras: ["README.md"], main: "extra-readme",
            source_ref: "v#{@version}",
            source_url: "https://github.com/elixir-lang/plug"]]
  end

  # Configuration for the OTP application
  def application do
    [applications: [:crypto, :logger],
     mod: {Plug, []}]
  end

  def deps do
    [{:cowboy, "~> 1.0", optional: true},
     {:earmark, "~> 0.1", only: :docs},
     {:ex_doc, "~> 0.10", only: :docs},
     {:inch_ex, only: :docs},
     {:hackney, "~> 1.2.0", only: :test}]
  end

  defp package do
    %{licenses: ["Apache 2"],
      links: %{"GitHub" => "https://github.com/elixir-lang/plug"}}
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<












































































Deleted deps/ranch/AUTHORS.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Ranch is available thanks to the work of:

Loïc Hoguin
James Fish
Andrew Majorov
Ransom Richardson
Fred Hebert
Geoff Cant
Klaus Trainer
josh rotenberg
0x00F6
Alexander Zhuravlev
Ali Sabil
Andre Graf
Andrew Thompson
Jihyun Yu
Slava Yurin
Stéphane Wirtel
Xiao Jia

The Ranch code was initially part of Cowboy. Before it was
split into a separate project, the following people worked
on the code that then became Ranch:

Loïc Hoguin
Ali Sabil
Andrew Thompson
DeadZen
Hunter Morris
Jesper Louis Andersen
Paul Oliver
Roberto Ostinelli
Steven Gravell
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


































































Deleted deps/ranch/LICENSE.

1
2
3
4
5
6
7
8
9
10
11
12
13
Copyright (c) 2011-2015, Loïc Hoguin <essen@ninenines.eu>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
<
<
<
<
<
<
<
<
<
<
<
<
<


























Deleted deps/ranch/Makefile.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# See LICENSE for licensing information.

PROJECT = ranch

# Options.

COMPILE_FIRST = ranch_transport
CT_OPTS += -pa test -ct_hooks ranch_ct_hook []
PLT_APPS = crypto public_key ssl
CI_OTP = \
	OTP_R15B01 OTP_R15B02 OTP_R15B03-1 \
	OTP_R16B OTP_R16B01 OTP_R16B02 OTP_R16B03-1 \
	OTP-17.0.2 OTP-17.1.2 OTP-17.2.2 OTP-17.3.4 OTP-17.4.1 OTP-17.5.6.2 \
	OTP-18.0.2

# Dependencies.

TEST_DEPS = ct_helper
dep_ct_helper = git https://github.com/ninenines/ct_helper master

# Standard targets.

include erlang.mk

# Also dialyze the tests.

DIALYZER_OPTS += --src -r test

# Use erl_make_certs from the tested release.

ci-setup:: $(DEPS_DIR)/ct_helper
	cp ~/.kerl/builds/$(CI_OTP_RELEASE)/otp_src_git/lib/ssl/test/erl_make_certs.erl deps/ct_helper/src/
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
































































Deleted deps/ranch/ebin/ranch.app.

1
2
3
4
5
6
7
8
9
10
11
12
{application,ranch,
             [{description,"Socket acceptor pool for TCP protocols."},
              {vsn,"1.2.0"},
              {id,"git"},
              {modules,[ranch,ranch_acceptor,ranch_acceptors_sup,ranch_app,
                        ranch_conns_sup,ranch_listener_sup,ranch_protocol,
                        ranch_server,ranch_ssl,ranch_sup,ranch_tcp,
                        ranch_transport]},
              {registered,[ranch_sup,ranch_server]},
              {applications,[kernel,stdlib]},
              {mod,{ranch_app,[]}},
              {env,[]}]}.
<
<
<
<
<
<
<
<
<
<
<
<
























Deleted deps/ranch/ebin/ranch.beam.

cannot compute difference between binary files

Deleted deps/ranch/ebin/ranch_acceptor.beam.

cannot compute difference between binary files

Deleted deps/ranch/ebin/ranch_acceptors_sup.beam.

cannot compute difference between binary files

Deleted deps/ranch/ebin/ranch_app.beam.

cannot compute difference between binary files

Deleted deps/ranch/ebin/ranch_conns_sup.beam.

cannot compute difference between binary files

Deleted deps/ranch/ebin/ranch_listener_sup.beam.

cannot compute difference between binary files

Deleted deps/ranch/ebin/ranch_protocol.beam.

cannot compute difference between binary files

Deleted deps/ranch/ebin/ranch_server.beam.

cannot compute difference between binary files

Deleted deps/ranch/ebin/ranch_ssl.beam.

cannot compute difference between binary files

Deleted deps/ranch/ebin/ranch_sup.beam.

cannot compute difference between binary files

Deleted deps/ranch/ebin/ranch_tcp.beam.

cannot compute difference between binary files

Deleted deps/ranch/ebin/ranch_transport.beam.

cannot compute difference between binary files

Deleted deps/ranch/erlang.mk.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
4526
4527
4528
4529
4530
4531
4532
4533
4534
4535
4536
4537
4538
4539
4540
4541
4542
4543
4544
4545
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
4560
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
4576
4577
4578
4579
4580
4581
4582
4583
4584
4585
4586
4587
4588
4589
4590
4591
4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606
4607
4608
4609
4610
4611
4612
4613
4614
4615
4616
4617
4618
4619
4620
4621
4622
4623
4624
4625
4626
4627
4628
4629
4630
4631
4632
4633
4634
4635
4636
4637
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651
4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
4669
4670
4671
4672
4673
4674
4675
4676
4677
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
4693
4694
4695
4696
4697
4698
4699
4700
4701
4702
4703
4704
4705
4706
4707
4708
4709
4710
4711
4712
4713
4714
4715
4716
4717
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
4732
4733
4734
4735
4736
4737
4738
4739
4740
4741
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771
4772
4773
4774
4775
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
4794
4795
4796
4797
4798
4799
4800
4801
4802
4803
4804
4805
4806
4807
4808
4809
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819
4820
4821
4822
4823
4824
4825
4826
4827
4828
4829
4830
4831
4832
4833
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
4847
4848
4849
4850
4851
4852
4853
4854
4855
4856
4857
4858
4859
4860
4861
4862
4863
4864
4865
4866
4867
4868
4869
4870
4871
4872
4873
4874
4875
4876
4877
4878
4879
4880
4881
4882
4883
4884
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
4915
4916
4917
4918
4919
4920
4921
4922
4923
4924
4925
4926
4927
4928
4929
4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960
4961
4962
4963
4964
4965
4966
4967
4968
4969
4970
4971
4972
4973
4974
4975
4976
4977
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987
4988
4989
4990
4991
4992
4993
4994
4995
4996
4997
4998
4999
5000
5001
5002
5003
5004
5005
5006
5007
5008
5009
5010
5011
5012
5013
5014
5015
5016
5017
5018
5019
5020
5021
5022
5023
5024
5025
5026
5027
5028
5029
5030
5031
5032
5033
5034
5035
5036
5037
5038
5039
5040
5041
5042
5043
5044
5045
5046
5047
5048
5049
5050
5051
5052
5053
5054
5055
5056
5057
5058
5059
5060
5061
5062
5063
5064
5065
5066
5067
5068
5069
5070
5071
5072
5073
5074
5075
5076
5077
5078
5079
5080
5081
5082
5083
5084
5085
5086
5087
5088
5089
5090
5091
5092
5093
5094
5095
5096
5097
5098
5099
5100
5101
5102
5103
5104
5105
5106
5107
5108
5109
5110
5111
5112
5113
5114
5115
5116
5117
5118
5119
5120
5121
5122
5123
5124
5125
5126
5127
5128
5129
5130
5131
5132
5133
5134
5135
5136
5137
5138
5139
5140
5141
5142
5143
5144
5145
5146
5147
5148
5149
5150
5151
5152
5153
5154
5155
5156
5157
5158
5159
5160
5161
5162
5163
5164
5165
5166
5167
5168
5169
5170
5171
5172
5173
5174
5175
5176
5177
5178
5179
5180
5181
5182
5183
5184
5185
5186
5187
5188
5189
5190
5191
5192
5193
5194
5195
5196
5197
5198
5199
5200
5201
5202
5203
5204
5205
5206
5207
5208
5209
5210
5211
5212
5213
5214
5215
5216
5217
5218
5219
5220
5221
5222
5223
5224
5225
5226
5227
5228
5229
5230
5231
5232
5233
5234
5235
5236
5237
5238
5239
5240
5241
5242
5243
5244
5245
5246
5247
5248
5249
5250
5251
5252
5253
5254
5255
5256
5257
5258
5259
5260
5261
5262
5263
5264
5265
5266
5267
5268
5269
5270
5271
5272
5273
5274
5275
5276
5277
5278
5279
5280
5281
5282
5283
5284
5285
5286
5287
5288
5289
5290
5291
5292
5293
5294
5295
5296
5297
5298
5299
5300
5301
5302
5303
5304
5305
5306
5307
5308
5309
5310
5311
5312
5313
5314
5315
5316
5317
5318
5319
5320
5321
5322
5323
5324
5325
5326
5327
5328
5329
5330
5331
5332
5333
5334
5335
5336
5337
5338
5339
5340
5341
5342
5343
5344
5345
5346
5347
5348
5349
5350
5351
5352
5353
5354
5355
5356
5357
5358
5359
5360
5361
5362
5363
5364
5365
5366
5367
5368
5369
5370
5371
5372
5373
5374
5375
5376
5377
5378
5379
5380
5381
5382
5383
5384
5385
5386
5387
5388
5389
5390
5391
5392
5393
5394
5395
5396
5397
5398
5399
5400
5401
5402
5403
5404
5405
5406
5407
5408
5409
5410
5411
5412
5413
5414
5415
5416
5417
5418
5419
5420
5421
5422
5423
5424
5425
5426
5427
5428
5429
5430
5431
5432
5433
5434
5435
5436
5437
5438
5439
5440
5441
5442
5443
5444
5445
5446
5447
5448
5449
5450
5451
5452
5453
5454
5455
5456
5457
5458
5459
5460
5461
5462
5463
5464
5465
5466
5467
5468
5469
5470
5471
5472
5473
5474
5475
5476
5477
5478
5479
5480
5481
5482
5483
5484
5485
5486
5487
5488
5489
5490
5491
5492
5493
5494
5495
5496
5497
5498
5499
5500
5501
5502
5503
5504
5505
5506
5507
5508
5509
5510
5511
5512
5513
5514
5515
5516
5517
5518
5519
5520
5521
5522
5523
5524
5525
5526
5527
5528
5529
5530
5531
5532
5533
5534
5535
5536
5537
5538
5539
5540
5541
5542
5543
5544
5545
5546
5547
5548
5549
5550
5551
5552
5553
5554
5555
5556
5557
5558
5559
5560
5561
5562
5563
5564
5565
5566
5567
5568
5569
5570
5571
5572
5573
5574
5575
5576
5577
5578
5579
5580
5581
5582
5583
5584
5585
5586
5587
5588
5589
5590
5591
5592
5593
5594
5595
5596
5597
5598
5599
5600
5601
5602
5603
5604
5605
5606
5607
5608
5609
5610
5611
5612
5613
5614
5615
5616
5617
5618
5619
5620
5621
5622
5623
5624
5625
5626
5627
5628
5629
5630
5631
5632
5633
5634
5635
5636
5637
5638
5639
5640
5641
5642
5643
5644
5645
5646
5647
5648
5649
5650
5651
5652
5653
5654
5655
5656
5657
5658
5659
5660
5661
5662
5663
5664
5665
5666
5667
5668
5669
5670
5671
5672
5673
5674
5675
5676
5677
5678
5679
5680
5681
5682
5683
5684
5685
5686
5687
5688
5689
5690
5691
5692
5693
5694
5695
5696
5697
5698
5699
5700
5701
5702
5703
5704
5705
5706
5707
5708
5709
5710
5711
5712
5713
5714
5715
5716
5717
5718
5719
5720
5721
5722
5723
5724
5725
5726
5727
5728
5729
5730
5731
5732
5733
5734
5735
5736
5737
5738
5739
5740
5741
5742
5743
5744
5745
5746
5747
5748
5749
5750
5751
5752
5753
5754
5755
5756
5757
5758
5759
5760
5761
5762
5763
5764
5765
5766
5767
5768
5769
5770
5771
5772
5773
5774
5775
5776
5777
5778
5779
5780
5781
5782
5783
5784
5785
5786
5787
5788
5789
5790
5791
5792
5793
5794
5795
5796
5797
5798
5799
5800
5801
5802
5803
5804
5805
5806
5807
5808
5809
5810
5811
5812
5813
5814
5815
5816
5817
5818
5819
5820
5821
5822
5823
5824
5825
5826
5827
5828
5829
5830
5831
5832
5833
5834
5835
5836
5837
5838
5839
5840
5841
5842
5843
5844
5845
5846
5847
5848
5849
5850
5851
5852
5853
5854
5855
5856
5857
5858
5859
5860
5861
5862
5863
5864
5865
5866
5867
5868
5869
5870
5871
5872
5873
5874
5875
5876
5877
5878
5879
5880
5881
5882
5883
5884
5885
5886
5887
5888
5889
5890
5891
5892
5893
5894
5895
5896
5897
5898
5899
5900
5901
5902
5903
5904
5905
5906
5907
5908
5909
5910
5911
5912
5913
5914
5915
5916
5917
5918
5919
5920
5921
5922
5923
5924
5925
5926
5927
5928
5929
5930
5931
5932
5933
5934
5935
5936
5937
5938
5939
5940
5941
5942
5943
5944
5945
5946
5947
5948
5949
5950
5951
5952
5953
5954
5955
5956
5957
5958
5959
5960
5961
5962
5963
5964
5965
5966
5967
5968
5969
5970
5971
5972
5973
5974
5975
5976
5977
5978
5979
5980
5981
5982
5983
5984
5985
5986
5987
5988
5989
5990
5991
5992
5993
5994
5995
5996
5997
5998
5999
6000
6001
6002
6003
6004
6005
6006
6007
6008
6009
6010
6011
6012
6013
6014
6015
6016
6017
6018
6019
6020
6021
6022
6023
6024
6025
6026
6027
6028
6029
# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

.PHONY: all app deps search rel docs install-docs check tests clean distclean help erlang-mk

ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))

ERLANG_MK_VERSION = 1.2.0-633-g8a32e48

# Core configuration.

PROJECT ?= $(notdir $(CURDIR))
PROJECT := $(strip $(PROJECT))

PROJECT_VERSION ?= rolling

# Verbosity.

V ?= 0

verbose_0 = @
verbose = $(verbose_$(V))

gen_verbose_0 = @echo " GEN   " $@;
gen_verbose = $(gen_verbose_$(V))

# Temporary files directory.

ERLANG_MK_TMP ?= $(CURDIR)/.erlang.mk
export ERLANG_MK_TMP

# "erl" command.

ERL = erl +A0 -noinput -boot start_clean

# Platform detection.
# @todo Add Windows/Cygwin detection eventually.

ifeq ($(PLATFORM),)
UNAME_S := $(shell uname -s)

ifeq ($(UNAME_S),Linux)
PLATFORM = linux
else ifeq ($(UNAME_S),Darwin)
PLATFORM = darwin
else ifeq ($(UNAME_S),SunOS)
PLATFORM = solaris
else ifeq ($(UNAME_S),GNU)
PLATFORM = gnu
else ifeq ($(UNAME_S),FreeBSD)
PLATFORM = freebsd
else ifeq ($(UNAME_S),NetBSD)
PLATFORM = netbsd
else ifeq ($(UNAME_S),OpenBSD)
PLATFORM = openbsd
else
$(error Unable to detect platform. Please open a ticket with the output of uname -a.)
endif

export PLATFORM
endif

# Core targets.

ifneq ($(words $(MAKECMDGOALS)),1)
.NOTPARALLEL:
endif

all:: deps
	$(verbose) $(MAKE) --no-print-directory app
	$(verbose) $(MAKE) --no-print-directory rel

# Noop to avoid a Make warning when there's nothing to do.
rel::
	$(verbose) echo -n

check:: clean app tests

clean:: clean-crashdump

clean-crashdump:
ifneq ($(wildcard erl_crash.dump),)
	$(gen_verbose) rm -f erl_crash.dump
endif

distclean:: clean

help::
	$(verbose) printf "%s\n" \
		"erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
		"Copyright (c) 2013-2015 Loïc Hoguin <essen@ninenines.eu>" \
		"" \
		"Usage: [V=1] $(MAKE) [-jNUM] [target]..." \
		"" \
		"Core targets:" \
		"  all           Run deps, app and rel targets in that order" \
		"  app           Compile the project" \
		"  deps          Fetch dependencies (if needed) and compile them" \
		"  search q=...  Search for a package in the built-in index" \
		"  rel           Build a release for this project, if applicable" \
		"  docs          Build the documentation for this project" \
		"  install-docs  Install the man pages for this project" \
		"  check         Compile and run all tests and analysis for this project" \
		"  tests         Run the tests for this project" \
		"  clean         Delete temporary and output files from most targets" \
		"  distclean     Delete all temporary and output files" \
		"  help          Display this help and exit" \
		"  erlang-mk     Update erlang.mk to the latest version"

# Core functions.

empty :=
space := $(empty) $(empty)
tab := $(empty)	$(empty)
comma := ,

define newline


endef

define comma_list
$(subst $(space),$(comma),$(strip $(1)))
endef

# Adding erlang.mk to make Erlang scripts who call init:get_plain_arguments() happy.
define erlang
$(ERL) -pz $(ERLANG_MK_TMP)/rebar/ebin -eval "$(subst $(newline),,$(subst ",\",$(1)))" -- erlang.mk
endef

ifeq ($(shell which wget 2>/dev/null | wc -l), 1)
define core_http_get
	wget --no-check-certificate -O $(1) $(2)|| rm $(1)
endef
else
define core_http_get.erl
	ssl:start(),
	inets:start(),
	case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
		{ok, {{_, 200, _}, _, Body}} ->
			case file:write_file("$(1)", Body) of
				ok -> ok;
				{error, R1} -> halt(R1)
			end;
		{error, R2} ->
			halt(R2)
	end,
	halt(0).
endef

define core_http_get
	$(call erlang,$(call core_http_get.erl,$(1),$(2)))
endef
endif

core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))

core_find = $(foreach d,$(call core_ls,$1*),$(call core_find,$d/,$2) $(filter $(subst *,%,$2),$d))

core_lc = $(subst A,a,$(subst B,b,$(subst C,c,$(subst D,d,$(subst E,e,$(subst F,f,$(subst G,g,$(subst H,h,$(subst I,i,$(subst J,j,$(subst K,k,$(subst L,l,$(subst M,m,$(subst N,n,$(subst O,o,$(subst P,p,$(subst Q,q,$(subst R,r,$(subst S,s,$(subst T,t,$(subst U,u,$(subst V,v,$(subst W,w,$(subst X,x,$(subst Y,y,$(subst Z,z,$(1)))))))))))))))))))))))))))

# @todo On Windows: $(shell dir /B $(1)); make sure to handle when no file exists.
core_ls = $(filter-out $(1),$(shell echo $(1)))

# Automated update.

ERLANG_MK_BUILD_CONFIG ?= build.config
ERLANG_MK_BUILD_DIR ?= .erlang.mk.build

erlang-mk:
	git clone https://github.com/ninenines/erlang.mk $(ERLANG_MK_BUILD_DIR)
	if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR); fi
	cd $(ERLANG_MK_BUILD_DIR) && $(MAKE)
	cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk
	rm -rf $(ERLANG_MK_BUILD_DIR)

# The erlang.mk package index is bundled in the default erlang.mk build.
# Search for the string "copyright" to skip to the rest of the code.

PACKAGES += aberth
pkg_aberth_name = aberth
pkg_aberth_description = Generic BERT-RPC server in Erlang
pkg_aberth_homepage = https://github.com/a13x/aberth
pkg_aberth_fetch = git
pkg_aberth_repo = https://github.com/a13x/aberth
pkg_aberth_commit = master

PACKAGES += active
pkg_active_name = active
pkg_active_description = Active development for Erlang: rebuild and reload source/binary files while the VM is running
pkg_active_homepage = https://github.com/proger/active
pkg_active_fetch = git
pkg_active_repo = https://github.com/proger/active
pkg_active_commit = master

PACKAGES += actordb_core
pkg_actordb_core_name = actordb_core
pkg_actordb_core_description = ActorDB main source
pkg_actordb_core_homepage = http://www.actordb.com/
pkg_actordb_core_fetch = git
pkg_actordb_core_repo = https://github.com/biokoda/actordb_core
pkg_actordb_core_commit = master

PACKAGES += actordb_thrift
pkg_actordb_thrift_name = actordb_thrift
pkg_actordb_thrift_description = Thrift API for ActorDB
pkg_actordb_thrift_homepage = http://www.actordb.com/
pkg_actordb_thrift_fetch = git
pkg_actordb_thrift_repo = https://github.com/biokoda/actordb_thrift
pkg_actordb_thrift_commit = master

PACKAGES += aleppo
pkg_aleppo_name = aleppo
pkg_aleppo_description = Alternative Erlang Pre-Processor
pkg_aleppo_homepage = https://github.com/ErlyORM/aleppo
pkg_aleppo_fetch = git
pkg_aleppo_repo = https://github.com/ErlyORM/aleppo
pkg_aleppo_commit = master

PACKAGES += alog
pkg_alog_name = alog
pkg_alog_description = Simply the best logging framework for Erlang
pkg_alog_homepage = https://github.com/siberian-fast-food/alogger
pkg_alog_fetch = git
pkg_alog_repo = https://github.com/siberian-fast-food/alogger
pkg_alog_commit = master

PACKAGES += amqp_client
pkg_amqp_client_name = amqp_client
pkg_amqp_client_description = RabbitMQ Erlang AMQP client
pkg_amqp_client_homepage = https://www.rabbitmq.com/erlang-client-user-guide.html
pkg_amqp_client_fetch = git
pkg_amqp_client_repo = https://github.com/rabbitmq/rabbitmq-erlang-client.git
pkg_amqp_client_commit = master

PACKAGES += annotations
pkg_annotations_name = annotations
pkg_annotations_description = Simple code instrumentation utilities
pkg_annotations_homepage = https://github.com/hyperthunk/annotations
pkg_annotations_fetch = git
pkg_annotations_repo = https://github.com/hyperthunk/annotations
pkg_annotations_commit = master

PACKAGES += antidote
pkg_antidote_name = antidote
pkg_antidote_description = Large-scale computation without synchronisation
pkg_antidote_homepage = https://syncfree.lip6.fr/
pkg_antidote_fetch = git
pkg_antidote_repo = https://github.com/SyncFree/antidote
pkg_antidote_commit = master

PACKAGES += apns
pkg_apns_name = apns
pkg_apns_description = Apple Push Notification Server for Erlang
pkg_apns_homepage = http://inaka.github.com/apns4erl
pkg_apns_fetch = git
pkg_apns_repo = https://github.com/inaka/apns4erl
pkg_apns_commit = 1.0.4

PACKAGES += azdht
pkg_azdht_name = azdht
pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang
pkg_azdht_homepage = https://github.com/arcusfelis/azdht
pkg_azdht_fetch = git
pkg_azdht_repo = https://github.com/arcusfelis/azdht
pkg_azdht_commit = master

PACKAGES += backoff
pkg_backoff_name = backoff
pkg_backoff_description = Simple exponential backoffs in Erlang
pkg_backoff_homepage = https://github.com/ferd/backoff
pkg_backoff_fetch = git
pkg_backoff_repo = https://github.com/ferd/backoff
pkg_backoff_commit = master

PACKAGES += barrel_tcp
pkg_barrel_tcp_name = barrel_tcp
pkg_barrel_tcp_description = barrel is a generic TCP acceptor pool with low latency in Erlang.
pkg_barrel_tcp_homepage = https://github.com/benoitc-attic/barrel_tcp
pkg_barrel_tcp_fetch = git
pkg_barrel_tcp_repo = https://github.com/benoitc-attic/barrel_tcp
pkg_barrel_tcp_commit = master

PACKAGES += basho_bench
pkg_basho_bench_name = basho_bench
pkg_basho_bench_description = A load-generation and testing tool for basically whatever you can write a returning Erlang function for.
pkg_basho_bench_homepage = https://github.com/basho/basho_bench
pkg_basho_bench_fetch = git
pkg_basho_bench_repo = https://github.com/basho/basho_bench
pkg_basho_bench_commit = master

PACKAGES += bcrypt
pkg_bcrypt_name = bcrypt
pkg_bcrypt_description = Bcrypt Erlang / C library
pkg_bcrypt_homepage = https://github.com/riverrun/branglecrypt
pkg_bcrypt_fetch = git
pkg_bcrypt_repo = https://github.com/riverrun/branglecrypt
pkg_bcrypt_commit = master

PACKAGES += beam
pkg_beam_name = beam
pkg_beam_description = BEAM emulator written in Erlang
pkg_beam_homepage = https://github.com/tonyrog/beam
pkg_beam_fetch = git
pkg_beam_repo = https://github.com/tonyrog/beam
pkg_beam_commit = master

PACKAGES += beanstalk
pkg_beanstalk_name = beanstalk
pkg_beanstalk_description = An Erlang client for beanstalkd
pkg_beanstalk_homepage = https://github.com/tim/erlang-beanstalk
pkg_beanstalk_fetch = git
pkg_beanstalk_repo = https://github.com/tim/erlang-beanstalk
pkg_beanstalk_commit = master

PACKAGES += bear
pkg_bear_name = bear
pkg_bear_description = a set of statistics functions for erlang
pkg_bear_homepage = https://github.com/boundary/bear
pkg_bear_fetch = git
pkg_bear_repo = https://github.com/boundary/bear
pkg_bear_commit = master

PACKAGES += bertconf
pkg_bertconf_name = bertconf
pkg_bertconf_description = Make ETS tables out of statc BERT files that are auto-reloaded
pkg_bertconf_homepage = https://github.com/ferd/bertconf
pkg_bertconf_fetch = git
pkg_bertconf_repo = https://github.com/ferd/bertconf
pkg_bertconf_commit = master

PACKAGES += bifrost
pkg_bifrost_name = bifrost
pkg_bifrost_description = Erlang FTP Server Framework
pkg_bifrost_homepage = https://github.com/thorstadt/bifrost
pkg_bifrost_fetch = git
pkg_bifrost_repo = https://github.com/thorstadt/bifrost
pkg_bifrost_commit = master

PACKAGES += binpp
pkg_binpp_name = binpp
pkg_binpp_description = Erlang Binary Pretty Printer
pkg_binpp_homepage = https://github.com/jtendo/binpp
pkg_binpp_fetch = git
pkg_binpp_repo = https://github.com/jtendo/binpp
pkg_binpp_commit = master

PACKAGES += bisect
pkg_bisect_name = bisect
pkg_bisect_description = Ordered fixed-size binary dictionary in Erlang
pkg_bisect_homepage = https://github.com/knutin/bisect
pkg_bisect_fetch = git
pkg_bisect_repo = https://github.com/knutin/bisect
pkg_bisect_commit = master

PACKAGES += bitcask
pkg_bitcask_name = bitcask
pkg_bitcask_description = because you need another a key/value storage engine
pkg_bitcask_homepage = https://github.com/basho/bitcask
pkg_bitcask_fetch = git
pkg_bitcask_repo = https://github.com/basho/bitcask
pkg_bitcask_commit = master

PACKAGES += bitstore
pkg_bitstore_name = bitstore
pkg_bitstore_description = A document based ontology development environment
pkg_bitstore_homepage = https://github.com/bdionne/bitstore
pkg_bitstore_fetch = git
pkg_bitstore_repo = https://github.com/bdionne/bitstore
pkg_bitstore_commit = master

PACKAGES += bootstrap
pkg_bootstrap_name = bootstrap
pkg_bootstrap_description = A simple, yet powerful Erlang cluster bootstrapping application.
pkg_bootstrap_homepage = https://github.com/schlagert/bootstrap
pkg_bootstrap_fetch = git
pkg_bootstrap_repo = https://github.com/schlagert/bootstrap
pkg_bootstrap_commit = master

PACKAGES += boss_db
pkg_boss_db_name = boss_db
pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang
pkg_boss_db_homepage = https://github.com/ErlyORM/boss_db
pkg_boss_db_fetch = git
pkg_boss_db_repo = https://github.com/ErlyORM/boss_db
pkg_boss_db_commit = master

PACKAGES += boss
pkg_boss_name = boss
pkg_boss_description = Erlang web MVC, now featuring Comet
pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss
pkg_boss_fetch = git
pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss
pkg_boss_commit = master

PACKAGES += bson
pkg_bson_name = bson
pkg_bson_description = BSON documents in Erlang, see bsonspec.org
pkg_bson_homepage = https://github.com/comtihon/bson-erlang
pkg_bson_fetch = git
pkg_bson_repo = https://github.com/comtihon/bson-erlang
pkg_bson_commit = master

PACKAGES += bullet
pkg_bullet_name = bullet
pkg_bullet_description = Simple, reliable, efficient streaming for Cowboy.
pkg_bullet_homepage = http://ninenines.eu
pkg_bullet_fetch = git
pkg_bullet_repo = https://github.com/extend/bullet
pkg_bullet_commit = master

PACKAGES += cache
pkg_cache_name = cache
pkg_cache_description = Erlang in-memory cache
pkg_cache_homepage = https://github.com/fogfish/cache
pkg_cache_fetch = git
pkg_cache_repo = https://github.com/fogfish/cache
pkg_cache_commit = master

PACKAGES += cake
pkg_cake_name = cake
pkg_cake_description = Really simple terminal colorization
pkg_cake_homepage = https://github.com/darach/cake-erl
pkg_cake_fetch = git
pkg_cake_repo = https://github.com/darach/cake-erl
pkg_cake_commit = v0.1.2

PACKAGES += carotene
pkg_carotene_name = carotene
pkg_carotene_description = Real-time server
pkg_carotene_homepage = https://github.com/carotene/carotene
pkg_carotene_fetch = git
pkg_carotene_repo = https://github.com/carotene/carotene
pkg_carotene_commit = master

PACKAGES += cberl
pkg_cberl_name = cberl
pkg_cberl_description = NIF based Erlang bindings for Couchbase
pkg_cberl_homepage = https://github.com/chitika/cberl
pkg_cberl_fetch = git
pkg_cberl_repo = https://github.com/chitika/cberl
pkg_cberl_commit = master

PACKAGES += cecho
pkg_cecho_name = cecho
pkg_cecho_description = An ncurses library for Erlang
pkg_cecho_homepage = https://github.com/mazenharake/cecho
pkg_cecho_fetch = git
pkg_cecho_repo = https://github.com/mazenharake/cecho
pkg_cecho_commit = master

PACKAGES += cferl
pkg_cferl_name = cferl
pkg_cferl_description = Rackspace / Open Stack Cloud Files Erlang Client
pkg_cferl_homepage = https://github.com/ddossot/cferl
pkg_cferl_fetch = git
pkg_cferl_repo = https://github.com/ddossot/cferl
pkg_cferl_commit = master

PACKAGES += chaos_monkey
pkg_chaos_monkey_name = chaos_monkey
pkg_chaos_monkey_description = This is The CHAOS MONKEY.  It will kill your processes.
pkg_chaos_monkey_homepage = https://github.com/dLuna/chaos_monkey
pkg_chaos_monkey_fetch = git
pkg_chaos_monkey_repo = https://github.com/dLuna/chaos_monkey
pkg_chaos_monkey_commit = master

PACKAGES += check_node
pkg_check_node_name = check_node
pkg_check_node_description = Nagios Scripts for monitoring Riak
pkg_check_node_homepage = https://github.com/basho-labs/riak_nagios
pkg_check_node_fetch = git
pkg_check_node_repo = https://github.com/basho-labs/riak_nagios
pkg_check_node_commit = master

PACKAGES += chronos
pkg_chronos_name = chronos
pkg_chronos_description = Timer module for Erlang that makes it easy to abstact time out of the tests.
pkg_chronos_homepage = https://github.com/lehoff/chronos
pkg_chronos_fetch = git
pkg_chronos_repo = https://github.com/lehoff/chronos
pkg_chronos_commit = master

PACKAGES += classifier
pkg_classifier_name = classifier
pkg_classifier_description = An Erlang Bayesian Filter and Text Classifier
pkg_classifier_homepage = https://github.com/inaka/classifier
pkg_classifier_fetch = git
pkg_classifier_repo = https://github.com/inaka/classifier
pkg_classifier_commit = master

PACKAGES += clique
pkg_clique_name = clique
pkg_clique_description = CLI Framework for Erlang
pkg_clique_homepage = https://github.com/basho/clique
pkg_clique_fetch = git
pkg_clique_repo = https://github.com/basho/clique
pkg_clique_commit = develop

PACKAGES += cl
pkg_cl_name = cl
pkg_cl_description = OpenCL binding for Erlang
pkg_cl_homepage = https://github.com/tonyrog/cl
pkg_cl_fetch = git
pkg_cl_repo = https://github.com/tonyrog/cl
pkg_cl_commit = master

PACKAGES += cloudi_core
pkg_cloudi_core_name = cloudi_core
pkg_cloudi_core_description = CloudI internal service runtime
pkg_cloudi_core_homepage = http://cloudi.org/
pkg_cloudi_core_fetch = git
pkg_cloudi_core_repo = https://github.com/CloudI/cloudi_core
pkg_cloudi_core_commit = master

PACKAGES += cloudi_service_api_requests
pkg_cloudi_service_api_requests_name = cloudi_service_api_requests
pkg_cloudi_service_api_requests_description = CloudI Service API requests (JSON-RPC/Erlang-term support)
pkg_cloudi_service_api_requests_homepage = http://cloudi.org/
pkg_cloudi_service_api_requests_fetch = git
pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests
pkg_cloudi_service_api_requests_commit = master

PACKAGES += cloudi_service_db_cassandra_cql
pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql
pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service
pkg_cloudi_service_db_cassandra_cql_homepage = http://cloudi.org/
pkg_cloudi_service_db_cassandra_cql_fetch = git
pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql
pkg_cloudi_service_db_cassandra_cql_commit = master

PACKAGES += cloudi_service_db_cassandra
pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra
pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service
pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/
pkg_cloudi_service_db_cassandra_fetch = git
pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra
pkg_cloudi_service_db_cassandra_commit = master

PACKAGES += cloudi_service_db_couchdb
pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb
pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service
pkg_cloudi_service_db_couchdb_homepage = http://cloudi.org/
pkg_cloudi_service_db_couchdb_fetch = git
pkg_cloudi_service_db_couchdb_repo = https://github.com/CloudI/cloudi_service_db_couchdb
pkg_cloudi_service_db_couchdb_commit = master

PACKAGES += cloudi_service_db_elasticsearch
pkg_cloudi_service_db_elasticsearch_name = cloudi_service_db_elasticsearch
pkg_cloudi_service_db_elasticsearch_description = elasticsearch CloudI Service
pkg_cloudi_service_db_elasticsearch_homepage = http://cloudi.org/
pkg_cloudi_service_db_elasticsearch_fetch = git
pkg_cloudi_service_db_elasticsearch_repo = https://github.com/CloudI/cloudi_service_db_elasticsearch
pkg_cloudi_service_db_elasticsearch_commit = master

PACKAGES += cloudi_service_db_memcached
pkg_cloudi_service_db_memcached_name = cloudi_service_db_memcached
pkg_cloudi_service_db_memcached_description = memcached CloudI Service
pkg_cloudi_service_db_memcached_homepage = http://cloudi.org/
pkg_cloudi_service_db_memcached_fetch = git
pkg_cloudi_service_db_memcached_repo = https://github.com/CloudI/cloudi_service_db_memcached
pkg_cloudi_service_db_memcached_commit = master

PACKAGES += cloudi_service_db
pkg_cloudi_service_db_name = cloudi_service_db
pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic)
pkg_cloudi_service_db_homepage = http://cloudi.org/
pkg_cloudi_service_db_fetch = git
pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db
pkg_cloudi_service_db_commit = master

PACKAGES += cloudi_service_db_mysql
pkg_cloudi_service_db_mysql_name = cloudi_service_db_mysql
pkg_cloudi_service_db_mysql_description = MySQL CloudI Service
pkg_cloudi_service_db_mysql_homepage = http://cloudi.org/
pkg_cloudi_service_db_mysql_fetch = git
pkg_cloudi_service_db_mysql_repo = https://github.com/CloudI/cloudi_service_db_mysql
pkg_cloudi_service_db_mysql_commit = master

PACKAGES += cloudi_service_db_pgsql
pkg_cloudi_service_db_pgsql_name = cloudi_service_db_pgsql
pkg_cloudi_service_db_pgsql_description = PostgreSQL CloudI Service
pkg_cloudi_service_db_pgsql_homepage = http://cloudi.org/
pkg_cloudi_service_db_pgsql_fetch = git
pkg_cloudi_service_db_pgsql_repo = https://github.com/CloudI/cloudi_service_db_pgsql
pkg_cloudi_service_db_pgsql_commit = master

PACKAGES += cloudi_service_db_riak
pkg_cloudi_service_db_riak_name = cloudi_service_db_riak
pkg_cloudi_service_db_riak_description = Riak CloudI Service
pkg_cloudi_service_db_riak_homepage = http://cloudi.org/
pkg_cloudi_service_db_riak_fetch = git
pkg_cloudi_service_db_riak_repo = https://github.com/CloudI/cloudi_service_db_riak
pkg_cloudi_service_db_riak_commit = master

PACKAGES += cloudi_service_db_tokyotyrant
pkg_cloudi_service_db_tokyotyrant_name = cloudi_service_db_tokyotyrant
pkg_cloudi_service_db_tokyotyrant_description = Tokyo Tyrant CloudI Service
pkg_cloudi_service_db_tokyotyrant_homepage = http://cloudi.org/
pkg_cloudi_service_db_tokyotyrant_fetch = git
pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant
pkg_cloudi_service_db_tokyotyrant_commit = master

PACKAGES += cloudi_service_filesystem
pkg_cloudi_service_filesystem_name = cloudi_service_filesystem
pkg_cloudi_service_filesystem_description = Filesystem CloudI Service
pkg_cloudi_service_filesystem_homepage = http://cloudi.org/
pkg_cloudi_service_filesystem_fetch = git
pkg_cloudi_service_filesystem_repo = https://github.com/CloudI/cloudi_service_filesystem
pkg_cloudi_service_filesystem_commit = master

PACKAGES += cloudi_service_http_client
pkg_cloudi_service_http_client_name = cloudi_service_http_client
pkg_cloudi_service_http_client_description = HTTP client CloudI Service
pkg_cloudi_service_http_client_homepage = http://cloudi.org/
pkg_cloudi_service_http_client_fetch = git
pkg_cloudi_service_http_client_repo = https://github.com/CloudI/cloudi_service_http_client
pkg_cloudi_service_http_client_commit = master

PACKAGES += cloudi_service_http_cowboy
pkg_cloudi_service_http_cowboy_name = cloudi_service_http_cowboy
pkg_cloudi_service_http_cowboy_description = cowboy HTTP/HTTPS CloudI Service
pkg_cloudi_service_http_cowboy_homepage = http://cloudi.org/
pkg_cloudi_service_http_cowboy_fetch = git
pkg_cloudi_service_http_cowboy_repo = https://github.com/CloudI/cloudi_service_http_cowboy
pkg_cloudi_service_http_cowboy_commit = master

PACKAGES += cloudi_service_http_elli
pkg_cloudi_service_http_elli_name = cloudi_service_http_elli
pkg_cloudi_service_http_elli_description = elli HTTP CloudI Service
pkg_cloudi_service_http_elli_homepage = http://cloudi.org/
pkg_cloudi_service_http_elli_fetch = git
pkg_cloudi_service_http_elli_repo = https://github.com/CloudI/cloudi_service_http_elli
pkg_cloudi_service_http_elli_commit = master

PACKAGES += cloudi_service_map_reduce
pkg_cloudi_service_map_reduce_name = cloudi_service_map_reduce
pkg_cloudi_service_map_reduce_description = Map/Reduce CloudI Service
pkg_cloudi_service_map_reduce_homepage = http://cloudi.org/
pkg_cloudi_service_map_reduce_fetch = git
pkg_cloudi_service_map_reduce_repo = https://github.com/CloudI/cloudi_service_map_reduce
pkg_cloudi_service_map_reduce_commit = master

PACKAGES += cloudi_service_oauth1
pkg_cloudi_service_oauth1_name = cloudi_service_oauth1
pkg_cloudi_service_oauth1_description = OAuth v1.0 CloudI Service
pkg_cloudi_service_oauth1_homepage = http://cloudi.org/
pkg_cloudi_service_oauth1_fetch = git
pkg_cloudi_service_oauth1_repo = https://github.com/CloudI/cloudi_service_oauth1
pkg_cloudi_service_oauth1_commit = master

PACKAGES += cloudi_service_queue
pkg_cloudi_service_queue_name = cloudi_service_queue
pkg_cloudi_service_queue_description = Persistent Queue Service
pkg_cloudi_service_queue_homepage = http://cloudi.org/
pkg_cloudi_service_queue_fetch = git
pkg_cloudi_service_queue_repo = https://github.com/CloudI/cloudi_service_queue
pkg_cloudi_service_queue_commit = master

PACKAGES += cloudi_service_quorum
pkg_cloudi_service_quorum_name = cloudi_service_quorum
pkg_cloudi_service_quorum_description = CloudI Quorum Service
pkg_cloudi_service_quorum_homepage = http://cloudi.org/
pkg_cloudi_service_quorum_fetch = git
pkg_cloudi_service_quorum_repo = https://github.com/CloudI/cloudi_service_quorum
pkg_cloudi_service_quorum_commit = master

PACKAGES += cloudi_service_router
pkg_cloudi_service_router_name = cloudi_service_router
pkg_cloudi_service_router_description = CloudI Router Service
pkg_cloudi_service_router_homepage = http://cloudi.org/
pkg_cloudi_service_router_fetch = git
pkg_cloudi_service_router_repo = https://github.com/CloudI/cloudi_service_router
pkg_cloudi_service_router_commit = master

PACKAGES += cloudi_service_tcp
pkg_cloudi_service_tcp_name = cloudi_service_tcp
pkg_cloudi_service_tcp_description = TCP CloudI Service
pkg_cloudi_service_tcp_homepage = http://cloudi.org/
pkg_cloudi_service_tcp_fetch = git
pkg_cloudi_service_tcp_repo = https://github.com/CloudI/cloudi_service_tcp
pkg_cloudi_service_tcp_commit = master

PACKAGES += cloudi_service_timers
pkg_cloudi_service_timers_name = cloudi_service_timers
pkg_cloudi_service_timers_description = Timers CloudI Service
pkg_cloudi_service_timers_homepage = http://cloudi.org/
pkg_cloudi_service_timers_fetch = git
pkg_cloudi_service_timers_repo = https://github.com/CloudI/cloudi_service_timers
pkg_cloudi_service_timers_commit = master

PACKAGES += cloudi_service_udp
pkg_cloudi_service_udp_name = cloudi_service_udp
pkg_cloudi_service_udp_description = UDP CloudI Service
pkg_cloudi_service_udp_homepage = http://cloudi.org/
pkg_cloudi_service_udp_fetch = git
pkg_cloudi_service_udp_repo = https://github.com/CloudI/cloudi_service_udp
pkg_cloudi_service_udp_commit = master

PACKAGES += cloudi_service_validate
pkg_cloudi_service_validate_name = cloudi_service_validate
pkg_cloudi_service_validate_description = CloudI Validate Service
pkg_cloudi_service_validate_homepage = http://cloudi.org/
pkg_cloudi_service_validate_fetch = git
pkg_cloudi_service_validate_repo = https://github.com/CloudI/cloudi_service_validate
pkg_cloudi_service_validate_commit = master

PACKAGES += cloudi_service_zeromq
pkg_cloudi_service_zeromq_name = cloudi_service_zeromq
pkg_cloudi_service_zeromq_description = ZeroMQ CloudI Service
pkg_cloudi_service_zeromq_homepage = http://cloudi.org/
pkg_cloudi_service_zeromq_fetch = git
pkg_cloudi_service_zeromq_repo = https://github.com/CloudI/cloudi_service_zeromq
pkg_cloudi_service_zeromq_commit = master

PACKAGES += cluster_info
pkg_cluster_info_name = cluster_info
pkg_cluster_info_description = Fork of Hibari's nifty cluster_info OTP app
pkg_cluster_info_homepage = https://github.com/basho/cluster_info
pkg_cluster_info_fetch = git
pkg_cluster_info_repo = https://github.com/basho/cluster_info
pkg_cluster_info_commit = master

PACKAGES += color
pkg_color_name = color
pkg_color_description = ANSI colors for your Erlang
pkg_color_homepage = https://github.com/julianduque/erlang-color
pkg_color_fetch = git
pkg_color_repo = https://github.com/julianduque/erlang-color
pkg_color_commit = master

PACKAGES += confetti
pkg_confetti_name = confetti
pkg_confetti_description = Erlang configuration provider / application:get_env/2 on steroids
pkg_confetti_homepage = https://github.com/jtendo/confetti
pkg_confetti_fetch = git
pkg_confetti_repo = https://github.com/jtendo/confetti
pkg_confetti_commit = master

PACKAGES += couchbeam
pkg_couchbeam_name = couchbeam
pkg_couchbeam_description = Apache CouchDB client in Erlang
pkg_couchbeam_homepage = https://github.com/benoitc/couchbeam
pkg_couchbeam_fetch = git
pkg_couchbeam_repo = https://github.com/benoitc/couchbeam
pkg_couchbeam_commit = master

PACKAGES += couch
pkg_couch_name = couch
pkg_couch_description = A embeddable document oriented database compatible with Apache CouchDB
pkg_couch_homepage = https://github.com/benoitc/opencouch
pkg_couch_fetch = git
pkg_couch_repo = https://github.com/benoitc/opencouch
pkg_couch_commit = master

PACKAGES += covertool
pkg_covertool_name = covertool
pkg_covertool_description = Tool to convert Erlang cover data files into Cobertura XML reports
pkg_covertool_homepage = https://github.com/idubrov/covertool
pkg_covertool_fetch = git
pkg_covertool_repo = https://github.com/idubrov/covertool
pkg_covertool_commit = master

PACKAGES += cowboy
pkg_cowboy_name = cowboy
pkg_cowboy_description = Small, fast and modular HTTP server.
pkg_cowboy_homepage = http://ninenines.eu
pkg_cowboy_fetch = git
pkg_cowboy_repo = https://github.com/ninenines/cowboy
pkg_cowboy_commit = 1.0.1

PACKAGES += cowdb
pkg_cowdb_name = cowdb
pkg_cowdb_description = Pure Key/Value database library for Erlang Applications
pkg_cowdb_homepage = https://github.com/refuge/cowdb
pkg_cowdb_fetch = git
pkg_cowdb_repo = https://github.com/refuge/cowdb
pkg_cowdb_commit = master

PACKAGES += cowlib
pkg_cowlib_name = cowlib
pkg_cowlib_description = Support library for manipulating Web protocols.
pkg_cowlib_homepage = http://ninenines.eu
pkg_cowlib_fetch = git
pkg_cowlib_repo = https://github.com/ninenines/cowlib
pkg_cowlib_commit = 1.0.1

PACKAGES += cpg
pkg_cpg_name = cpg
pkg_cpg_description = CloudI Process Groups
pkg_cpg_homepage = https://github.com/okeuday/cpg
pkg_cpg_fetch = git
pkg_cpg_repo = https://github.com/okeuday/cpg
pkg_cpg_commit = master

PACKAGES += cqerl
pkg_cqerl_name = cqerl
pkg_cqerl_description = Native Erlang CQL client for Cassandra
pkg_cqerl_homepage = https://matehat.github.io/cqerl/
pkg_cqerl_fetch = git
pkg_cqerl_repo = https://github.com/matehat/cqerl
pkg_cqerl_commit = master

PACKAGES += cr
pkg_cr_name = cr
pkg_cr_description = Chain Replication
pkg_cr_homepage = https://synrc.com/apps/cr/doc/cr.htm
pkg_cr_fetch = git
pkg_cr_repo = https://github.com/spawnproc/cr
pkg_cr_commit = master

PACKAGES += cuttlefish
pkg_cuttlefish_name = cuttlefish
pkg_cuttlefish_description = never lose your childlike sense of wonder baby cuttlefish, promise me?
pkg_cuttlefish_homepage = https://github.com/basho/cuttlefish
pkg_cuttlefish_fetch = git
pkg_cuttlefish_repo = https://github.com/basho/cuttlefish
pkg_cuttlefish_commit = master

PACKAGES += damocles
pkg_damocles_name = damocles
pkg_damocles_description = Erlang library for generating adversarial network conditions for QAing distributed applications/systems on a single Linux box.
pkg_damocles_homepage = https://github.com/lostcolony/damocles
pkg_damocles_fetch = git
pkg_damocles_repo = https://github.com/lostcolony/damocles
pkg_damocles_commit = master

PACKAGES += debbie
pkg_debbie_name = debbie
pkg_debbie_description = .DEB Built In Erlang
pkg_debbie_homepage = https://github.com/crownedgrouse/debbie
pkg_debbie_fetch = git
pkg_debbie_repo = https://github.com/crownedgrouse/debbie
pkg_debbie_commit = master

PACKAGES += decimal
pkg_decimal_name = decimal
pkg_decimal_description = An Erlang decimal arithmetic library
pkg_decimal_homepage = https://github.com/tim/erlang-decimal
pkg_decimal_fetch = git
pkg_decimal_repo = https://github.com/tim/erlang-decimal
pkg_decimal_commit = master

PACKAGES += detergent
pkg_detergent_name = detergent
pkg_detergent_description = An emulsifying Erlang SOAP library
pkg_detergent_homepage = https://github.com/devinus/detergent
pkg_detergent_fetch = git
pkg_detergent_repo = https://github.com/devinus/detergent
pkg_detergent_commit = master

PACKAGES += detest
pkg_detest_name = detest
pkg_detest_description = Tool for running tests on a cluster of erlang nodes
pkg_detest_homepage = https://github.com/biokoda/detest
pkg_detest_fetch = git
pkg_detest_repo = https://github.com/biokoda/detest
pkg_detest_commit = master

PACKAGES += dh_date
pkg_dh_date_name = dh_date
pkg_dh_date_description = Date formatting / parsing library for erlang
pkg_dh_date_homepage = https://github.com/daleharvey/dh_date
pkg_dh_date_fetch = git
pkg_dh_date_repo = https://github.com/daleharvey/dh_date
pkg_dh_date_commit = master

PACKAGES += dhtcrawler
pkg_dhtcrawler_name = dhtcrawler
pkg_dhtcrawler_description = dhtcrawler is a DHT crawler written in erlang. It can join a DHT network and crawl many P2P torrents.
pkg_dhtcrawler_homepage = https://github.com/kevinlynx/dhtcrawler
pkg_dhtcrawler_fetch = git
pkg_dhtcrawler_repo = https://github.com/kevinlynx/dhtcrawler
pkg_dhtcrawler_commit = master

PACKAGES += dirbusterl
pkg_dirbusterl_name = dirbusterl
pkg_dirbusterl_description = DirBuster successor in Erlang
pkg_dirbusterl_homepage = https://github.com/silentsignal/DirBustErl
pkg_dirbusterl_fetch = git
pkg_dirbusterl_repo = https://github.com/silentsignal/DirBustErl
pkg_dirbusterl_commit = master

PACKAGES += dispcount
pkg_dispcount_name = dispcount
pkg_dispcount_description = Erlang task dispatcher based on ETS counters.
pkg_dispcount_homepage = https://github.com/ferd/dispcount
pkg_dispcount_fetch = git
pkg_dispcount_repo = https://github.com/ferd/dispcount
pkg_dispcount_commit = master

PACKAGES += dlhttpc
pkg_dlhttpc_name = dlhttpc
pkg_dlhttpc_description = dispcount-based lhttpc fork for massive amounts of requests to limited endpoints
pkg_dlhttpc_homepage = https://github.com/ferd/dlhttpc
pkg_dlhttpc_fetch = git
pkg_dlhttpc_repo = https://github.com/ferd/dlhttpc
pkg_dlhttpc_commit = master

PACKAGES += dns
pkg_dns_name = dns
pkg_dns_description = Erlang DNS library
pkg_dns_homepage = https://github.com/aetrion/dns_erlang
pkg_dns_fetch = git
pkg_dns_repo = https://github.com/aetrion/dns_erlang
pkg_dns_commit = master

PACKAGES += dnssd
pkg_dnssd_name = dnssd
pkg_dnssd_description = Erlang interface to Apple's Bonjour D    NS Service Discovery implementation
pkg_dnssd_homepage = https://github.com/benoitc/dnssd_erlang
pkg_dnssd_fetch = git
pkg_dnssd_repo = https://github.com/benoitc/dnssd_erlang
pkg_dnssd_commit = master

PACKAGES += dtl
pkg_dtl_name = dtl
pkg_dtl_description = Django Template Language: A full-featured port of the Django template engine to Erlang.
pkg_dtl_homepage = https://github.com/oinksoft/dtl
pkg_dtl_fetch = git
pkg_dtl_repo = https://github.com/oinksoft/dtl
pkg_dtl_commit = master

PACKAGES += dynamic_compile
pkg_dynamic_compile_name = dynamic_compile
pkg_dynamic_compile_description = compile and load erlang modules from string input
pkg_dynamic_compile_homepage = https://github.com/jkvor/dynamic_compile
pkg_dynamic_compile_fetch = git
pkg_dynamic_compile_repo = https://github.com/jkvor/dynamic_compile
pkg_dynamic_compile_commit = master

PACKAGES += e2
pkg_e2_name = e2
pkg_e2_description = Library to simply writing correct OTP applications.
pkg_e2_homepage = http://e2project.org
pkg_e2_fetch = git
pkg_e2_repo = https://github.com/gar1t/e2
pkg_e2_commit = master

PACKAGES += eamf
pkg_eamf_name = eamf
pkg_eamf_description = eAMF provides Action Message Format (AMF) support for Erlang
pkg_eamf_homepage = https://github.com/mrinalwadhwa/eamf
pkg_eamf_fetch = git
pkg_eamf_repo = https://github.com/mrinalwadhwa/eamf
pkg_eamf_commit = master

PACKAGES += eavro
pkg_eavro_name = eavro
pkg_eavro_description = Apache Avro encoder/decoder
pkg_eavro_homepage = https://github.com/SIfoxDevTeam/eavro
pkg_eavro_fetch = git
pkg_eavro_repo = https://github.com/SIfoxDevTeam/eavro
pkg_eavro_commit = master

PACKAGES += ecapnp
pkg_ecapnp_name = ecapnp
pkg_ecapnp_description = Cap'n Proto library for Erlang
pkg_ecapnp_homepage = https://github.com/kaos/ecapnp
pkg_ecapnp_fetch = git
pkg_ecapnp_repo = https://github.com/kaos/ecapnp
pkg_ecapnp_commit = master

PACKAGES += econfig
pkg_econfig_name = econfig
pkg_econfig_description = simple Erlang config handler using INI files
pkg_econfig_homepage = https://github.com/benoitc/econfig
pkg_econfig_fetch = git
pkg_econfig_repo = https://github.com/benoitc/econfig
pkg_econfig_commit = master

PACKAGES += edate
pkg_edate_name = edate
pkg_edate_description = date manipulation library for erlang
pkg_edate_homepage = https://github.com/dweldon/edate
pkg_edate_fetch = git
pkg_edate_repo = https://github.com/dweldon/edate
pkg_edate_commit = master

PACKAGES += edgar
pkg_edgar_name = edgar
pkg_edgar_description = Erlang Does GNU AR
pkg_edgar_homepage = https://github.com/crownedgrouse/edgar
pkg_edgar_fetch = git
pkg_edgar_repo = https://github.com/crownedgrouse/edgar
pkg_edgar_commit = master

PACKAGES += edis
pkg_edis_name = edis
pkg_edis_description = An Erlang implementation of Redis KV Store
pkg_edis_homepage = http://inaka.github.com/edis/
pkg_edis_fetch = git
pkg_edis_repo = https://github.com/inaka/edis
pkg_edis_commit = master

PACKAGES += edns
pkg_edns_name = edns
pkg_edns_description = Erlang/OTP DNS server
pkg_edns_homepage = https://github.com/hcvst/erlang-dns
pkg_edns_fetch = git
pkg_edns_repo = https://github.com/hcvst/erlang-dns
pkg_edns_commit = master

PACKAGES += edown
pkg_edown_name = edown
pkg_edown_description = EDoc extension for generating Github-flavored Markdown
pkg_edown_homepage = https://github.com/uwiger/edown
pkg_edown_fetch = git
pkg_edown_repo = https://github.com/uwiger/edown
pkg_edown_commit = master

PACKAGES += eep_app
pkg_eep_app_name = eep_app
pkg_eep_app_description = Embedded Event Processing
pkg_eep_app_homepage = https://github.com/darach/eep-erl
pkg_eep_app_fetch = git
pkg_eep_app_repo = https://github.com/darach/eep-erl
pkg_eep_app_commit = master

PACKAGES += eep
pkg_eep_name = eep
pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy
pkg_eep_homepage = https://github.com/virtan/eep
pkg_eep_fetch = git
pkg_eep_repo = https://github.com/virtan/eep
pkg_eep_commit = master

PACKAGES += efene
pkg_efene_name = efene
pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX
pkg_efene_homepage = https://github.com/efene/efene
pkg_efene_fetch = git
pkg_efene_repo = https://github.com/efene/efene
pkg_efene_commit = master

PACKAGES += eganglia
pkg_eganglia_name = eganglia
pkg_eganglia_description = Erlang library to interact with Ganglia
pkg_eganglia_homepage = https://github.com/inaka/eganglia
pkg_eganglia_fetch = git
pkg_eganglia_repo = https://github.com/inaka/eganglia
pkg_eganglia_commit = v0.9.1

PACKAGES += egeoip
pkg_egeoip_name = egeoip
pkg_egeoip_description = Erlang IP Geolocation module, currently supporting the MaxMind GeoLite City Database.
pkg_egeoip_homepage = https://github.com/mochi/egeoip
pkg_egeoip_fetch = git
pkg_egeoip_repo = https://github.com/mochi/egeoip
pkg_egeoip_commit = master

PACKAGES += ehsa
pkg_ehsa_name = ehsa
pkg_ehsa_description = Erlang HTTP server basic and digest authentication modules
pkg_ehsa_homepage = https://bitbucket.org/a12n/ehsa
pkg_ehsa_fetch = hg
pkg_ehsa_repo = https://bitbucket.org/a12n/ehsa
pkg_ehsa_commit = 2.0.4

PACKAGES += ejabberd
pkg_ejabberd_name = ejabberd
pkg_ejabberd_description = Robust, ubiquitous and massively scalable Jabber / XMPP Instant Messaging platform
pkg_ejabberd_homepage = https://github.com/processone/ejabberd
pkg_ejabberd_fetch = git
pkg_ejabberd_repo = https://github.com/processone/ejabberd
pkg_ejabberd_commit = master

PACKAGES += ej
pkg_ej_name = ej
pkg_ej_description = Helper module for working with Erlang terms representing JSON
pkg_ej_homepage = https://github.com/seth/ej
pkg_ej_fetch = git
pkg_ej_repo = https://github.com/seth/ej
pkg_ej_commit = master

PACKAGES += ejwt
pkg_ejwt_name = ejwt
pkg_ejwt_description = erlang library for JSON Web Token
pkg_ejwt_homepage = https://github.com/artefactop/ejwt
pkg_ejwt_fetch = git
pkg_ejwt_repo = https://github.com/artefactop/ejwt
pkg_ejwt_commit = master

PACKAGES += ekaf
pkg_ekaf_name = ekaf
pkg_ekaf_description = A minimal, high-performance Kafka client in Erlang.
pkg_ekaf_homepage = https://github.com/helpshift/ekaf
pkg_ekaf_fetch = git
pkg_ekaf_repo = https://github.com/helpshift/ekaf
pkg_ekaf_commit = master

PACKAGES += elarm
pkg_elarm_name = elarm
pkg_elarm_description = Alarm Manager for Erlang.
pkg_elarm_homepage = https://github.com/esl/elarm
pkg_elarm_fetch = git
pkg_elarm_repo = https://github.com/esl/elarm
pkg_elarm_commit = master

PACKAGES += eleveldb
pkg_eleveldb_name = eleveldb
pkg_eleveldb_description = Erlang LevelDB API
pkg_eleveldb_homepage = https://github.com/basho/eleveldb
pkg_eleveldb_fetch = git
pkg_eleveldb_repo = https://github.com/basho/eleveldb
pkg_eleveldb_commit = master

PACKAGES += elli
pkg_elli_name = elli
pkg_elli_description = Simple, robust and performant Erlang web server
pkg_elli_homepage = https://github.com/knutin/elli
pkg_elli_fetch = git
pkg_elli_repo = https://github.com/knutin/elli
pkg_elli_commit = master

PACKAGES += elvis
pkg_elvis_name = elvis
pkg_elvis_description = Erlang Style Reviewer
pkg_elvis_homepage = https://github.com/inaka/elvis
pkg_elvis_fetch = git
pkg_elvis_repo = https://github.com/inaka/elvis
pkg_elvis_commit = 0.2.4

PACKAGES += emagick
pkg_emagick_name = emagick
pkg_emagick_description = Wrapper for Graphics/ImageMagick command line tool.
pkg_emagick_homepage = https://github.com/kivra/emagick
pkg_emagick_fetch = git
pkg_emagick_repo = https://github.com/kivra/emagick
pkg_emagick_commit = master

PACKAGES += emysql
pkg_emysql_name = emysql
pkg_emysql_description = Stable, pure Erlang MySQL driver.
pkg_emysql_homepage = https://github.com/Eonblast/Emysql
pkg_emysql_fetch = git
pkg_emysql_repo = https://github.com/Eonblast/Emysql
pkg_emysql_commit = master

PACKAGES += enm
pkg_enm_name = enm
pkg_enm_description = Erlang driver for nanomsg
pkg_enm_homepage = https://github.com/basho/enm
pkg_enm_fetch = git
pkg_enm_repo = https://github.com/basho/enm
pkg_enm_commit = master

PACKAGES += entop
pkg_entop_name = entop
pkg_entop_description = A top-like tool for monitoring an Erlang node
pkg_entop_homepage = https://github.com/mazenharake/entop
pkg_entop_fetch = git
pkg_entop_repo = https://github.com/mazenharake/entop
pkg_entop_commit = master

PACKAGES += epcap
pkg_epcap_name = epcap
pkg_epcap_description = Erlang packet capture interface using pcap
pkg_epcap_homepage = https://github.com/msantos/epcap
pkg_epcap_fetch = git
pkg_epcap_repo = https://github.com/msantos/epcap
pkg_epcap_commit = master

PACKAGES += eper
pkg_eper_name = eper
pkg_eper_description = Erlang performance and debugging tools.
pkg_eper_homepage = https://github.com/massemanet/eper
pkg_eper_fetch = git
pkg_eper_repo = https://github.com/massemanet/eper
pkg_eper_commit = master

PACKAGES += epgsql
pkg_epgsql_name = epgsql
pkg_epgsql_description = Erlang PostgreSQL client library.
pkg_epgsql_homepage = https://github.com/epgsql/epgsql
pkg_epgsql_fetch = git
pkg_epgsql_repo = https://github.com/epgsql/epgsql
pkg_epgsql_commit = master

PACKAGES += episcina
pkg_episcina_name = episcina
pkg_episcina_description = A simple non intrusive resource pool for connections
pkg_episcina_homepage = https://github.com/erlware/episcina
pkg_episcina_fetch = git
pkg_episcina_repo = https://github.com/erlware/episcina
pkg_episcina_commit = master

PACKAGES += eplot
pkg_eplot_name = eplot
pkg_eplot_description = A plot engine written in erlang.
pkg_eplot_homepage = https://github.com/psyeugenic/eplot
pkg_eplot_fetch = git
pkg_eplot_repo = https://github.com/psyeugenic/eplot
pkg_eplot_commit = master

PACKAGES += epocxy
pkg_epocxy_name = epocxy
pkg_epocxy_description = Erlang Patterns of Concurrency
pkg_epocxy_homepage = https://github.com/duomark/epocxy
pkg_epocxy_fetch = git
pkg_epocxy_repo = https://github.com/duomark/epocxy
pkg_epocxy_commit = master

PACKAGES += epubnub
pkg_epubnub_name = epubnub
pkg_epubnub_description = Erlang PubNub API
pkg_epubnub_homepage = https://github.com/tsloughter/epubnub
pkg_epubnub_fetch = git
pkg_epubnub_repo = https://github.com/tsloughter/epubnub
pkg_epubnub_commit = master

PACKAGES += eqm
pkg_eqm_name = eqm
pkg_eqm_description = Erlang pub sub with supply-demand channels
pkg_eqm_homepage = https://github.com/loucash/eqm
pkg_eqm_fetch = git
pkg_eqm_repo = https://github.com/loucash/eqm
pkg_eqm_commit = master

PACKAGES += eredis
pkg_eredis_name = eredis
pkg_eredis_description = Erlang Redis client
pkg_eredis_homepage = https://github.com/wooga/eredis
pkg_eredis_fetch = git
pkg_eredis_repo = https://github.com/wooga/eredis
pkg_eredis_commit = master

PACKAGES += eredis_pool
pkg_eredis_pool_name = eredis_pool
pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy.
pkg_eredis_pool_homepage = https://github.com/hiroeorz/eredis_pool
pkg_eredis_pool_fetch = git
pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool
pkg_eredis_pool_commit = master

PACKAGES += erlang_cep
pkg_erlang_cep_name = erlang_cep
pkg_erlang_cep_description = A basic CEP package written in erlang
pkg_erlang_cep_homepage = https://github.com/danmacklin/erlang_cep
pkg_erlang_cep_fetch = git
pkg_erlang_cep_repo = https://github.com/danmacklin/erlang_cep
pkg_erlang_cep_commit = master

PACKAGES += erlang_js
pkg_erlang_js_name = erlang_js
pkg_erlang_js_description = A linked-in driver for Erlang to Mozilla's Spidermonkey Javascript runtime.
pkg_erlang_js_homepage = https://github.com/basho/erlang_js
pkg_erlang_js_fetch = git
pkg_erlang_js_repo = https://github.com/basho/erlang_js
pkg_erlang_js_commit = master

PACKAGES += erlang_localtime
pkg_erlang_localtime_name = erlang_localtime
pkg_erlang_localtime_description = Erlang library for conversion from one local time to another
pkg_erlang_localtime_homepage = https://github.com/dmitryme/erlang_localtime
pkg_erlang_localtime_fetch = git
pkg_erlang_localtime_repo = https://github.com/dmitryme/erlang_localtime
pkg_erlang_localtime_commit = master

PACKAGES += erlang_smtp
pkg_erlang_smtp_name = erlang_smtp
pkg_erlang_smtp_description = Erlang SMTP and POP3 server code.
pkg_erlang_smtp_homepage = https://github.com/tonyg/erlang-smtp
pkg_erlang_smtp_fetch = git
pkg_erlang_smtp_repo = https://github.com/tonyg/erlang-smtp
pkg_erlang_smtp_commit = master

PACKAGES += erlang_term
pkg_erlang_term_name = erlang_term
pkg_erlang_term_description = Erlang Term Info
pkg_erlang_term_homepage = https://github.com/okeuday/erlang_term
pkg_erlang_term_fetch = git
pkg_erlang_term_repo = https://github.com/okeuday/erlang_term
pkg_erlang_term_commit = master

PACKAGES += erlastic_search
pkg_erlastic_search_name = erlastic_search
pkg_erlastic_search_description = An Erlang app for communicating with Elastic Search's rest interface.
pkg_erlastic_search_homepage = https://github.com/tsloughter/erlastic_search
pkg_erlastic_search_fetch = git
pkg_erlastic_search_repo = https://github.com/tsloughter/erlastic_search
pkg_erlastic_search_commit = master

PACKAGES += erlasticsearch
pkg_erlasticsearch_name = erlasticsearch
pkg_erlasticsearch_description = Erlang thrift interface to elastic_search
pkg_erlasticsearch_homepage = https://github.com/dieswaytoofast/erlasticsearch
pkg_erlasticsearch_fetch = git
pkg_erlasticsearch_repo = https://github.com/dieswaytoofast/erlasticsearch
pkg_erlasticsearch_commit = master

PACKAGES += erlbrake
pkg_erlbrake_name = erlbrake
pkg_erlbrake_description = Erlang Airbrake notification client
pkg_erlbrake_homepage = https://github.com/kenpratt/erlbrake
pkg_erlbrake_fetch = git
pkg_erlbrake_repo = https://github.com/kenpratt/erlbrake
pkg_erlbrake_commit = master

PACKAGES += erlcloud
pkg_erlcloud_name = erlcloud
pkg_erlcloud_description = Cloud Computing library for erlang (Amazon EC2, S3, SQS, SimpleDB, Mechanical Turk, ELB)
pkg_erlcloud_homepage = https://github.com/gleber/erlcloud
pkg_erlcloud_fetch = git
pkg_erlcloud_repo = https://github.com/gleber/erlcloud
pkg_erlcloud_commit = master

PACKAGES += erlcron
pkg_erlcron_name = erlcron
pkg_erlcron_description = Erlang cronish system
pkg_erlcron_homepage = https://github.com/erlware/erlcron
pkg_erlcron_fetch = git
pkg_erlcron_repo = https://github.com/erlware/erlcron
pkg_erlcron_commit = master

PACKAGES += erldb
pkg_erldb_name = erldb
pkg_erldb_description = ORM (Object-relational mapping) application implemented in Erlang
pkg_erldb_homepage = http://erldb.org
pkg_erldb_fetch = git
pkg_erldb_repo = https://github.com/erldb/erldb
pkg_erldb_commit = master

PACKAGES += erldis
pkg_erldis_name = erldis
pkg_erldis_description = redis erlang client library
pkg_erldis_homepage = https://github.com/cstar/erldis
pkg_erldis_fetch = git
pkg_erldis_repo = https://github.com/cstar/erldis
pkg_erldis_commit = master

PACKAGES += erldns
pkg_erldns_name = erldns
pkg_erldns_description = DNS server, in erlang.
pkg_erldns_homepage = https://github.com/aetrion/erl-dns
pkg_erldns_fetch = git
pkg_erldns_repo = https://github.com/aetrion/erl-dns
pkg_erldns_commit = master

PACKAGES += erldocker
pkg_erldocker_name = erldocker
pkg_erldocker_description = Docker Remote API client for Erlang
pkg_erldocker_homepage = https://github.com/proger/erldocker
pkg_erldocker_fetch = git
pkg_erldocker_repo = https://github.com/proger/erldocker
pkg_erldocker_commit = master

PACKAGES += erlfsmon
pkg_erlfsmon_name = erlfsmon
pkg_erlfsmon_description = Erlang filesystem event watcher for Linux and OSX
pkg_erlfsmon_homepage = https://github.com/proger/erlfsmon
pkg_erlfsmon_fetch = git
pkg_erlfsmon_repo = https://github.com/proger/erlfsmon
pkg_erlfsmon_commit = master

PACKAGES += erlgit
pkg_erlgit_name = erlgit
pkg_erlgit_description = Erlang convenience wrapper around git executable
pkg_erlgit_homepage = https://github.com/gleber/erlgit
pkg_erlgit_fetch = git
pkg_erlgit_repo = https://github.com/gleber/erlgit
pkg_erlgit_commit = master

PACKAGES += erlguten
pkg_erlguten_name = erlguten
pkg_erlguten_description = ErlGuten is a system for high-quality typesetting, written purely in Erlang.
pkg_erlguten_homepage = https://github.com/richcarl/erlguten
pkg_erlguten_fetch = git
pkg_erlguten_repo = https://github.com/richcarl/erlguten
pkg_erlguten_commit = master

PACKAGES += erlmc
pkg_erlmc_name = erlmc
pkg_erlmc_description = Erlang memcached binary protocol client
pkg_erlmc_homepage = https://github.com/jkvor/erlmc
pkg_erlmc_fetch = git
pkg_erlmc_repo = https://github.com/jkvor/erlmc
pkg_erlmc_commit = master

PACKAGES += erlmongo
pkg_erlmongo_name = erlmongo
pkg_erlmongo_description = Record based Erlang driver for MongoDB with gridfs support
pkg_erlmongo_homepage = https://github.com/SergejJurecko/erlmongo
pkg_erlmongo_fetch = git
pkg_erlmongo_repo = https://github.com/SergejJurecko/erlmongo
pkg_erlmongo_commit = master

PACKAGES += erlog
pkg_erlog_name = erlog
pkg_erlog_description = Prolog interpreter in and for Erlang
pkg_erlog_homepage = https://github.com/rvirding/erlog
pkg_erlog_fetch = git
pkg_erlog_repo = https://github.com/rvirding/erlog
pkg_erlog_commit = master

PACKAGES += erlpass
pkg_erlpass_name = erlpass
pkg_erlpass_description = A library to handle password hashing and changing in a safe manner, independent from any kind of storage whatsoever.
pkg_erlpass_homepage = https://github.com/ferd/erlpass
pkg_erlpass_fetch = git
pkg_erlpass_repo = https://github.com/ferd/erlpass
pkg_erlpass_commit = master

PACKAGES += erlport
pkg_erlport_name = erlport
pkg_erlport_description = ErlPort - connect Erlang to other languages
pkg_erlport_homepage = https://github.com/hdima/erlport
pkg_erlport_fetch = git
pkg_erlport_repo = https://github.com/hdima/erlport
pkg_erlport_commit = master

PACKAGES += erlsha2
pkg_erlsha2_name = erlsha2
pkg_erlsha2_description = SHA-224, SHA-256, SHA-384, SHA-512 implemented in Erlang NIFs.
pkg_erlsha2_homepage = https://github.com/vinoski/erlsha2
pkg_erlsha2_fetch = git
pkg_erlsha2_repo = https://github.com/vinoski/erlsha2
pkg_erlsha2_commit = master

PACKAGES += erlsh
pkg_erlsh_name = erlsh
pkg_erlsh_description = Erlang shell tools
pkg_erlsh_homepage = https://github.com/proger/erlsh
pkg_erlsh_fetch = git
pkg_erlsh_repo = https://github.com/proger/erlsh
pkg_erlsh_commit = master

PACKAGES += erlsom
pkg_erlsom_name = erlsom
pkg_erlsom_description = XML parser for Erlang
pkg_erlsom_homepage = https://github.com/willemdj/erlsom
pkg_erlsom_fetch = git
pkg_erlsom_repo = https://github.com/willemdj/erlsom
pkg_erlsom_commit = master

PACKAGES += erl_streams
pkg_erl_streams_name = erl_streams
pkg_erl_streams_description = Streams in Erlang
pkg_erl_streams_homepage = https://github.com/epappas/erl_streams
pkg_erl_streams_fetch = git
pkg_erl_streams_repo = https://github.com/epappas/erl_streams
pkg_erl_streams_commit = master

PACKAGES += erlubi
pkg_erlubi_name = erlubi
pkg_erlubi_description = Ubigraph Erlang Client (and Process Visualizer)
pkg_erlubi_homepage = https://github.com/krestenkrab/erlubi
pkg_erlubi_fetch = git
pkg_erlubi_repo = https://github.com/krestenkrab/erlubi
pkg_erlubi_commit = master

PACKAGES += erlvolt
pkg_erlvolt_name = erlvolt
pkg_erlvolt_description = VoltDB Erlang Client Driver
pkg_erlvolt_homepage = https://github.com/VoltDB/voltdb-client-erlang
pkg_erlvolt_fetch = git
pkg_erlvolt_repo = https://github.com/VoltDB/voltdb-client-erlang
pkg_erlvolt_commit = master

PACKAGES += erlware_commons
pkg_erlware_commons_name = erlware_commons
pkg_erlware_commons_description = Erlware Commons is an Erlware project focused on all aspects of reusable Erlang components.
pkg_erlware_commons_homepage = https://github.com/erlware/erlware_commons
pkg_erlware_commons_fetch = git
pkg_erlware_commons_repo = https://github.com/erlware/erlware_commons
pkg_erlware_commons_commit = master

PACKAGES += erlydtl
pkg_erlydtl_name = erlydtl
pkg_erlydtl_description = Django Template Language for Erlang.
pkg_erlydtl_homepage = https://github.com/erlydtl/erlydtl
pkg_erlydtl_fetch = git
pkg_erlydtl_repo = https://github.com/erlydtl/erlydtl
pkg_erlydtl_commit = master

PACKAGES += errd
pkg_errd_name = errd
pkg_errd_description = Erlang RRDTool library
pkg_errd_homepage = https://github.com/archaelus/errd
pkg_errd_fetch = git
pkg_errd_repo = https://github.com/archaelus/errd
pkg_errd_commit = master

PACKAGES += erserve
pkg_erserve_name = erserve
pkg_erserve_description = Erlang/Rserve communication interface
pkg_erserve_homepage = https://github.com/del/erserve
pkg_erserve_fetch = git
pkg_erserve_repo = https://github.com/del/erserve
pkg_erserve_commit = master

PACKAGES += erwa
pkg_erwa_name = erwa
pkg_erwa_description = A WAMP router and client written in Erlang.
pkg_erwa_homepage = https://github.com/bwegh/erwa
pkg_erwa_fetch = git
pkg_erwa_repo = https://github.com/bwegh/erwa
pkg_erwa_commit = 0.1.1

PACKAGES += espec
pkg_espec_name = espec
pkg_espec_description = ESpec: Behaviour driven development framework for Erlang
pkg_espec_homepage = https://github.com/lucaspiller/espec
pkg_espec_fetch = git
pkg_espec_repo = https://github.com/lucaspiller/espec
pkg_espec_commit = master

PACKAGES += estatsd
pkg_estatsd_name = estatsd
pkg_estatsd_description = Erlang stats aggregation app that periodically flushes data to graphite
pkg_estatsd_homepage = https://github.com/RJ/estatsd
pkg_estatsd_fetch = git
pkg_estatsd_repo = https://github.com/RJ/estatsd
pkg_estatsd_commit = master

PACKAGES += etap
pkg_etap_name = etap
pkg_etap_description = etap is a simple erlang testing library that provides TAP compliant output.
pkg_etap_homepage = https://github.com/ngerakines/etap
pkg_etap_fetch = git
pkg_etap_repo = https://github.com/ngerakines/etap
pkg_etap_commit = master

PACKAGES += etest_http
pkg_etest_http_name = etest_http
pkg_etest_http_description = etest Assertions around HTTP (client-side)
pkg_etest_http_homepage = https://github.com/wooga/etest_http
pkg_etest_http_fetch = git
pkg_etest_http_repo = https://github.com/wooga/etest_http
pkg_etest_http_commit = master

PACKAGES += etest
pkg_etest_name = etest
pkg_etest_description = A lightweight, convention over configuration test framework for Erlang
pkg_etest_homepage = https://github.com/wooga/etest
pkg_etest_fetch = git
pkg_etest_repo = https://github.com/wooga/etest
pkg_etest_commit = master

PACKAGES += etoml
pkg_etoml_name = etoml
pkg_etoml_description = TOML language erlang parser
pkg_etoml_homepage = https://github.com/kalta/etoml
pkg_etoml_fetch = git
pkg_etoml_repo = https://github.com/kalta/etoml
pkg_etoml_commit = master

PACKAGES += eunit_formatters
pkg_eunit_formatters_name = eunit_formatters
pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better.
pkg_eunit_formatters_homepage = https://github.com/seancribbs/eunit_formatters
pkg_eunit_formatters_fetch = git
pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters
pkg_eunit_formatters_commit = master

PACKAGES += eunit
pkg_eunit_name = eunit
pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository.
pkg_eunit_homepage = https://github.com/richcarl/eunit
pkg_eunit_fetch = git
pkg_eunit_repo = https://github.com/richcarl/eunit
pkg_eunit_commit = master

PACKAGES += euthanasia
pkg_euthanasia_name = euthanasia
pkg_euthanasia_description = Merciful killer for your Erlang processes
pkg_euthanasia_homepage = https://github.com/doubleyou/euthanasia
pkg_euthanasia_fetch = git
pkg_euthanasia_repo = https://github.com/doubleyou/euthanasia
pkg_euthanasia_commit = master

PACKAGES += evum
pkg_evum_name = evum
pkg_evum_description = Spawn Linux VMs as Erlang processes in the Erlang VM
pkg_evum_homepage = https://github.com/msantos/evum
pkg_evum_fetch = git
pkg_evum_repo = https://github.com/msantos/evum
pkg_evum_commit = master

PACKAGES += exec
pkg_exec_name = exec
pkg_exec_description = Execute and control OS processes from Erlang/OTP.
pkg_exec_homepage = http://saleyn.github.com/erlexec
pkg_exec_fetch = git
pkg_exec_repo = https://github.com/saleyn/erlexec
pkg_exec_commit = master

PACKAGES += exml
pkg_exml_name = exml
pkg_exml_description = XML parsing library in Erlang
pkg_exml_homepage = https://github.com/paulgray/exml
pkg_exml_fetch = git
pkg_exml_repo = https://github.com/paulgray/exml
pkg_exml_commit = master

PACKAGES += exometer
pkg_exometer_name = exometer
pkg_exometer_description = Basic measurement objects and probe behavior
pkg_exometer_homepage = https://github.com/Feuerlabs/exometer
pkg_exometer_fetch = git
pkg_exometer_repo = https://github.com/Feuerlabs/exometer
pkg_exometer_commit = 1.2

PACKAGES += exs1024
pkg_exs1024_name = exs1024
pkg_exs1024_description = Xorshift1024star pseudo random number generator for Erlang.
pkg_exs1024_homepage = https://github.com/jj1bdx/exs1024
pkg_exs1024_fetch = git
pkg_exs1024_repo = https://github.com/jj1bdx/exs1024
pkg_exs1024_commit = master

PACKAGES += exs64
pkg_exs64_name = exs64
pkg_exs64_description = Xorshift64star pseudo random number generator for Erlang.
pkg_exs64_homepage = https://github.com/jj1bdx/exs64
pkg_exs64_fetch = git
pkg_exs64_repo = https://github.com/jj1bdx/exs64
pkg_exs64_commit = master

PACKAGES += exsplus116
pkg_exsplus116_name = exsplus116
pkg_exsplus116_description = Xorshift116plus for Erlang
pkg_exsplus116_homepage = https://github.com/jj1bdx/exsplus116
pkg_exsplus116_fetch = git
pkg_exsplus116_repo = https://github.com/jj1bdx/exsplus116
pkg_exsplus116_commit = master

PACKAGES += exsplus128
pkg_exsplus128_name = exsplus128
pkg_exsplus128_description = Xorshift128plus pseudo random number generator for Erlang.
pkg_exsplus128_homepage = https://github.com/jj1bdx/exsplus128
pkg_exsplus128_fetch = git
pkg_exsplus128_repo = https://github.com/jj1bdx/exsplus128
pkg_exsplus128_commit = master

PACKAGES += ezmq
pkg_ezmq_name = ezmq
pkg_ezmq_description = zMQ implemented in Erlang
pkg_ezmq_homepage = https://github.com/RoadRunnr/ezmq
pkg_ezmq_fetch = git
pkg_ezmq_repo = https://github.com/RoadRunnr/ezmq
pkg_ezmq_commit = master

PACKAGES += ezmtp
pkg_ezmtp_name = ezmtp
pkg_ezmtp_description = ZMTP protocol in pure Erlang.
pkg_ezmtp_homepage = https://github.com/a13x/ezmtp
pkg_ezmtp_fetch = git
pkg_ezmtp_repo = https://github.com/a13x/ezmtp
pkg_ezmtp_commit = master

PACKAGES += fast_disk_log
pkg_fast_disk_log_name = fast_disk_log
pkg_fast_disk_log_description = Pool-based asynchronous Erlang disk logger
pkg_fast_disk_log_homepage = https://github.com/lpgauth/fast_disk_log
pkg_fast_disk_log_fetch = git
pkg_fast_disk_log_repo = https://github.com/lpgauth/fast_disk_log
pkg_fast_disk_log_commit = master

PACKAGES += feeder
pkg_feeder_name = feeder
pkg_feeder_description = Stream parse RSS and Atom formatted XML feeds.
pkg_feeder_homepage = https://github.com/michaelnisi/feeder
pkg_feeder_fetch = git
pkg_feeder_repo = https://github.com/michaelnisi/feeder
pkg_feeder_commit = v1.4.6

PACKAGES += fix
pkg_fix_name = fix
pkg_fix_description = http://fixprotocol.org/ implementation.
pkg_fix_homepage = https://github.com/maxlapshin/fix
pkg_fix_fetch = git
pkg_fix_repo = https://github.com/maxlapshin/fix
pkg_fix_commit = master

PACKAGES += flower
pkg_flower_name = flower
pkg_flower_description = FlowER - a Erlang OpenFlow development platform
pkg_flower_homepage = https://github.com/travelping/flower
pkg_flower_fetch = git
pkg_flower_repo = https://github.com/travelping/flower
pkg_flower_commit = master

PACKAGES += fn
pkg_fn_name = fn
pkg_fn_description = Function utilities for Erlang
pkg_fn_homepage = https://github.com/reiddraper/fn
pkg_fn_fetch = git
pkg_fn_repo = https://github.com/reiddraper/fn
pkg_fn_commit = master

PACKAGES += folsom_cowboy
pkg_folsom_cowboy_name = folsom_cowboy
pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper.
pkg_folsom_cowboy_homepage = https://github.com/boundary/folsom_cowboy
pkg_folsom_cowboy_fetch = git
pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy
pkg_folsom_cowboy_commit = master

PACKAGES += folsomite
pkg_folsomite_name = folsomite
pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics
pkg_folsomite_homepage = https://github.com/campanja/folsomite
pkg_folsomite_fetch = git
pkg_folsomite_repo = https://github.com/campanja/folsomite
pkg_folsomite_commit = master

PACKAGES += folsom
pkg_folsom_name = folsom
pkg_folsom_description = Expose Erlang Events and Metrics
pkg_folsom_homepage = https://github.com/boundary/folsom
pkg_folsom_fetch = git
pkg_folsom_repo = https://github.com/boundary/folsom
pkg_folsom_commit = master

PACKAGES += fs
pkg_fs_name = fs
pkg_fs_description = Erlang FileSystem Listener
pkg_fs_homepage = https://github.com/synrc/fs
pkg_fs_fetch = git
pkg_fs_repo = https://github.com/synrc/fs
pkg_fs_commit = master

PACKAGES += fuse
pkg_fuse_name = fuse
pkg_fuse_description = A Circuit Breaker for Erlang
pkg_fuse_homepage = https://github.com/jlouis/fuse
pkg_fuse_fetch = git
pkg_fuse_repo = https://github.com/jlouis/fuse
pkg_fuse_commit = master

PACKAGES += gcm
pkg_gcm_name = gcm
pkg_gcm_description = An Erlang application for Google Cloud Messaging
pkg_gcm_homepage = https://github.com/pdincau/gcm-erlang
pkg_gcm_fetch = git
pkg_gcm_repo = https://github.com/pdincau/gcm-erlang
pkg_gcm_commit = master

PACKAGES += gcprof
pkg_gcprof_name = gcprof
pkg_gcprof_description = Garbage Collection profiler for Erlang
pkg_gcprof_homepage = https://github.com/knutin/gcprof
pkg_gcprof_fetch = git
pkg_gcprof_repo = https://github.com/knutin/gcprof
pkg_gcprof_commit = master

PACKAGES += geas
pkg_geas_name = geas
pkg_geas_description = Guess Erlang Application Scattering
pkg_geas_homepage = https://github.com/crownedgrouse/geas
pkg_geas_fetch = git
pkg_geas_repo = https://github.com/crownedgrouse/geas
pkg_geas_commit = master

PACKAGES += geef
pkg_geef_name = geef
pkg_geef_description = Git NEEEEF (Erlang NIF)
pkg_geef_homepage = https://github.com/carlosmn/geef
pkg_geef_fetch = git
pkg_geef_repo = https://github.com/carlosmn/geef
pkg_geef_commit = master

PACKAGES += gen_cycle
pkg_gen_cycle_name = gen_cycle
pkg_gen_cycle_description = Simple, generic OTP behaviour for recurring tasks
pkg_gen_cycle_homepage = https://github.com/aerosol/gen_cycle
pkg_gen_cycle_fetch = git
pkg_gen_cycle_repo = https://github.com/aerosol/gen_cycle
pkg_gen_cycle_commit = develop

PACKAGES += gen_icmp
pkg_gen_icmp_name = gen_icmp
pkg_gen_icmp_description = Erlang interface to ICMP sockets
pkg_gen_icmp_homepage = https://github.com/msantos/gen_icmp
pkg_gen_icmp_fetch = git
pkg_gen_icmp_repo = https://github.com/msantos/gen_icmp
pkg_gen_icmp_commit = master

PACKAGES += gen_nb_server
pkg_gen_nb_server_name = gen_nb_server
pkg_gen_nb_server_description = OTP behavior for writing non-blocking servers
pkg_gen_nb_server_homepage = https://github.com/kevsmith/gen_nb_server
pkg_gen_nb_server_fetch = git
pkg_gen_nb_server_repo = https://github.com/kevsmith/gen_nb_server
pkg_gen_nb_server_commit = master

PACKAGES += gen_paxos
pkg_gen_paxos_name = gen_paxos
pkg_gen_paxos_description = An Erlang/OTP-style implementation of the PAXOS distributed consensus protocol
pkg_gen_paxos_homepage = https://github.com/gburd/gen_paxos
pkg_gen_paxos_fetch = git
pkg_gen_paxos_repo = https://github.com/gburd/gen_paxos
pkg_gen_paxos_commit = master

PACKAGES += gen_smtp
pkg_gen_smtp_name = gen_smtp
pkg_gen_smtp_description = A generic Erlang SMTP server and client that can be extended via callback modules
pkg_gen_smtp_homepage = https://github.com/Vagabond/gen_smtp
pkg_gen_smtp_fetch = git
pkg_gen_smtp_repo = https://github.com/Vagabond/gen_smtp
pkg_gen_smtp_commit = master

PACKAGES += gen_tracker
pkg_gen_tracker_name = gen_tracker
pkg_gen_tracker_description = supervisor with ets handling of children and their metadata
pkg_gen_tracker_homepage = https://github.com/erlyvideo/gen_tracker
pkg_gen_tracker_fetch = git
pkg_gen_tracker_repo = https://github.com/erlyvideo/gen_tracker
pkg_gen_tracker_commit = master

PACKAGES += gen_unix
pkg_gen_unix_name = gen_unix
pkg_gen_unix_description = Erlang Unix socket interface
pkg_gen_unix_homepage = https://github.com/msantos/gen_unix
pkg_gen_unix_fetch = git
pkg_gen_unix_repo = https://github.com/msantos/gen_unix
pkg_gen_unix_commit = master

PACKAGES += getopt
pkg_getopt_name = getopt
pkg_getopt_description = Module to parse command line arguments using the GNU getopt syntax
pkg_getopt_homepage = https://github.com/jcomellas/getopt
pkg_getopt_fetch = git
pkg_getopt_repo = https://github.com/jcomellas/getopt
pkg_getopt_commit = master

PACKAGES += gettext
pkg_gettext_name = gettext
pkg_gettext_description = Erlang internationalization library.
pkg_gettext_homepage = https://github.com/etnt/gettext
pkg_gettext_fetch = git
pkg_gettext_repo = https://github.com/etnt/gettext
pkg_gettext_commit = master

PACKAGES += giallo
pkg_giallo_name = giallo
pkg_giallo_description = Small and flexible web framework on top of Cowboy
pkg_giallo_homepage = https://github.com/kivra/giallo
pkg_giallo_fetch = git
pkg_giallo_repo = https://github.com/kivra/giallo
pkg_giallo_commit = master

PACKAGES += gin
pkg_gin_name = gin
pkg_gin_description = The guards  and  for Erlang parse_transform
pkg_gin_homepage = https://github.com/mad-cocktail/gin
pkg_gin_fetch = git
pkg_gin_repo = https://github.com/mad-cocktail/gin
pkg_gin_commit = master

PACKAGES += gitty
pkg_gitty_name = gitty
pkg_gitty_description = Git access in erlang
pkg_gitty_homepage = https://github.com/maxlapshin/gitty
pkg_gitty_fetch = git
pkg_gitty_repo = https://github.com/maxlapshin/gitty
pkg_gitty_commit = master

PACKAGES += gold_fever
pkg_gold_fever_name = gold_fever
pkg_gold_fever_description = A Treasure Hunt for Erlangers
pkg_gold_fever_homepage = https://github.com/inaka/gold_fever
pkg_gold_fever_fetch = git
pkg_gold_fever_repo = https://github.com/inaka/gold_fever
pkg_gold_fever_commit = master

PACKAGES += gossiperl
pkg_gossiperl_name = gossiperl
pkg_gossiperl_description = Gossip middleware in Erlang
pkg_gossiperl_homepage = http://gossiperl.com/
pkg_gossiperl_fetch = git
pkg_gossiperl_repo = https://github.com/gossiperl/gossiperl
pkg_gossiperl_commit = master

PACKAGES += gpb
pkg_gpb_name = gpb
pkg_gpb_description = A Google Protobuf implementation for Erlang
pkg_gpb_homepage = https://github.com/tomas-abrahamsson/gpb
pkg_gpb_fetch = git
pkg_gpb_repo = https://github.com/tomas-abrahamsson/gpb
pkg_gpb_commit = master

PACKAGES += gproc
pkg_gproc_name = gproc
pkg_gproc_description = Extended process registry for Erlang
pkg_gproc_homepage = https://github.com/uwiger/gproc
pkg_gproc_fetch = git
pkg_gproc_repo = https://github.com/uwiger/gproc
pkg_gproc_commit = master

PACKAGES += grapherl
pkg_grapherl_name = grapherl
pkg_grapherl_description = Create graphs of Erlang systems and programs
pkg_grapherl_homepage = https://github.com/eproxus/grapherl
pkg_grapherl_fetch = git
pkg_grapherl_repo = https://github.com/eproxus/grapherl
pkg_grapherl_commit = master

PACKAGES += gun
pkg_gun_name = gun
pkg_gun_description = Asynchronous SPDY, HTTP and Websocket client written in Erlang.
pkg_gun_homepage = http//ninenines.eu
pkg_gun_fetch = git
pkg_gun_repo = https://github.com/ninenines/gun
pkg_gun_commit = master

PACKAGES += gut
pkg_gut_name = gut
pkg_gut_description = gut is a template printing, aka scaffolding, tool for Erlang. Like rails generate or yeoman
pkg_gut_homepage = https://github.com/unbalancedparentheses/gut
pkg_gut_fetch = git
pkg_gut_repo = https://github.com/unbalancedparentheses/gut
pkg_gut_commit = master

PACKAGES += hackney
pkg_hackney_name = hackney
pkg_hackney_description = simple HTTP client in Erlang
pkg_hackney_homepage = https://github.com/benoitc/hackney
pkg_hackney_fetch = git
pkg_hackney_repo = https://github.com/benoitc/hackney
pkg_hackney_commit = master

PACKAGES += hamcrest
pkg_hamcrest_name = hamcrest
pkg_hamcrest_description = Erlang port of Hamcrest
pkg_hamcrest_homepage = https://github.com/hyperthunk/hamcrest-erlang
pkg_hamcrest_fetch = git
pkg_hamcrest_repo = https://github.com/hyperthunk/hamcrest-erlang
pkg_hamcrest_commit = master

PACKAGES += hanoidb
pkg_hanoidb_name = hanoidb
pkg_hanoidb_description = Erlang LSM BTree Storage
pkg_hanoidb_homepage = https://github.com/krestenkrab/hanoidb
pkg_hanoidb_fetch = git
pkg_hanoidb_repo = https://github.com/krestenkrab/hanoidb
pkg_hanoidb_commit = master

PACKAGES += hottub
pkg_hottub_name = hottub
pkg_hottub_description = Permanent Erlang Worker Pool
pkg_hottub_homepage = https://github.com/bfrog/hottub
pkg_hottub_fetch = git
pkg_hottub_repo = https://github.com/bfrog/hottub
pkg_hottub_commit = master

PACKAGES += hpack
pkg_hpack_name = hpack
pkg_hpack_description = HPACK Implementation for Erlang
pkg_hpack_homepage = https://github.com/joedevivo/hpack
pkg_hpack_fetch = git
pkg_hpack_repo = https://github.com/joedevivo/hpack
pkg_hpack_commit = master

PACKAGES += hyper
pkg_hyper_name = hyper
pkg_hyper_description = Erlang implementation of HyperLogLog
pkg_hyper_homepage = https://github.com/GameAnalytics/hyper
pkg_hyper_fetch = git
pkg_hyper_repo = https://github.com/GameAnalytics/hyper
pkg_hyper_commit = master

PACKAGES += ibrowse
pkg_ibrowse_name = ibrowse
pkg_ibrowse_description = Erlang HTTP client
pkg_ibrowse_homepage = https://github.com/cmullaparthi/ibrowse
pkg_ibrowse_fetch = git
pkg_ibrowse_repo = https://github.com/cmullaparthi/ibrowse
pkg_ibrowse_commit = v4.1.1

PACKAGES += ierlang
pkg_ierlang_name = ierlang
pkg_ierlang_description = An Erlang language kernel for IPython.
pkg_ierlang_homepage = https://github.com/robbielynch/ierlang
pkg_ierlang_fetch = git
pkg_ierlang_repo = https://github.com/robbielynch/ierlang
pkg_ierlang_commit = master

PACKAGES += iota
pkg_iota_name = iota
pkg_iota_description = iota (Inter-dependency Objective Testing Apparatus) - a tool to enforce clean separation of responsibilities in Erlang code
pkg_iota_homepage = https://github.com/jpgneves/iota
pkg_iota_fetch = git
pkg_iota_repo = https://github.com/jpgneves/iota
pkg_iota_commit = master

PACKAGES += ircd
pkg_ircd_name = ircd
pkg_ircd_description = A pluggable IRC daemon application/library for Erlang.
pkg_ircd_homepage = https://github.com/tonyg/erlang-ircd
pkg_ircd_fetch = git
pkg_ircd_repo = https://github.com/tonyg/erlang-ircd
pkg_ircd_commit = master

PACKAGES += irc_lib
pkg_irc_lib_name = irc_lib
pkg_irc_lib_description = Erlang irc client library
pkg_irc_lib_homepage = https://github.com/OtpChatBot/irc_lib
pkg_irc_lib_fetch = git
pkg_irc_lib_repo = https://github.com/OtpChatBot/irc_lib
pkg_irc_lib_commit = master

PACKAGES += iris
pkg_iris_name = iris
pkg_iris_description = Iris Erlang binding
pkg_iris_homepage = https://github.com/project-iris/iris-erl
pkg_iris_fetch = git
pkg_iris_repo = https://github.com/project-iris/iris-erl
pkg_iris_commit = master

PACKAGES += iso8601
pkg_iso8601_name = iso8601
pkg_iso8601_description = Erlang ISO 8601 date formatter/parser
pkg_iso8601_homepage = https://github.com/seansawyer/erlang_iso8601
pkg_iso8601_fetch = git
pkg_iso8601_repo = https://github.com/seansawyer/erlang_iso8601
pkg_iso8601_commit = master

PACKAGES += itweet
pkg_itweet_name = itweet
pkg_itweet_description = Twitter Stream API on ibrowse
pkg_itweet_homepage = http://inaka.github.com/itweet/
pkg_itweet_fetch = git
pkg_itweet_repo = https://github.com/inaka/itweet
pkg_itweet_commit = v2.0

PACKAGES += jerg
pkg_jerg_name = jerg
pkg_jerg_description = JSON Schema to Erlang Records Generator
pkg_jerg_homepage = https://github.com/ddossot/jerg
pkg_jerg_fetch = git
pkg_jerg_repo = https://github.com/ddossot/jerg
pkg_jerg_commit = master

PACKAGES += jesse
pkg_jesse_name = jesse
pkg_jesse_description = jesse (JSon Schema Erlang) is an implementation of a json schema validator for Erlang.
pkg_jesse_homepage = https://github.com/klarna/jesse
pkg_jesse_fetch = git
pkg_jesse_repo = https://github.com/klarna/jesse
pkg_jesse_commit = master

PACKAGES += jiffy
pkg_jiffy_name = jiffy
pkg_jiffy_description = JSON NIFs for Erlang.
pkg_jiffy_homepage = https://github.com/davisp/jiffy
pkg_jiffy_fetch = git
pkg_jiffy_repo = https://github.com/davisp/jiffy
pkg_jiffy_commit = master

PACKAGES += jiffy_v
pkg_jiffy_v_name = jiffy_v
pkg_jiffy_v_description = JSON validation utility
pkg_jiffy_v_homepage = https://github.com/shizzard/jiffy-v
pkg_jiffy_v_fetch = git
pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
pkg_jiffy_v_commit = 0.3.3

PACKAGES += jobs
pkg_jobs_name = jobs
pkg_jobs_description = a Job scheduler for load regulation
pkg_jobs_homepage = https://github.com/esl/jobs
pkg_jobs_fetch = git
pkg_jobs_repo = https://github.com/esl/jobs
pkg_jobs_commit = 0.3

PACKAGES += joxa
pkg_joxa_name = joxa
pkg_joxa_description = A Modern Lisp for the Erlang VM
pkg_joxa_homepage = https://github.com/joxa/joxa
pkg_joxa_fetch = git
pkg_joxa_repo = https://github.com/joxa/joxa
pkg_joxa_commit = master

PACKAGES += jsonerl
pkg_jsonerl_name = jsonerl
pkg_jsonerl_description = yet another but slightly different erlang <-> json encoder/decoder
pkg_jsonerl_homepage = https://github.com/lambder/jsonerl
pkg_jsonerl_fetch = git
pkg_jsonerl_repo = https://github.com/lambder/jsonerl
pkg_jsonerl_commit = master

PACKAGES += json
pkg_json_name = json
pkg_json_description = a high level json library for erlang (17.0+)
pkg_json_homepage = https://github.com/talentdeficit/json
pkg_json_fetch = git
pkg_json_repo = https://github.com/talentdeficit/json
pkg_json_commit = master

PACKAGES += jsonpath
pkg_jsonpath_name = jsonpath
pkg_jsonpath_description = Fast Erlang JSON data retrieval and updates via javascript-like notation
pkg_jsonpath_homepage = https://github.com/GeneStevens/jsonpath
pkg_jsonpath_fetch = git
pkg_jsonpath_repo = https://github.com/GeneStevens/jsonpath
pkg_jsonpath_commit = master

PACKAGES += json_rec
pkg_json_rec_name = json_rec
pkg_json_rec_description = JSON to erlang record
pkg_json_rec_homepage = https://github.com/justinkirby/json_rec
pkg_json_rec_fetch = git
pkg_json_rec_repo = https://github.com/justinkirby/json_rec
pkg_json_rec_commit = master

PACKAGES += jsonx
pkg_jsonx_name = jsonx
pkg_jsonx_description = JSONX is an Erlang library for efficient decode and encode JSON, written in C.
pkg_jsonx_homepage = https://github.com/iskra/jsonx
pkg_jsonx_fetch = git
pkg_jsonx_repo = https://github.com/iskra/jsonx
pkg_jsonx_commit = master

PACKAGES += jsx
pkg_jsx_name = jsx
pkg_jsx_description = An Erlang application for consuming, producing and manipulating JSON.
pkg_jsx_homepage = https://github.com/talentdeficit/jsx
pkg_jsx_fetch = git
pkg_jsx_repo = https://github.com/talentdeficit/jsx
pkg_jsx_commit = master

PACKAGES += kafka
pkg_kafka_name = kafka
pkg_kafka_description = Kafka consumer and producer in Erlang
pkg_kafka_homepage = https://github.com/wooga/kafka-erlang
pkg_kafka_fetch = git
pkg_kafka_repo = https://github.com/wooga/kafka-erlang
pkg_kafka_commit = master

PACKAGES += kai
pkg_kai_name = kai
pkg_kai_description = DHT storage by Takeshi Inoue
pkg_kai_homepage = https://github.com/synrc/kai
pkg_kai_fetch = git
pkg_kai_repo = https://github.com/synrc/kai
pkg_kai_commit = master

PACKAGES += katja
pkg_katja_name = katja
pkg_katja_description = A simple Riemann client written in Erlang.
pkg_katja_homepage = https://github.com/nifoc/katja
pkg_katja_fetch = git
pkg_katja_repo = https://github.com/nifoc/katja
pkg_katja_commit = master

PACKAGES += kdht
pkg_kdht_name = kdht
pkg_kdht_description = kdht is an erlang DHT implementation
pkg_kdht_homepage = https://github.com/kevinlynx/kdht
pkg_kdht_fetch = git
pkg_kdht_repo = https://github.com/kevinlynx/kdht
pkg_kdht_commit = master

PACKAGES += key2value
pkg_key2value_name = key2value
pkg_key2value_description = Erlang 2-way map
pkg_key2value_homepage = https://github.com/okeuday/key2value
pkg_key2value_fetch = git
pkg_key2value_repo = https://github.com/okeuday/key2value
pkg_key2value_commit = master

PACKAGES += keys1value
pkg_keys1value_name = keys1value
pkg_keys1value_description = Erlang set associative map for key lists
pkg_keys1value_homepage = https://github.com/okeuday/keys1value
pkg_keys1value_fetch = git
pkg_keys1value_repo = https://github.com/okeuday/keys1value
pkg_keys1value_commit = master

PACKAGES += kinetic
pkg_kinetic_name = kinetic
pkg_kinetic_description = Erlang Kinesis Client
pkg_kinetic_homepage = https://github.com/AdRoll/kinetic
pkg_kinetic_fetch = git
pkg_kinetic_repo = https://github.com/AdRoll/kinetic
pkg_kinetic_commit = master

PACKAGES += kjell
pkg_kjell_name = kjell
pkg_kjell_description = Erlang Shell
pkg_kjell_homepage = https://github.com/karlll/kjell
pkg_kjell_fetch = git
pkg_kjell_repo = https://github.com/karlll/kjell
pkg_kjell_commit = master

PACKAGES += kraken
pkg_kraken_name = kraken
pkg_kraken_description = Distributed Pubsub Server for Realtime Apps
pkg_kraken_homepage = https://github.com/Asana/kraken
pkg_kraken_fetch = git
pkg_kraken_repo = https://github.com/Asana/kraken
pkg_kraken_commit = master

PACKAGES += kucumberl
pkg_kucumberl_name = kucumberl
pkg_kucumberl_description = A pure-erlang, open-source, implementation of Cucumber
pkg_kucumberl_homepage = https://github.com/openshine/kucumberl
pkg_kucumberl_fetch = git
pkg_kucumberl_repo = https://github.com/openshine/kucumberl
pkg_kucumberl_commit = master

PACKAGES += kvc
pkg_kvc_name = kvc
pkg_kvc_description = KVC - Key Value Coding for Erlang data structures
pkg_kvc_homepage = https://github.com/etrepum/kvc
pkg_kvc_fetch = git
pkg_kvc_repo = https://github.com/etrepum/kvc
pkg_kvc_commit = master

PACKAGES += kvlists
pkg_kvlists_name = kvlists
pkg_kvlists_description = Lists of key-value pairs (decoded JSON) in Erlang
pkg_kvlists_homepage = https://github.com/jcomellas/kvlists
pkg_kvlists_fetch = git
pkg_kvlists_repo = https://github.com/jcomellas/kvlists
pkg_kvlists_commit = master

PACKAGES += kvs
pkg_kvs_name = kvs
pkg_kvs_description = Container and Iterator
pkg_kvs_homepage = https://github.com/synrc/kvs
pkg_kvs_fetch = git
pkg_kvs_repo = https://github.com/synrc/kvs
pkg_kvs_commit = master

PACKAGES += lager_amqp_backend
pkg_lager_amqp_backend_name = lager_amqp_backend
pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend
pkg_lager_amqp_backend_homepage = https://github.com/jbrisbin/lager_amqp_backend
pkg_lager_amqp_backend_fetch = git
pkg_lager_amqp_backend_repo = https://github.com/jbrisbin/lager_amqp_backend
pkg_lager_amqp_backend_commit = master

PACKAGES += lager
pkg_lager_name = lager
pkg_lager_description = A logging framework for Erlang/OTP.
pkg_lager_homepage = https://github.com/basho/lager
pkg_lager_fetch = git
pkg_lager_repo = https://github.com/basho/lager
pkg_lager_commit = master

PACKAGES += lager_syslog
pkg_lager_syslog_name = lager_syslog
pkg_lager_syslog_description = Syslog backend for lager
pkg_lager_syslog_homepage = https://github.com/basho/lager_syslog
pkg_lager_syslog_fetch = git
pkg_lager_syslog_repo = https://github.com/basho/lager_syslog
pkg_lager_syslog_commit = master

PACKAGES += lambdapad
pkg_lambdapad_name = lambdapad
pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang.
pkg_lambdapad_homepage = https://github.com/gar1t/lambdapad
pkg_lambdapad_fetch = git
pkg_lambdapad_repo = https://github.com/gar1t/lambdapad
pkg_lambdapad_commit = master

PACKAGES += lasp
pkg_lasp_name = lasp
pkg_lasp_description = A Language for Distributed, Eventually Consistent Computations
pkg_lasp_homepage = http://lasp-lang.org/
pkg_lasp_fetch = git
pkg_lasp_repo = https://github.com/lasp-lang/lasp
pkg_lasp_commit = master

PACKAGES += lasse
pkg_lasse_name = lasse
pkg_lasse_description = SSE handler for Cowboy
pkg_lasse_homepage = https://github.com/inaka/lasse
pkg_lasse_fetch = git
pkg_lasse_repo = https://github.com/inaka/lasse
pkg_lasse_commit = 0.1.0

PACKAGES += ldap
pkg_ldap_name = ldap
pkg_ldap_description = LDAP server written in Erlang
pkg_ldap_homepage = https://github.com/spawnproc/ldap
pkg_ldap_fetch = git
pkg_ldap_repo = https://github.com/spawnproc/ldap
pkg_ldap_commit = master

PACKAGES += lethink
pkg_lethink_name = lethink
pkg_lethink_description = erlang driver for rethinkdb
pkg_lethink_homepage = https://github.com/taybin/lethink
pkg_lethink_fetch = git
pkg_lethink_repo = https://github.com/taybin/lethink
pkg_lethink_commit = master

PACKAGES += lfe
pkg_lfe_name = lfe
pkg_lfe_description = Lisp Flavoured Erlang (LFE)
pkg_lfe_homepage = https://github.com/rvirding/lfe
pkg_lfe_fetch = git
pkg_lfe_repo = https://github.com/rvirding/lfe
pkg_lfe_commit = master

PACKAGES += ling
pkg_ling_name = ling
pkg_ling_description = Erlang on Xen
pkg_ling_homepage = https://github.com/cloudozer/ling
pkg_ling_fetch = git
pkg_ling_repo = https://github.com/cloudozer/ling
pkg_ling_commit = master

PACKAGES += live
pkg_live_name = live
pkg_live_description = Automated module and configuration reloader.
pkg_live_homepage = http://ninenines.eu
pkg_live_fetch = git
pkg_live_repo = https://github.com/ninenines/live
pkg_live_commit = master

PACKAGES += lmq
pkg_lmq_name = lmq
pkg_lmq_description = Lightweight Message Queue
pkg_lmq_homepage = https://github.com/iij/lmq
pkg_lmq_fetch = git
pkg_lmq_repo = https://github.com/iij/lmq
pkg_lmq_commit = master

PACKAGES += locker
pkg_locker_name = locker
pkg_locker_description = Atomic distributed 'check and set' for short-lived keys
pkg_locker_homepage = https://github.com/wooga/locker
pkg_locker_fetch = git
pkg_locker_repo = https://github.com/wooga/locker
pkg_locker_commit = master

PACKAGES += locks
pkg_locks_name = locks
pkg_locks_description = A scalable, deadlock-resolving resource locker
pkg_locks_homepage = https://github.com/uwiger/locks
pkg_locks_fetch = git
pkg_locks_repo = https://github.com/uwiger/locks
pkg_locks_commit = master

PACKAGES += log4erl
pkg_log4erl_name = log4erl
pkg_log4erl_description = A logger for erlang in the spirit of Log4J.
pkg_log4erl_homepage = https://github.com/ahmednawras/log4erl
pkg_log4erl_fetch = git
pkg_log4erl_repo = https://github.com/ahmednawras/log4erl
pkg_log4erl_commit = master

PACKAGES += lol
pkg_lol_name = lol
pkg_lol_description = Lisp on erLang, and programming is fun again
pkg_lol_homepage = https://github.com/b0oh/lol
pkg_lol_fetch = git
pkg_lol_repo = https://github.com/b0oh/lol
pkg_lol_commit = master

PACKAGES += lucid
pkg_lucid_name = lucid
pkg_lucid_description = HTTP/2 server written in Erlang
pkg_lucid_homepage = https://github.com/tatsuhiro-t/lucid
pkg_lucid_fetch = git
pkg_lucid_repo = https://github.com/tatsuhiro-t/lucid
pkg_lucid_commit = master

PACKAGES += luerl
pkg_luerl_name = luerl
pkg_luerl_description = Lua in Erlang
pkg_luerl_homepage = https://github.com/rvirding/luerl
pkg_luerl_fetch = git
pkg_luerl_repo = https://github.com/rvirding/luerl
pkg_luerl_commit = develop

PACKAGES += luwak
pkg_luwak_name = luwak
pkg_luwak_description = Large-object storage interface for Riak
pkg_luwak_homepage = https://github.com/basho/luwak
pkg_luwak_fetch = git
pkg_luwak_repo = https://github.com/basho/luwak
pkg_luwak_commit = master

PACKAGES += lux
pkg_lux_name = lux
pkg_lux_description = Lux (LUcid eXpect scripting) simplifies test automation and provides an Expect-style execution of commands
pkg_lux_homepage = https://github.com/hawk/lux
pkg_lux_fetch = git
pkg_lux_repo = https://github.com/hawk/lux
pkg_lux_commit = master

PACKAGES += machi
pkg_machi_name = machi
pkg_machi_description = Machi file store
pkg_machi_homepage = https://github.com/basho/machi
pkg_machi_fetch = git
pkg_machi_repo = https://github.com/basho/machi
pkg_machi_commit = master

PACKAGES += mad
pkg_mad_name = mad
pkg_mad_description = Small and Fast Rebar Replacement
pkg_mad_homepage = https://github.com/synrc/mad
pkg_mad_fetch = git
pkg_mad_repo = https://github.com/synrc/mad
pkg_mad_commit = master

PACKAGES += marina
pkg_marina_name = marina
pkg_marina_description = Non-blocking Erlang Cassandra CQL3 client
pkg_marina_homepage = https://github.com/lpgauth/marina
pkg_marina_fetch = git
pkg_marina_repo = https://github.com/lpgauth/marina
pkg_marina_commit = master

PACKAGES += mavg
pkg_mavg_name = mavg
pkg_mavg_description = Erlang :: Exponential moving average library
pkg_mavg_homepage = https://github.com/EchoTeam/mavg
pkg_mavg_fetch = git
pkg_mavg_repo = https://github.com/EchoTeam/mavg
pkg_mavg_commit = master

PACKAGES += mcd
pkg_mcd_name = mcd
pkg_mcd_description = Fast memcached protocol client in pure Erlang
pkg_mcd_homepage = https://github.com/EchoTeam/mcd
pkg_mcd_fetch = git
pkg_mcd_repo = https://github.com/EchoTeam/mcd
pkg_mcd_commit = master

PACKAGES += mcerlang
pkg_mcerlang_name = mcerlang
pkg_mcerlang_description = The McErlang model checker for Erlang
pkg_mcerlang_homepage = https://github.com/fredlund/McErlang
pkg_mcerlang_fetch = git
pkg_mcerlang_repo = https://github.com/fredlund/McErlang
pkg_mcerlang_commit = master

PACKAGES += mc_erl
pkg_mc_erl_name = mc_erl
pkg_mc_erl_description = mc-erl is a server for Minecraft 1.4.7 written in Erlang.
pkg_mc_erl_homepage = https://github.com/clonejo/mc-erl
pkg_mc_erl_fetch = git
pkg_mc_erl_repo = https://github.com/clonejo/mc-erl
pkg_mc_erl_commit = master

PACKAGES += meck
pkg_meck_name = meck
pkg_meck_description = A mocking library for Erlang
pkg_meck_homepage = https://github.com/eproxus/meck
pkg_meck_fetch = git
pkg_meck_repo = https://github.com/eproxus/meck
pkg_meck_commit = master

PACKAGES += mekao
pkg_mekao_name = mekao
pkg_mekao_description = SQL constructor
pkg_mekao_homepage = https://github.com/ddosia/mekao
pkg_mekao_fetch = git
pkg_mekao_repo = https://github.com/ddosia/mekao
pkg_mekao_commit = master

PACKAGES += memo
pkg_memo_name = memo
pkg_memo_description = Erlang memoization server
pkg_memo_homepage = https://github.com/tuncer/memo
pkg_memo_fetch = git
pkg_memo_repo = https://github.com/tuncer/memo
pkg_memo_commit = master

PACKAGES += merge_index
pkg_merge_index_name = merge_index
pkg_merge_index_description = MergeIndex is an Erlang library for storing ordered sets on disk. It is very similar to an SSTable (in Google's Bigtable) or an HFile (in Hadoop).
pkg_merge_index_homepage = https://github.com/basho/merge_index
pkg_merge_index_fetch = git
pkg_merge_index_repo = https://github.com/basho/merge_index
pkg_merge_index_commit = master

PACKAGES += merl
pkg_merl_name = merl
pkg_merl_description = Metaprogramming in Erlang
pkg_merl_homepage = https://github.com/richcarl/merl
pkg_merl_fetch = git
pkg_merl_repo = https://github.com/richcarl/merl
pkg_merl_commit = master

PACKAGES += mimetypes
pkg_mimetypes_name = mimetypes
pkg_mimetypes_description = Erlang MIME types library
pkg_mimetypes_homepage = https://github.com/spawngrid/mimetypes
pkg_mimetypes_fetch = git
pkg_mimetypes_repo = https://github.com/spawngrid/mimetypes
pkg_mimetypes_commit = master

PACKAGES += mixer
pkg_mixer_name = mixer
pkg_mixer_description = Mix in functions from other modules
pkg_mixer_homepage = https://github.com/chef/mixer
pkg_mixer_fetch = git
pkg_mixer_repo = https://github.com/chef/mixer
pkg_mixer_commit = master

PACKAGES += mochiweb
pkg_mochiweb_name = mochiweb
pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers.
pkg_mochiweb_homepage = https://github.com/mochi/mochiweb
pkg_mochiweb_fetch = git
pkg_mochiweb_repo = https://github.com/mochi/mochiweb
pkg_mochiweb_commit = master

PACKAGES += mochiweb_xpath
pkg_mochiweb_xpath_name = mochiweb_xpath
pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser
pkg_mochiweb_xpath_homepage = https://github.com/retnuh/mochiweb_xpath
pkg_mochiweb_xpath_fetch = git
pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath
pkg_mochiweb_xpath_commit = master

PACKAGES += mockgyver
pkg_mockgyver_name = mockgyver
pkg_mockgyver_description = A mocking library for Erlang
pkg_mockgyver_homepage = https://github.com/klajo/mockgyver
pkg_mockgyver_fetch = git
pkg_mockgyver_repo = https://github.com/klajo/mockgyver
pkg_mockgyver_commit = master

PACKAGES += modlib
pkg_modlib_name = modlib
pkg_modlib_description = Web framework based on Erlang's inets httpd
pkg_modlib_homepage = https://github.com/gar1t/modlib
pkg_modlib_fetch = git
pkg_modlib_repo = https://github.com/gar1t/modlib
pkg_modlib_commit = master

PACKAGES += mongodb
pkg_mongodb_name = mongodb
pkg_mongodb_description = MongoDB driver for Erlang
pkg_mongodb_homepage = https://github.com/comtihon/mongodb-erlang
pkg_mongodb_fetch = git
pkg_mongodb_repo = https://github.com/comtihon/mongodb-erlang
pkg_mongodb_commit = master

PACKAGES += mongooseim
pkg_mongooseim_name = mongooseim
pkg_mongooseim_description = Jabber / XMPP server with focus on performance and scalability, by Erlang Solutions
pkg_mongooseim_homepage = https://www.erlang-solutions.com/products/mongooseim-massively-scalable-ejabberd-platform
pkg_mongooseim_fetch = git
pkg_mongooseim_repo = https://github.com/esl/MongooseIM
pkg_mongooseim_commit = master

PACKAGES += moyo
pkg_moyo_name = moyo
pkg_moyo_description = Erlang utility functions library
pkg_moyo_homepage = https://github.com/dwango/moyo
pkg_moyo_fetch = git
pkg_moyo_repo = https://github.com/dwango/moyo
pkg_moyo_commit = master

PACKAGES += msgpack
pkg_msgpack_name = msgpack
pkg_msgpack_description = MessagePack (de)serializer implementation for Erlang
pkg_msgpack_homepage = https://github.com/msgpack/msgpack-erlang
pkg_msgpack_fetch = git
pkg_msgpack_repo = https://github.com/msgpack/msgpack-erlang
pkg_msgpack_commit = master

PACKAGES += mu2
pkg_mu2_name = mu2
pkg_mu2_description = Erlang mutation testing tool
pkg_mu2_homepage = https://github.com/ramsay-t/mu2
pkg_mu2_fetch = git
pkg_mu2_repo = https://github.com/ramsay-t/mu2
pkg_mu2_commit = master

PACKAGES += mustache
pkg_mustache_name = mustache
pkg_mustache_description = Mustache template engine for Erlang.
pkg_mustache_homepage = https://github.com/mojombo/mustache.erl
pkg_mustache_fetch = git
pkg_mustache_repo = https://github.com/mojombo/mustache.erl
pkg_mustache_commit = master

PACKAGES += myproto
pkg_myproto_name = myproto
pkg_myproto_description = MySQL Server Protocol in Erlang
pkg_myproto_homepage = https://github.com/altenwald/myproto
pkg_myproto_fetch = git
pkg_myproto_repo = https://github.com/altenwald/myproto
pkg_myproto_commit = master

PACKAGES += mysql
pkg_mysql_name = mysql
pkg_mysql_description = Erlang MySQL Driver (from code.google.com)
pkg_mysql_homepage = https://github.com/dizzyd/erlang-mysql-driver
pkg_mysql_fetch = git
pkg_mysql_repo = https://github.com/dizzyd/erlang-mysql-driver
pkg_mysql_commit = master

PACKAGES += n2o
pkg_n2o_name = n2o
pkg_n2o_description = WebSocket Application Server
pkg_n2o_homepage = https://github.com/5HT/n2o
pkg_n2o_fetch = git
pkg_n2o_repo = https://github.com/5HT/n2o
pkg_n2o_commit = master

PACKAGES += nat_upnp
pkg_nat_upnp_name = nat_upnp
pkg_nat_upnp_description = Erlang library to map your internal port to an external using UNP IGD
pkg_nat_upnp_homepage = https://github.com/benoitc/nat_upnp
pkg_nat_upnp_fetch = git
pkg_nat_upnp_repo = https://github.com/benoitc/nat_upnp
pkg_nat_upnp_commit = master

PACKAGES += neo4j
pkg_neo4j_name = neo4j
pkg_neo4j_description = Erlang client library for Neo4J.
pkg_neo4j_homepage = https://github.com/dmitriid/neo4j-erlang
pkg_neo4j_fetch = git
pkg_neo4j_repo = https://github.com/dmitriid/neo4j-erlang
pkg_neo4j_commit = master

PACKAGES += neotoma
pkg_neotoma_name = neotoma
pkg_neotoma_description = Erlang library and packrat parser-generator for parsing expression grammars.
pkg_neotoma_homepage = https://github.com/seancribbs/neotoma
pkg_neotoma_fetch = git
pkg_neotoma_repo = https://github.com/seancribbs/neotoma
pkg_neotoma_commit = master

PACKAGES += newrelic
pkg_newrelic_name = newrelic
pkg_newrelic_description = Erlang library for sending metrics to New Relic
pkg_newrelic_homepage = https://github.com/wooga/newrelic-erlang
pkg_newrelic_fetch = git
pkg_newrelic_repo = https://github.com/wooga/newrelic-erlang
pkg_newrelic_commit = master

PACKAGES += nifty
pkg_nifty_name = nifty
pkg_nifty_description = Erlang NIF wrapper generator
pkg_nifty_homepage = https://github.com/parapluu/nifty
pkg_nifty_fetch = git
pkg_nifty_repo = https://github.com/parapluu/nifty
pkg_nifty_commit = master

PACKAGES += nitrogen_core
pkg_nitrogen_core_name = nitrogen_core
pkg_nitrogen_core_description = The core Nitrogen library.
pkg_nitrogen_core_homepage = http://nitrogenproject.com/
pkg_nitrogen_core_fetch = git
pkg_nitrogen_core_repo = https://github.com/nitrogen/nitrogen_core
pkg_nitrogen_core_commit = master

PACKAGES += nkbase
pkg_nkbase_name = nkbase
pkg_nkbase_description = NkBASE distributed database
pkg_nkbase_homepage = https://github.com/Nekso/nkbase
pkg_nkbase_fetch = git
pkg_nkbase_repo = https://github.com/Nekso/nkbase
pkg_nkbase_commit = develop

PACKAGES += nkdocker
pkg_nkdocker_name = nkdocker
pkg_nkdocker_description = Erlang Docker client
pkg_nkdocker_homepage = https://github.com/Nekso/nkdocker
pkg_nkdocker_fetch = git
pkg_nkdocker_repo = https://github.com/Nekso/nkdocker
pkg_nkdocker_commit = master

PACKAGES += nkpacket
pkg_nkpacket_name = nkpacket
pkg_nkpacket_description = Generic Erlang transport layer
pkg_nkpacket_homepage = https://github.com/Nekso/nkpacket
pkg_nkpacket_fetch = git
pkg_nkpacket_repo = https://github.com/Nekso/nkpacket
pkg_nkpacket_commit = master

PACKAGES += nodefinder
pkg_nodefinder_name = nodefinder
pkg_nodefinder_description = automatic node discovery via UDP multicast
pkg_nodefinder_homepage = https://github.com/erlanger/nodefinder
pkg_nodefinder_fetch = git
pkg_nodefinder_repo = https://github.com/okeuday/nodefinder
pkg_nodefinder_commit = master

PACKAGES += nprocreg
pkg_nprocreg_name = nprocreg
pkg_nprocreg_description = Minimal Distributed Erlang Process Registry
pkg_nprocreg_homepage = http://nitrogenproject.com/
pkg_nprocreg_fetch = git
pkg_nprocreg_repo = https://github.com/nitrogen/nprocreg
pkg_nprocreg_commit = master

PACKAGES += oauth2c
pkg_oauth2c_name = oauth2c
pkg_oauth2c_description = Erlang OAuth2 Client
pkg_oauth2c_homepage = https://github.com/kivra/oauth2_client
pkg_oauth2c_fetch = git
pkg_oauth2c_repo = https://github.com/kivra/oauth2_client
pkg_oauth2c_commit = master

PACKAGES += oauth2
pkg_oauth2_name = oauth2
pkg_oauth2_description = Erlang Oauth2 implementation
pkg_oauth2_homepage = https://github.com/kivra/oauth2
pkg_oauth2_fetch = git
pkg_oauth2_repo = https://github.com/kivra/oauth2
pkg_oauth2_commit = master

PACKAGES += oauth
pkg_oauth_name = oauth
pkg_oauth_description = An Erlang OAuth 1.0 implementation
pkg_oauth_homepage = https://github.com/tim/erlang-oauth
pkg_oauth_fetch = git
pkg_oauth_repo = https://github.com/tim/erlang-oauth
pkg_oauth_commit = master

PACKAGES += of_protocol
pkg_of_protocol_name = of_protocol
pkg_of_protocol_description = OpenFlow Protocol Library for Erlang
pkg_of_protocol_homepage = https://github.com/FlowForwarding/of_protocol
pkg_of_protocol_fetch = git
pkg_of_protocol_repo = https://github.com/FlowForwarding/of_protocol
pkg_of_protocol_commit = master

PACKAGES += openflow
pkg_openflow_name = openflow
pkg_openflow_description = An OpenFlow controller written in pure erlang
pkg_openflow_homepage = https://github.com/renatoaguiar/erlang-openflow
pkg_openflow_fetch = git
pkg_openflow_repo = https://github.com/renatoaguiar/erlang-openflow
pkg_openflow_commit = master

PACKAGES += openid
pkg_openid_name = openid
pkg_openid_description = Erlang OpenID
pkg_openid_homepage = https://github.com/brendonh/erl_openid
pkg_openid_fetch = git
pkg_openid_repo = https://github.com/brendonh/erl_openid
pkg_openid_commit = master

PACKAGES += openpoker
pkg_openpoker_name = openpoker
pkg_openpoker_description = Genesis Texas hold'em Game Server
pkg_openpoker_homepage = https://github.com/hpyhacking/openpoker
pkg_openpoker_fetch = git
pkg_openpoker_repo = https://github.com/hpyhacking/openpoker
pkg_openpoker_commit = master

PACKAGES += pal
pkg_pal_name = pal
pkg_pal_description = Pragmatic Authentication Library
pkg_pal_homepage = https://github.com/manifest/pal
pkg_pal_fetch = git
pkg_pal_repo = https://github.com/manifest/pal
pkg_pal_commit = master

PACKAGES += parse_trans
pkg_parse_trans_name = parse_trans
pkg_parse_trans_description = Parse transform utilities for Erlang
pkg_parse_trans_homepage = https://github.com/uwiger/parse_trans
pkg_parse_trans_fetch = git
pkg_parse_trans_repo = https://github.com/uwiger/parse_trans
pkg_parse_trans_commit = master

PACKAGES += parsexml
pkg_parsexml_name = parsexml
pkg_parsexml_description = Simple DOM XML parser with convenient and very simple API
pkg_parsexml_homepage = https://github.com/maxlapshin/parsexml
pkg_parsexml_fetch = git
pkg_parsexml_repo = https://github.com/maxlapshin/parsexml
pkg_parsexml_commit = master

PACKAGES += pegjs
pkg_pegjs_name = pegjs
pkg_pegjs_description = An implementation of PEG.js grammar for Erlang.
pkg_pegjs_homepage = https://github.com/dmitriid/pegjs
pkg_pegjs_fetch = git
pkg_pegjs_repo = https://github.com/dmitriid/pegjs
pkg_pegjs_commit = 0.3

PACKAGES += percept2
pkg_percept2_name = percept2
pkg_percept2_description = Concurrent profiling tool for Erlang
pkg_percept2_homepage = https://github.com/huiqing/percept2
pkg_percept2_fetch = git
pkg_percept2_repo = https://github.com/huiqing/percept2
pkg_percept2_commit = master

PACKAGES += pgsql
pkg_pgsql_name = pgsql
pkg_pgsql_description = Erlang PostgreSQL driver
pkg_pgsql_homepage = https://github.com/semiocast/pgsql
pkg_pgsql_fetch = git
pkg_pgsql_repo = https://github.com/semiocast/pgsql
pkg_pgsql_commit = master

PACKAGES += pkgx
pkg_pkgx_name = pkgx
pkg_pkgx_description = Build .deb packages from Erlang releases
pkg_pkgx_homepage = https://github.com/arjan/pkgx
pkg_pkgx_fetch = git
pkg_pkgx_repo = https://github.com/arjan/pkgx
pkg_pkgx_commit = master

PACKAGES += pkt
pkg_pkt_name = pkt
pkg_pkt_description = Erlang network protocol library
pkg_pkt_homepage = https://github.com/msantos/pkt
pkg_pkt_fetch = git
pkg_pkt_repo = https://github.com/msantos/pkt
pkg_pkt_commit = master

PACKAGES += plain_fsm
pkg_plain_fsm_name = plain_fsm
pkg_plain_fsm_description = A behaviour/support library for writing plain Erlang FSMs.
pkg_plain_fsm_homepage = https://github.com/uwiger/plain_fsm
pkg_plain_fsm_fetch = git
pkg_plain_fsm_repo = https://github.com/uwiger/plain_fsm
pkg_plain_fsm_commit = master

PACKAGES += plumtree
pkg_plumtree_name = plumtree
pkg_plumtree_description = Epidemic Broadcast Trees
pkg_plumtree_homepage = https://github.com/helium/plumtree
pkg_plumtree_fetch = git
pkg_plumtree_repo = https://github.com/helium/plumtree
pkg_plumtree_commit = master

PACKAGES += pmod_transform
pkg_pmod_transform_name = pmod_transform
pkg_pmod_transform_description = Parse transform for parameterized modules
pkg_pmod_transform_homepage = https://github.com/erlang/pmod_transform
pkg_pmod_transform_fetch = git
pkg_pmod_transform_repo = https://github.com/erlang/pmod_transform
pkg_pmod_transform_commit = master

PACKAGES += pobox
pkg_pobox_name = pobox
pkg_pobox_description = External buffer processes to protect against mailbox overflow in Erlang
pkg_pobox_homepage = https://github.com/ferd/pobox
pkg_pobox_fetch = git
pkg_pobox_repo = https://github.com/ferd/pobox
pkg_pobox_commit = master

PACKAGES += ponos
pkg_ponos_name = ponos
pkg_ponos_description = ponos is a simple yet powerful load generator written in erlang
pkg_ponos_homepage = https://github.com/klarna/ponos
pkg_ponos_fetch = git
pkg_ponos_repo = https://github.com/klarna/ponos
pkg_ponos_commit = master

PACKAGES += poolboy
pkg_poolboy_name = poolboy
pkg_poolboy_description = A hunky Erlang worker pool factory
pkg_poolboy_homepage = https://github.com/devinus/poolboy
pkg_poolboy_fetch = git
pkg_poolboy_repo = https://github.com/devinus/poolboy
pkg_poolboy_commit = master

PACKAGES += pooler
pkg_pooler_name = pooler
pkg_pooler_description = An OTP Process Pool Application
pkg_pooler_homepage = https://github.com/seth/pooler
pkg_pooler_fetch = git
pkg_pooler_repo = https://github.com/seth/pooler
pkg_pooler_commit = master

PACKAGES += pqueue
pkg_pqueue_name = pqueue
pkg_pqueue_description = Erlang Priority Queues
pkg_pqueue_homepage = https://github.com/okeuday/pqueue
pkg_pqueue_fetch = git
pkg_pqueue_repo = https://github.com/okeuday/pqueue
pkg_pqueue_commit = master

PACKAGES += procket
pkg_procket_name = procket
pkg_procket_description = Erlang interface to low level socket operations
pkg_procket_homepage = http://blog.listincomprehension.com/search/label/procket
pkg_procket_fetch = git
pkg_procket_repo = https://github.com/msantos/procket
pkg_procket_commit = master

PACKAGES += proper
pkg_proper_name = proper
pkg_proper_description = PropEr: a QuickCheck-inspired property-based testing tool for Erlang.
pkg_proper_homepage = http://proper.softlab.ntua.gr
pkg_proper_fetch = git
pkg_proper_repo = https://github.com/manopapad/proper
pkg_proper_commit = master

PACKAGES += prop
pkg_prop_name = prop
pkg_prop_description = An Erlang code scaffolding and generator system.
pkg_prop_homepage = https://github.com/nuex/prop
pkg_prop_fetch = git
pkg_prop_repo = https://github.com/nuex/prop
pkg_prop_commit = master

PACKAGES += props
pkg_props_name = props
pkg_props_description = Property structure library
pkg_props_homepage = https://github.com/greyarea/props
pkg_props_fetch = git
pkg_props_repo = https://github.com/greyarea/props
pkg_props_commit = master

PACKAGES += protobuffs
pkg_protobuffs_name = protobuffs
pkg_protobuffs_description = An implementation of Google's Protocol Buffers for Erlang, based on ngerakines/erlang_protobuffs.
pkg_protobuffs_homepage = https://github.com/basho/erlang_protobuffs
pkg_protobuffs_fetch = git
pkg_protobuffs_repo = https://github.com/basho/erlang_protobuffs
pkg_protobuffs_commit = master

PACKAGES += psycho
pkg_psycho_name = psycho
pkg_psycho_description = HTTP server that provides a WSGI-like interface for applications and middleware.
pkg_psycho_homepage = https://github.com/gar1t/psycho
pkg_psycho_fetch = git
pkg_psycho_repo = https://github.com/gar1t/psycho
pkg_psycho_commit = master

PACKAGES += ptrackerl
pkg_ptrackerl_name = ptrackerl
pkg_ptrackerl_description = Pivotal Tracker API Client written in Erlang
pkg_ptrackerl_homepage = https://github.com/inaka/ptrackerl
pkg_ptrackerl_fetch = git
pkg_ptrackerl_repo = https://github.com/inaka/ptrackerl
pkg_ptrackerl_commit = master

PACKAGES += purity
pkg_purity_name = purity
pkg_purity_description = A side-effect analyzer for Erlang
pkg_purity_homepage = https://github.com/mpitid/purity
pkg_purity_fetch = git
pkg_purity_repo = https://github.com/mpitid/purity
pkg_purity_commit = master

PACKAGES += push_service
pkg_push_service_name = push_service
pkg_push_service_description = Push service
pkg_push_service_homepage = https://github.com/hairyhum/push_service
pkg_push_service_fetch = git
pkg_push_service_repo = https://github.com/hairyhum/push_service
pkg_push_service_commit = master

PACKAGES += qdate
pkg_qdate_name = qdate
pkg_qdate_description = Date, time, and timezone parsing, formatting, and conversion for Erlang.
pkg_qdate_homepage = https://github.com/choptastic/qdate
pkg_qdate_fetch = git
pkg_qdate_repo = https://github.com/choptastic/qdate
pkg_qdate_commit = 0.4.0

PACKAGES += qrcode
pkg_qrcode_name = qrcode
pkg_qrcode_description = QR Code encoder in Erlang
pkg_qrcode_homepage = https://github.com/komone/qrcode
pkg_qrcode_fetch = git
pkg_qrcode_repo = https://github.com/komone/qrcode
pkg_qrcode_commit = master

PACKAGES += quest
pkg_quest_name = quest
pkg_quest_description = Learn Erlang through this set of challenges. An interactive system for getting to know Erlang.
pkg_quest_homepage = https://github.com/eriksoe/ErlangQuest
pkg_quest_fetch = git
pkg_quest_repo = https://github.com/eriksoe/ErlangQuest
pkg_quest_commit = master

PACKAGES += quickrand
pkg_quickrand_name = quickrand
pkg_quickrand_description = Quick Erlang Random Number Generation
pkg_quickrand_homepage = https://github.com/okeuday/quickrand
pkg_quickrand_fetch = git
pkg_quickrand_repo = https://github.com/okeuday/quickrand
pkg_quickrand_commit = master

PACKAGES += rabbit_exchange_type_riak
pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak
pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak
pkg_rabbit_exchange_type_riak_homepage = https://github.com/jbrisbin/riak-exchange
pkg_rabbit_exchange_type_riak_fetch = git
pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange
pkg_rabbit_exchange_type_riak_commit = master

PACKAGES += rabbit
pkg_rabbit_name = rabbit
pkg_rabbit_description = RabbitMQ Server
pkg_rabbit_homepage = https://www.rabbitmq.com/
pkg_rabbit_fetch = git
pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git
pkg_rabbit_commit = master

PACKAGES += rack
pkg_rack_name = rack
pkg_rack_description = Rack handler for erlang
pkg_rack_homepage = https://github.com/erlyvideo/rack
pkg_rack_fetch = git
pkg_rack_repo = https://github.com/erlyvideo/rack
pkg_rack_commit = master

PACKAGES += radierl
pkg_radierl_name = radierl
pkg_radierl_description = RADIUS protocol stack implemented in Erlang.
pkg_radierl_homepage = https://github.com/vances/radierl
pkg_radierl_fetch = git
pkg_radierl_repo = https://github.com/vances/radierl
pkg_radierl_commit = master

PACKAGES += rafter
pkg_rafter_name = rafter
pkg_rafter_description = An Erlang library application which implements the Raft consensus protocol
pkg_rafter_homepage = https://github.com/andrewjstone/rafter
pkg_rafter_fetch = git
pkg_rafter_repo = https://github.com/andrewjstone/rafter
pkg_rafter_commit = master

PACKAGES += ranch
pkg_ranch_name = ranch
pkg_ranch_description = Socket acceptor pool for TCP protocols.
pkg_ranch_homepage = http://ninenines.eu
pkg_ranch_fetch = git
pkg_ranch_repo = https://github.com/ninenines/ranch
pkg_ranch_commit = 1.1.0

PACKAGES += rbeacon
pkg_rbeacon_name = rbeacon
pkg_rbeacon_description = LAN discovery and presence in Erlang.
pkg_rbeacon_homepage = https://github.com/refuge/rbeacon
pkg_rbeacon_fetch = git
pkg_rbeacon_repo = https://github.com/refuge/rbeacon
pkg_rbeacon_commit = master

PACKAGES += rebar
pkg_rebar_name = rebar
pkg_rebar_description = Erlang build tool that makes it easy to compile and test Erlang applications, port drivers and releases.
pkg_rebar_homepage = http://www.rebar3.org
pkg_rebar_fetch = git
pkg_rebar_repo = https://github.com/rebar/rebar3
pkg_rebar_commit = master

PACKAGES += rebus
pkg_rebus_name = rebus
pkg_rebus_description = A stupid simple, internal, pub/sub event bus written in- and for Erlang.
pkg_rebus_homepage = https://github.com/olle/rebus
pkg_rebus_fetch = git
pkg_rebus_repo = https://github.com/olle/rebus
pkg_rebus_commit = master

PACKAGES += rec2json
pkg_rec2json_name = rec2json
pkg_rec2json_description = Compile erlang record definitions into modules to convert them to/from json easily.
pkg_rec2json_homepage = https://github.com/lordnull/rec2json
pkg_rec2json_fetch = git
pkg_rec2json_repo = https://github.com/lordnull/rec2json
pkg_rec2json_commit = master

PACKAGES += recon
pkg_recon_name = recon
pkg_recon_description = Collection of functions and scripts to debug Erlang in production.
pkg_recon_homepage = https://github.com/ferd/recon
pkg_recon_fetch = git
pkg_recon_repo = https://github.com/ferd/recon
pkg_recon_commit = 2.2.1

PACKAGES += record_info
pkg_record_info_name = record_info
pkg_record_info_description = Convert between record and proplist
pkg_record_info_homepage = https://github.com/bipthelin/erlang-record_info
pkg_record_info_fetch = git
pkg_record_info_repo = https://github.com/bipthelin/erlang-record_info
pkg_record_info_commit = master

PACKAGES += redgrid
pkg_redgrid_name = redgrid
pkg_redgrid_description = automatic Erlang node discovery via redis
pkg_redgrid_homepage = https://github.com/jkvor/redgrid
pkg_redgrid_fetch = git
pkg_redgrid_repo = https://github.com/jkvor/redgrid
pkg_redgrid_commit = master

PACKAGES += redo
pkg_redo_name = redo
pkg_redo_description = pipelined erlang redis client
pkg_redo_homepage = https://github.com/jkvor/redo
pkg_redo_fetch = git
pkg_redo_repo = https://github.com/jkvor/redo
pkg_redo_commit = master

PACKAGES += reltool_util
pkg_reltool_util_name = reltool_util
pkg_reltool_util_description = Erlang reltool utility functionality application
pkg_reltool_util_homepage = https://github.com/okeuday/reltool_util
pkg_reltool_util_fetch = git
pkg_reltool_util_repo = https://github.com/okeuday/reltool_util
pkg_reltool_util_commit = master

PACKAGES += relx
pkg_relx_name = relx
pkg_relx_description = Sane, simple release creation for Erlang
pkg_relx_homepage = https://github.com/erlware/relx
pkg_relx_fetch = git
pkg_relx_repo = https://github.com/erlware/relx
pkg_relx_commit = master

PACKAGES += resource_discovery
pkg_resource_discovery_name = resource_discovery
pkg_resource_discovery_description = An application used to dynamically discover resources present in an Erlang node cluster.
pkg_resource_discovery_homepage = http://erlware.org/
pkg_resource_discovery_fetch = git
pkg_resource_discovery_repo = https://github.com/erlware/resource_discovery
pkg_resource_discovery_commit = master

PACKAGES += restc
pkg_restc_name = restc
pkg_restc_description = Erlang Rest Client
pkg_restc_homepage = https://github.com/kivra/restclient
pkg_restc_fetch = git
pkg_restc_repo = https://github.com/kivra/restclient
pkg_restc_commit = master

PACKAGES += rfc4627_jsonrpc
pkg_rfc4627_jsonrpc_name = rfc4627_jsonrpc
pkg_rfc4627_jsonrpc_description = Erlang RFC4627 (JSON) codec and JSON-RPC server implementation.
pkg_rfc4627_jsonrpc_homepage = https://github.com/tonyg/erlang-rfc4627
pkg_rfc4627_jsonrpc_fetch = git
pkg_rfc4627_jsonrpc_repo = https://github.com/tonyg/erlang-rfc4627
pkg_rfc4627_jsonrpc_commit = master

PACKAGES += riakc
pkg_riakc_name = riakc
pkg_riakc_description = Erlang clients for Riak.
pkg_riakc_homepage = https://github.com/basho/riak-erlang-client
pkg_riakc_fetch = git
pkg_riakc_repo = https://github.com/basho/riak-erlang-client
pkg_riakc_commit = master

PACKAGES += riak_core
pkg_riak_core_name = riak_core
pkg_riak_core_description = Distributed systems infrastructure used by Riak.
pkg_riak_core_homepage = https://github.com/basho/riak_core
pkg_riak_core_fetch = git
pkg_riak_core_repo = https://github.com/basho/riak_core
pkg_riak_core_commit = master

PACKAGES += riak_dt
pkg_riak_dt_name = riak_dt
pkg_riak_dt_description = Convergent replicated datatypes in Erlang
pkg_riak_dt_homepage = https://github.com/basho/riak_dt
pkg_riak_dt_fetch = git
pkg_riak_dt_repo = https://github.com/basho/riak_dt
pkg_riak_dt_commit = master

PACKAGES += riak_ensemble
pkg_riak_ensemble_name = riak_ensemble
pkg_riak_ensemble_description = Multi-Paxos framework in Erlang
pkg_riak_ensemble_homepage = https://github.com/basho/riak_ensemble
pkg_riak_ensemble_fetch = git
pkg_riak_ensemble_repo = https://github.com/basho/riak_ensemble
pkg_riak_ensemble_commit = master

PACKAGES += riakhttpc
pkg_riakhttpc_name = riakhttpc
pkg_riakhttpc_description = Riak Erlang client using the HTTP interface
pkg_riakhttpc_homepage = https://github.com/basho/riak-erlang-http-client
pkg_riakhttpc_fetch = git
pkg_riakhttpc_repo = https://github.com/basho/riak-erlang-http-client
pkg_riakhttpc_commit = master

PACKAGES += riak_kv
pkg_riak_kv_name = riak_kv
pkg_riak_kv_description = Riak Key/Value Store
pkg_riak_kv_homepage = https://github.com/basho/riak_kv
pkg_riak_kv_fetch = git
pkg_riak_kv_repo = https://github.com/basho/riak_kv
pkg_riak_kv_commit = master

PACKAGES += riaknostic
pkg_riaknostic_name = riaknostic
pkg_riaknostic_description = A diagnostic tool for Riak installations, to find common errors asap
pkg_riaknostic_homepage = https://github.com/basho/riaknostic
pkg_riaknostic_fetch = git
pkg_riaknostic_repo = https://github.com/basho/riaknostic
pkg_riaknostic_commit = master

PACKAGES += riak_pg
pkg_riak_pg_name = riak_pg
pkg_riak_pg_description = Distributed process groups with riak_core.
pkg_riak_pg_homepage = https://github.com/cmeiklejohn/riak_pg
pkg_riak_pg_fetch = git
pkg_riak_pg_repo = https://github.com/cmeiklejohn/riak_pg
pkg_riak_pg_commit = master

PACKAGES += riak_pipe
pkg_riak_pipe_name = riak_pipe
pkg_riak_pipe_description = Riak Pipelines
pkg_riak_pipe_homepage = https://github.com/basho/riak_pipe
pkg_riak_pipe_fetch = git
pkg_riak_pipe_repo = https://github.com/basho/riak_pipe
pkg_riak_pipe_commit = master

PACKAGES += riakpool
pkg_riakpool_name = riakpool
pkg_riakpool_description = erlang riak client pool
pkg_riakpool_homepage = https://github.com/dweldon/riakpool
pkg_riakpool_fetch = git
pkg_riakpool_repo = https://github.com/dweldon/riakpool
pkg_riakpool_commit = master

PACKAGES += riak_sysmon
pkg_riak_sysmon_name = riak_sysmon
pkg_riak_sysmon_description = Simple OTP app for managing Erlang VM system_monitor event messages
pkg_riak_sysmon_homepage = https://github.com/basho/riak_sysmon
pkg_riak_sysmon_fetch = git
pkg_riak_sysmon_repo = https://github.com/basho/riak_sysmon
pkg_riak_sysmon_commit = master

PACKAGES += riak_test
pkg_riak_test_name = riak_test
pkg_riak_test_description = I'm in your cluster, testing your riaks
pkg_riak_test_homepage = https://github.com/basho/riak_test
pkg_riak_test_fetch = git
pkg_riak_test_repo = https://github.com/basho/riak_test
pkg_riak_test_commit = master

PACKAGES += rivus_cep
pkg_rivus_cep_name = rivus_cep
pkg_rivus_cep_description = Complex event processing in Erlang
pkg_rivus_cep_homepage = https://github.com/vascokk/rivus_cep
pkg_rivus_cep_fetch = git
pkg_rivus_cep_repo = https://github.com/vascokk/rivus_cep
pkg_rivus_cep_commit = master

PACKAGES += rlimit
pkg_rlimit_name = rlimit
pkg_rlimit_description = Magnus Klaar's rate limiter code from etorrent
pkg_rlimit_homepage = https://github.com/jlouis/rlimit
pkg_rlimit_fetch = git
pkg_rlimit_repo = https://github.com/jlouis/rlimit
pkg_rlimit_commit = master

PACKAGES += safetyvalve
pkg_safetyvalve_name = safetyvalve
pkg_safetyvalve_description = A safety valve for your erlang node
pkg_safetyvalve_homepage = https://github.com/jlouis/safetyvalve
pkg_safetyvalve_fetch = git
pkg_safetyvalve_repo = https://github.com/jlouis/safetyvalve
pkg_safetyvalve_commit = master

PACKAGES += seestar
pkg_seestar_name = seestar
pkg_seestar_description = The Erlang client for Cassandra 1.2+ binary protocol
pkg_seestar_homepage = https://github.com/iamaleksey/seestar
pkg_seestar_fetch = git
pkg_seestar_repo = https://github.com/iamaleksey/seestar
pkg_seestar_commit = master

PACKAGES += service
pkg_service_name = service
pkg_service_description = A minimal Erlang behavior for creating CloudI internal services
pkg_service_homepage = http://cloudi.org/
pkg_service_fetch = git
pkg_service_repo = https://github.com/CloudI/service
pkg_service_commit = master

PACKAGES += setup
pkg_setup_name = setup
pkg_setup_description = Generic setup utility for Erlang-based systems
pkg_setup_homepage = https://github.com/uwiger/setup
pkg_setup_fetch = git
pkg_setup_repo = https://github.com/uwiger/setup
pkg_setup_commit = master

PACKAGES += sext
pkg_sext_name = sext
pkg_sext_description = Sortable Erlang Term Serialization
pkg_sext_homepage = https://github.com/uwiger/sext
pkg_sext_fetch = git
pkg_sext_repo = https://github.com/uwiger/sext
pkg_sext_commit = master

PACKAGES += sfmt
pkg_sfmt_name = sfmt
pkg_sfmt_description = SFMT pseudo random number generator for Erlang.
pkg_sfmt_homepage = https://github.com/jj1bdx/sfmt-erlang
pkg_sfmt_fetch = git
pkg_sfmt_repo = https://github.com/jj1bdx/sfmt-erlang
pkg_sfmt_commit = master

PACKAGES += sgte
pkg_sgte_name = sgte
pkg_sgte_description = A simple Erlang Template Engine
pkg_sgte_homepage = https://github.com/filippo/sgte
pkg_sgte_fetch = git
pkg_sgte_repo = https://github.com/filippo/sgte
pkg_sgte_commit = master

PACKAGES += sheriff
pkg_sheriff_name = sheriff
pkg_sheriff_description = Parse transform for type based validation.
pkg_sheriff_homepage = http://ninenines.eu
pkg_sheriff_fetch = git
pkg_sheriff_repo = https://github.com/extend/sheriff
pkg_sheriff_commit = master

PACKAGES += shotgun
pkg_shotgun_name = shotgun
pkg_shotgun_description = better than just a gun
pkg_shotgun_homepage = https://github.com/inaka/shotgun
pkg_shotgun_fetch = git
pkg_shotgun_repo = https://github.com/inaka/shotgun
pkg_shotgun_commit = 0.1.0

PACKAGES += sidejob
pkg_sidejob_name = sidejob
pkg_sidejob_description = Parallel worker and capacity limiting library for Erlang
pkg_sidejob_homepage = https://github.com/basho/sidejob
pkg_sidejob_fetch = git
pkg_sidejob_repo = https://github.com/basho/sidejob
pkg_sidejob_commit = master

PACKAGES += sieve
pkg_sieve_name = sieve
pkg_sieve_description = sieve is a simple TCP routing proxy (layer 7) in erlang
pkg_sieve_homepage = https://github.com/benoitc/sieve
pkg_sieve_fetch = git
pkg_sieve_repo = https://github.com/benoitc/sieve
pkg_sieve_commit = master

PACKAGES += sighandler
pkg_sighandler_name = sighandler
pkg_sighandler_description = Handle UNIX signals in Er    lang
pkg_sighandler_homepage = https://github.com/jkingsbery/sighandler
pkg_sighandler_fetch = git
pkg_sighandler_repo = https://github.com/jkingsbery/sighandler
pkg_sighandler_commit = master

PACKAGES += simhash
pkg_simhash_name = simhash
pkg_simhash_description = Simhashing for Erlang -- hashing algorithm to find near-duplicates in binary data.
pkg_simhash_homepage = https://github.com/ferd/simhash
pkg_simhash_fetch = git
pkg_simhash_repo = https://github.com/ferd/simhash
pkg_simhash_commit = master

PACKAGES += simple_bridge
pkg_simple_bridge_name = simple_bridge
pkg_simple_bridge_description = A simple, standardized interface library to Erlang HTTP Servers.
pkg_simple_bridge_homepage = https://github.com/nitrogen/simple_bridge
pkg_simple_bridge_fetch = git
pkg_simple_bridge_repo = https://github.com/nitrogen/simple_bridge
pkg_simple_bridge_commit = master

PACKAGES += simple_oauth2
pkg_simple_oauth2_name = simple_oauth2
pkg_simple_oauth2_description = Simple erlang OAuth2 client module for any http server framework (Google, Facebook, Yandex, Vkontakte are preconfigured)
pkg_simple_oauth2_homepage = https://github.com/virtan/simple_oauth2
pkg_simple_oauth2_fetch = git
pkg_simple_oauth2_repo = https://github.com/virtan/simple_oauth2
pkg_simple_oauth2_commit = master

PACKAGES += skel
pkg_skel_name = skel
pkg_skel_description = A Streaming Process-based Skeleton Library for Erlang
pkg_skel_homepage = https://github.com/ParaPhrase/skel
pkg_skel_fetch = git
pkg_skel_repo = https://github.com/ParaPhrase/skel
pkg_skel_commit = master

PACKAGES += smother
pkg_smother_name = smother
pkg_smother_description = Extended code coverage metrics for Erlang.
pkg_smother_homepage = https://ramsay-t.github.io/Smother/
pkg_smother_fetch = git
pkg_smother_repo = https://github.com/ramsay-t/Smother
pkg_smother_commit = master

PACKAGES += social
pkg_social_name = social
pkg_social_description = Cowboy handler for social login via OAuth2 providers
pkg_social_homepage = https://github.com/dvv/social
pkg_social_fetch = git
pkg_social_repo = https://github.com/dvv/social
pkg_social_commit = master

PACKAGES += spapi_router
pkg_spapi_router_name = spapi_router
pkg_spapi_router_description = Partially-connected Erlang clustering
pkg_spapi_router_homepage = https://github.com/spilgames/spapi-router
pkg_spapi_router_fetch = git
pkg_spapi_router_repo = https://github.com/spilgames/spapi-router
pkg_spapi_router_commit = master

PACKAGES += sqerl
pkg_sqerl_name = sqerl
pkg_sqerl_description = An Erlang-flavoured SQL DSL
pkg_sqerl_homepage = https://github.com/hairyhum/sqerl
pkg_sqerl_fetch = git
pkg_sqerl_repo = https://github.com/hairyhum/sqerl
pkg_sqerl_commit = master

PACKAGES += srly
pkg_srly_name = srly
pkg_srly_description = Native Erlang Unix serial interface
pkg_srly_homepage = https://github.com/msantos/srly
pkg_srly_fetch = git
pkg_srly_repo = https://github.com/msantos/srly
pkg_srly_commit = master

PACKAGES += sshrpc
pkg_sshrpc_name = sshrpc
pkg_sshrpc_description = Erlang SSH RPC module (experimental)
pkg_sshrpc_homepage = https://github.com/jj1bdx/sshrpc
pkg_sshrpc_fetch = git
pkg_sshrpc_repo = https://github.com/jj1bdx/sshrpc
pkg_sshrpc_commit = master

PACKAGES += stable
pkg_stable_name = stable
pkg_stable_description = Library of assorted helpers for Cowboy web server.
pkg_stable_homepage = https://github.com/dvv/stable
pkg_stable_fetch = git
pkg_stable_repo = https://github.com/dvv/stable
pkg_stable_commit = master

PACKAGES += statebox
pkg_statebox_name = statebox
pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak.
pkg_statebox_homepage = https://github.com/mochi/statebox
pkg_statebox_fetch = git
pkg_statebox_repo = https://github.com/mochi/statebox
pkg_statebox_commit = master

PACKAGES += statebox_riak
pkg_statebox_riak_name = statebox_riak
pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media.
pkg_statebox_riak_homepage = https://github.com/mochi/statebox_riak
pkg_statebox_riak_fetch = git
pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak
pkg_statebox_riak_commit = master

PACKAGES += statman
pkg_statman_name = statman
pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM
pkg_statman_homepage = https://github.com/knutin/statman
pkg_statman_fetch = git
pkg_statman_repo = https://github.com/knutin/statman
pkg_statman_commit = master

PACKAGES += statsderl
pkg_statsderl_name = statsderl
pkg_statsderl_description = StatsD client (erlang)
pkg_statsderl_homepage = https://github.com/lpgauth/statsderl
pkg_statsderl_fetch = git
pkg_statsderl_repo = https://github.com/lpgauth/statsderl
pkg_statsderl_commit = master

PACKAGES += stdinout_pool
pkg_stdinout_pool_name = stdinout_pool
pkg_stdinout_pool_description = stdinout_pool    : stuff goes in, stuff goes out. there's never any miscommunication.
pkg_stdinout_pool_homepage = https://github.com/mattsta/erlang-stdinout-pool
pkg_stdinout_pool_fetch = git
pkg_stdinout_pool_repo = https://github.com/mattsta/erlang-stdinout-pool
pkg_stdinout_pool_commit = master

PACKAGES += stockdb
pkg_stockdb_name = stockdb
pkg_stockdb_description = Database for storing Stock Exchange quotes in erlang
pkg_stockdb_homepage = https://github.com/maxlapshin/stockdb
pkg_stockdb_fetch = git
pkg_stockdb_repo = https://github.com/maxlapshin/stockdb
pkg_stockdb_commit = master

PACKAGES += stripe
pkg_stripe_name = stripe
pkg_stripe_description = Erlang interface to the stripe.com API
pkg_stripe_homepage = https://github.com/mattsta/stripe-erlang
pkg_stripe_fetch = git
pkg_stripe_repo = https://github.com/mattsta/stripe-erlang
pkg_stripe_commit = v1

PACKAGES += surrogate
pkg_surrogate_name = surrogate
pkg_surrogate_description = Proxy server written in erlang. Supports reverse proxy load balancing and forward proxy with http (including CONNECT), socks4, socks5, and transparent proxy modes.
pkg_surrogate_homepage = https://github.com/skruger/Surrogate
pkg_surrogate_fetch = git
pkg_surrogate_repo = https://github.com/skruger/Surrogate
pkg_surrogate_commit = master

PACKAGES += swab
pkg_swab_name = swab
pkg_swab_description = General purpose buffer handling module
pkg_swab_homepage = https://github.com/crownedgrouse/swab
pkg_swab_fetch = git
pkg_swab_repo = https://github.com/crownedgrouse/swab
pkg_swab_commit = master

PACKAGES += swarm
pkg_swarm_name = swarm
pkg_swarm_description = Fast and simple acceptor pool for Erlang
pkg_swarm_homepage = https://github.com/jeremey/swarm
pkg_swarm_fetch = git
pkg_swarm_repo = https://github.com/jeremey/swarm
pkg_swarm_commit = master

PACKAGES += switchboard
pkg_switchboard_name = switchboard
pkg_switchboard_description = A framework for processing email using worker plugins.
pkg_switchboard_homepage = https://github.com/thusfresh/switchboard
pkg_switchboard_fetch = git
pkg_switchboard_repo = https://github.com/thusfresh/switchboard
pkg_switchboard_commit = master

PACKAGES += sync
pkg_sync_name = sync
pkg_sync_description = On-the-fly recompiling and reloading in Erlang.
pkg_sync_homepage = https://github.com/rustyio/sync
pkg_sync_fetch = git
pkg_sync_repo = https://github.com/rustyio/sync
pkg_sync_commit = master

PACKAGES += syn
pkg_syn_name = syn
pkg_syn_description = A global process registry for Erlang.
pkg_syn_homepage = https://github.com/ostinelli/syn
pkg_syn_fetch = git
pkg_syn_repo = https://github.com/ostinelli/syn
pkg_syn_commit = master

PACKAGES += syntaxerl
pkg_syntaxerl_name = syntaxerl
pkg_syntaxerl_description = Syntax checker for Erlang
pkg_syntaxerl_homepage = https://github.com/ten0s/syntaxerl
pkg_syntaxerl_fetch = git
pkg_syntaxerl_repo = https://github.com/ten0s/syntaxerl
pkg_syntaxerl_commit = master

PACKAGES += syslog
pkg_syslog_name = syslog
pkg_syslog_description = Erlang port driver for interacting with syslog via syslog(3)
pkg_syslog_homepage = https://github.com/Vagabond/erlang-syslog
pkg_syslog_fetch = git
pkg_syslog_repo = https://github.com/Vagabond/erlang-syslog
pkg_syslog_commit = master

PACKAGES += taskforce
pkg_taskforce_name = taskforce
pkg_taskforce_description = Erlang worker pools for controlled parallelisation of arbitrary tasks.
pkg_taskforce_homepage = https://github.com/g-andrade/taskforce
pkg_taskforce_fetch = git
pkg_taskforce_repo = https://github.com/g-andrade/taskforce
pkg_taskforce_commit = master

PACKAGES += tddreloader
pkg_tddreloader_name = tddreloader
pkg_tddreloader_description = Shell utility for recompiling, reloading, and testing code as it changes
pkg_tddreloader_homepage = https://github.com/version2beta/tddreloader
pkg_tddreloader_fetch = git
pkg_tddreloader_repo = https://github.com/version2beta/tddreloader
pkg_tddreloader_commit = master

PACKAGES += tempo
pkg_tempo_name = tempo
pkg_tempo_description = NIF-based date and time parsing and formatting for Erlang.
pkg_tempo_homepage = https://github.com/selectel/tempo
pkg_tempo_fetch = git
pkg_tempo_repo = https://github.com/selectel/tempo
pkg_tempo_commit = master

PACKAGES += ticktick
pkg_ticktick_name = ticktick
pkg_ticktick_description = Ticktick is an id generator for message service.
pkg_ticktick_homepage = https://github.com/ericliang/ticktick
pkg_ticktick_fetch = git
pkg_ticktick_repo = https://github.com/ericliang/ticktick
pkg_ticktick_commit = master

PACKAGES += tinymq
pkg_tinymq_name = tinymq
pkg_tinymq_description = TinyMQ - a diminutive, in-memory message queue
pkg_tinymq_homepage = https://github.com/ChicagoBoss/tinymq
pkg_tinymq_fetch = git
pkg_tinymq_repo = https://github.com/ChicagoBoss/tinymq
pkg_tinymq_commit = master

PACKAGES += tinymt
pkg_tinymt_name = tinymt
pkg_tinymt_description = TinyMT pseudo random number generator for Erlang.
pkg_tinymt_homepage = https://github.com/jj1bdx/tinymt-erlang
pkg_tinymt_fetch = git
pkg_tinymt_repo = https://github.com/jj1bdx/tinymt-erlang
pkg_tinymt_commit = master

PACKAGES += tirerl
pkg_tirerl_name = tirerl
pkg_tirerl_description = Erlang interface to Elastic Search
pkg_tirerl_homepage = https://github.com/inaka/tirerl
pkg_tirerl_fetch = git
pkg_tirerl_repo = https://github.com/inaka/tirerl
pkg_tirerl_commit = master

PACKAGES += traffic_tools
pkg_traffic_tools_name = traffic_tools
pkg_traffic_tools_description = Simple traffic limiting library
pkg_traffic_tools_homepage = https://github.com/systra/traffic_tools
pkg_traffic_tools_fetch = git
pkg_traffic_tools_repo = https://github.com/systra/traffic_tools
pkg_traffic_tools_commit = master

PACKAGES += trails
pkg_trails_name = trails
pkg_trails_description = A couple of improvements over Cowboy Routes
pkg_trails_homepage = http://inaka.github.io/cowboy-trails/
pkg_trails_fetch = git
pkg_trails_repo = https://github.com/inaka/cowboy-trails
pkg_trails_commit = master

PACKAGES += trane
pkg_trane_name = trane
pkg_trane_description = SAX style broken HTML parser in Erlang
pkg_trane_homepage = https://github.com/massemanet/trane
pkg_trane_fetch = git
pkg_trane_repo = https://github.com/massemanet/trane
pkg_trane_commit = master

PACKAGES += transit
pkg_transit_name = transit
pkg_transit_description = transit format for erlang
pkg_transit_homepage = https://github.com/isaiah/transit-erlang
pkg_transit_fetch = git
pkg_transit_repo = https://github.com/isaiah/transit-erlang
pkg_transit_commit = master

PACKAGES += trie
pkg_trie_name = trie
pkg_trie_description = Erlang Trie Implementation
pkg_trie_homepage = https://github.com/okeuday/trie
pkg_trie_fetch = git
pkg_trie_repo = https://github.com/okeuday/trie
pkg_trie_commit = master

PACKAGES += triq
pkg_triq_name = triq
pkg_triq_description = Trifork QuickCheck
pkg_triq_homepage = https://github.com/krestenkrab/triq
pkg_triq_fetch = git
pkg_triq_repo = https://github.com/krestenkrab/triq
pkg_triq_commit = master

PACKAGES += tunctl
pkg_tunctl_name = tunctl
pkg_tunctl_description = Erlang TUN/TAP interface
pkg_tunctl_homepage = https://github.com/msantos/tunctl
pkg_tunctl_fetch = git
pkg_tunctl_repo = https://github.com/msantos/tunctl
pkg_tunctl_commit = master

PACKAGES += twerl
pkg_twerl_name = twerl
pkg_twerl_description = Erlang client for the Twitter Streaming API
pkg_twerl_homepage = https://github.com/lucaspiller/twerl
pkg_twerl_fetch = git
pkg_twerl_repo = https://github.com/lucaspiller/twerl
pkg_twerl_commit = oauth

PACKAGES += twitter_erlang
pkg_twitter_erlang_name = twitter_erlang
pkg_twitter_erlang_description = An Erlang twitter client
pkg_twitter_erlang_homepage = https://github.com/ngerakines/erlang_twitter
pkg_twitter_erlang_fetch = git
pkg_twitter_erlang_repo = https://github.com/ngerakines/erlang_twitter
pkg_twitter_erlang_commit = master

PACKAGES += ucol_nif
pkg_ucol_nif_name = ucol_nif
pkg_ucol_nif_description = ICU based collation Erlang module
pkg_ucol_nif_homepage = https://github.com/refuge/ucol_nif
pkg_ucol_nif_fetch = git
pkg_ucol_nif_repo = https://github.com/refuge/ucol_nif
pkg_ucol_nif_commit = master

PACKAGES += unicorn
pkg_unicorn_name = unicorn
pkg_unicorn_description = Generic configuration server
pkg_unicorn_homepage = https://github.com/shizzard/unicorn
pkg_unicorn_fetch = git
pkg_unicorn_repo = https://github.com/shizzard/unicorn
pkg_unicorn_commit = 0.3.0

PACKAGES += unsplit
pkg_unsplit_name = unsplit
pkg_unsplit_description = Resolves conflicts in Mnesia after network splits
pkg_unsplit_homepage = https://github.com/uwiger/unsplit
pkg_unsplit_fetch = git
pkg_unsplit_repo = https://github.com/uwiger/unsplit
pkg_unsplit_commit = master

PACKAGES += uuid
pkg_uuid_name = uuid
pkg_uuid_description = Erlang UUID Implementation
pkg_uuid_homepage = https://github.com/okeuday/uuid
pkg_uuid_fetch = git
pkg_uuid_repo = https://github.com/okeuday/uuid
pkg_uuid_commit = v1.4.0

PACKAGES += ux
pkg_ux_name = ux
pkg_ux_description = Unicode eXtention for Erlang (Strings, Collation)
pkg_ux_homepage = https://github.com/erlang-unicode/ux
pkg_ux_fetch = git
pkg_ux_repo = https://github.com/erlang-unicode/ux
pkg_ux_commit = master

PACKAGES += vert
pkg_vert_name = vert
pkg_vert_description = Erlang binding to libvirt virtualization API
pkg_vert_homepage = https://github.com/msantos/erlang-libvirt
pkg_vert_fetch = git
pkg_vert_repo = https://github.com/msantos/erlang-libvirt
pkg_vert_commit = master

PACKAGES += verx
pkg_verx_name = verx
pkg_verx_description = Erlang implementation of the libvirtd remote protocol
pkg_verx_homepage = https://github.com/msantos/verx
pkg_verx_fetch = git
pkg_verx_repo = https://github.com/msantos/verx
pkg_verx_commit = master

PACKAGES += vmq_acl
pkg_vmq_acl_name = vmq_acl
pkg_vmq_acl_description = Component of VerneMQ: A distributed MQTT message broker
pkg_vmq_acl_homepage = https://verne.mq/
pkg_vmq_acl_fetch = git
pkg_vmq_acl_repo = https://github.com/erlio/vmq_acl
pkg_vmq_acl_commit = master

PACKAGES += vmq_bridge
pkg_vmq_bridge_name = vmq_bridge
pkg_vmq_bridge_description = Component of VerneMQ: A distributed MQTT message broker
pkg_vmq_bridge_homepage = https://verne.mq/
pkg_vmq_bridge_fetch = git
pkg_vmq_bridge_repo = https://github.com/erlio/vmq_bridge
pkg_vmq_bridge_commit = master

PACKAGES += vmq_graphite
pkg_vmq_graphite_name = vmq_graphite
pkg_vmq_graphite_description = Component of VerneMQ: A distributed MQTT message broker
pkg_vmq_graphite_homepage = https://verne.mq/
pkg_vmq_graphite_fetch = git
pkg_vmq_graphite_repo = https://github.com/erlio/vmq_graphite
pkg_vmq_graphite_commit = master

PACKAGES += vmq_passwd
pkg_vmq_passwd_name = vmq_passwd
pkg_vmq_passwd_description = Component of VerneMQ: A distributed MQTT message broker
pkg_vmq_passwd_homepage = https://verne.mq/
pkg_vmq_passwd_fetch = git
pkg_vmq_passwd_repo = https://github.com/erlio/vmq_passwd
pkg_vmq_passwd_commit = master

PACKAGES += vmq_server
pkg_vmq_server_name = vmq_server
pkg_vmq_server_description = Component of VerneMQ: A distributed MQTT message broker
pkg_vmq_server_homepage = https://verne.mq/
pkg_vmq_server_fetch = git
pkg_vmq_server_repo = https://github.com/erlio/vmq_server
pkg_vmq_server_commit = master

PACKAGES += vmq_snmp
pkg_vmq_snmp_name = vmq_snmp
pkg_vmq_snmp_description = Component of VerneMQ: A distributed MQTT message broker
pkg_vmq_snmp_homepage = https://verne.mq/
pkg_vmq_snmp_fetch = git
pkg_vmq_snmp_repo = https://github.com/erlio/vmq_snmp
pkg_vmq_snmp_commit = master

PACKAGES += vmq_systree
pkg_vmq_systree_name = vmq_systree
pkg_vmq_systree_description = Component of VerneMQ: A distributed MQTT message broker
pkg_vmq_systree_homepage = https://verne.mq/
pkg_vmq_systree_fetch = git
pkg_vmq_systree_repo = https://github.com/erlio/vmq_systree
pkg_vmq_systree_commit = master

PACKAGES += vmstats
pkg_vmstats_name = vmstats
pkg_vmstats_description = tiny Erlang app that works in conjunction with statsderl in order to generate information on the Erlang VM for graphite logs.
pkg_vmstats_homepage = https://github.com/ferd/vmstats
pkg_vmstats_fetch = git
pkg_vmstats_repo = https://github.com/ferd/vmstats
pkg_vmstats_commit = master

PACKAGES += walrus
pkg_walrus_name = walrus
pkg_walrus_description = Walrus - Mustache-like Templating
pkg_walrus_homepage = https://github.com/devinus/walrus
pkg_walrus_fetch = git
pkg_walrus_repo = https://github.com/devinus/walrus
pkg_walrus_commit = master

PACKAGES += webmachine
pkg_webmachine_name = webmachine
pkg_webmachine_description = A REST-based system for building web applications.
pkg_webmachine_homepage = https://github.com/basho/webmachine
pkg_webmachine_fetch = git
pkg_webmachine_repo = https://github.com/basho/webmachine
pkg_webmachine_commit = master

PACKAGES += websocket_client
pkg_websocket_client_name = websocket_client
pkg_websocket_client_description = Erlang websocket client (ws and wss supported)
pkg_websocket_client_homepage = https://github.com/jeremyong/websocket_client
pkg_websocket_client_fetch = git
pkg_websocket_client_repo = https://github.com/jeremyong/websocket_client
pkg_websocket_client_commit = master

PACKAGES += worker_pool
pkg_worker_pool_name = worker_pool
pkg_worker_pool_description = a simple erlang worker pool
pkg_worker_pool_homepage = https://github.com/inaka/worker_pool
pkg_worker_pool_fetch = git
pkg_worker_pool_repo = https://github.com/inaka/worker_pool
pkg_worker_pool_commit = 1.0.2

PACKAGES += wrangler
pkg_wrangler_name = wrangler
pkg_wrangler_description = Import of the Wrangler svn repository.
pkg_wrangler_homepage = http://www.cs.kent.ac.uk/projects/wrangler/Home.html
pkg_wrangler_fetch = git
pkg_wrangler_repo = https://github.com/RefactoringTools/wrangler
pkg_wrangler_commit = master

PACKAGES += wsock
pkg_wsock_name = wsock
pkg_wsock_description = Erlang library to build WebSocket clients and servers
pkg_wsock_homepage = https://github.com/madtrick/wsock
pkg_wsock_fetch = git
pkg_wsock_repo = https://github.com/madtrick/wsock
pkg_wsock_commit = master

PACKAGES += xhttpc
pkg_xhttpc_name = xhttpc
pkg_xhttpc_description = Extensible HTTP Client for Erlang
pkg_xhttpc_homepage = https://github.com/seriyps/xhttpc
pkg_xhttpc_fetch = git
pkg_xhttpc_repo = https://github.com/seriyps/xhttpc
pkg_xhttpc_commit = master

PACKAGES += xref_runner
pkg_xref_runner_name = xref_runner
pkg_xref_runner_description = Erlang Xref Runner (inspired in rebar xref)
pkg_xref_runner_homepage = https://github.com/inaka/xref_runner
pkg_xref_runner_fetch = git
pkg_xref_runner_repo = https://github.com/inaka/xref_runner
pkg_xref_runner_commit = 0.2.0

PACKAGES += yamerl
pkg_yamerl_name = yamerl
pkg_yamerl_description = YAML 1.2 parser in pure Erlang
pkg_yamerl_homepage = https://github.com/yakaz/yamerl
pkg_yamerl_fetch = git
pkg_yamerl_repo = https://github.com/yakaz/yamerl
pkg_yamerl_commit = master

PACKAGES += yamler
pkg_yamler_name = yamler
pkg_yamler_description = libyaml-based yaml loader for Erlang
pkg_yamler_homepage = https://github.com/goertzenator/yamler
pkg_yamler_fetch = git
pkg_yamler_repo = https://github.com/goertzenator/yamler
pkg_yamler_commit = master

PACKAGES += yaws
pkg_yaws_name = yaws
pkg_yaws_description = Yaws webserver
pkg_yaws_homepage = http://yaws.hyber.org
pkg_yaws_fetch = git
pkg_yaws_repo = https://github.com/klacke/yaws
pkg_yaws_commit = master

PACKAGES += zab_engine
pkg_zab_engine_name = zab_engine
pkg_zab_engine_description = zab propotocol implement by erlang
pkg_zab_engine_homepage = https://github.com/xinmingyao/zab_engine
pkg_zab_engine_fetch = git
pkg_zab_engine_repo = https://github.com/xinmingyao/zab_engine
pkg_zab_engine_commit = master

PACKAGES += zeta
pkg_zeta_name = zeta
pkg_zeta_description = HTTP access log parser in Erlang
pkg_zeta_homepage = https://github.com/s1n4/zeta
pkg_zeta_fetch = git
pkg_zeta_repo = https://github.com/s1n4/zeta
pkg_zeta_commit =  

PACKAGES += zippers
pkg_zippers_name = zippers
pkg_zippers_description = A library for functional zipper data structures in Erlang. Read more on zippers
pkg_zippers_homepage = https://github.com/ferd/zippers
pkg_zippers_fetch = git
pkg_zippers_repo = https://github.com/ferd/zippers
pkg_zippers_commit = master

PACKAGES += zlists
pkg_zlists_name = zlists
pkg_zlists_description = Erlang lazy lists library.
pkg_zlists_homepage = https://github.com/vjache/erlang-zlists
pkg_zlists_fetch = git
pkg_zlists_repo = https://github.com/vjache/erlang-zlists
pkg_zlists_commit = master

PACKAGES += zraft_lib
pkg_zraft_lib_name = zraft_lib
pkg_zraft_lib_description = Erlang raft consensus protocol implementation
pkg_zraft_lib_homepage = https://github.com/dreyk/zraft_lib
pkg_zraft_lib_fetch = git
pkg_zraft_lib_repo = https://github.com/dreyk/zraft_lib
pkg_zraft_lib_commit = master

PACKAGES += zucchini
pkg_zucchini_name = zucchini
pkg_zucchini_description = An Erlang INI parser
pkg_zucchini_homepage = https://github.com/devinus/zucchini
pkg_zucchini_fetch = git
pkg_zucchini_repo = https://github.com/devinus/zucchini
pkg_zucchini_commit = master

# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: search

define pkg_print
	$(verbose) printf "%s\n" \
		$(if $(call core_eq,$(1),$(pkg_$(1)_name)),,"Pkg name:    $(1)") \
		"App name:    $(pkg_$(1)_name)" \
		"Description: $(pkg_$(1)_description)" \
		"Home page:   $(pkg_$(1)_homepage)" \
		"Fetch with:  $(pkg_$(1)_fetch)" \
		"Repository:  $(pkg_$(1)_repo)" \
		"Commit:      $(pkg_$(1)_commit)" \
		""

endef

search:
ifdef q
	$(foreach p,$(PACKAGES), \
		$(if $(findstring $(call core_lc,$(q)),$(call core_lc,$(pkg_$(p)_name) $(pkg_$(p)_description))), \
			$(call pkg_print,$(p))))
else
	$(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
endif

# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: distclean-deps distclean-pkg

# Configuration.

IGNORE_DEPS ?=

DEPS_DIR ?= $(CURDIR)/deps
export DEPS_DIR

REBAR_DEPS_DIR = $(DEPS_DIR)
export REBAR_DEPS_DIR

ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(filter-out $(IGNORE_DEPS),$(DEPS)))

ifeq ($(filter $(DEPS_DIR),$(subst :, ,$(ERL_LIBS))),)
ifeq ($(ERL_LIBS),)
	ERL_LIBS = $(DEPS_DIR)
else
	ERL_LIBS := $(ERL_LIBS):$(DEPS_DIR)
endif
endif
export ERL_LIBS

# Verbosity.

dep_verbose_0 = @echo " DEP   " $(1);
dep_verbose = $(dep_verbose_$(V))

# Core targets.

ifneq ($(SKIP_DEPS),)
deps::
else
deps:: $(ALL_DEPS_DIRS)
ifneq ($(IS_DEP),1)
	$(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
endif
	$(verbose) mkdir -p $(ERLANG_MK_TMP)
	$(verbose) for dep in $(ALL_DEPS_DIRS) ; do \
		if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \
			echo -n; \
		else \
			echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \
			if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \
				$(MAKE) -C $$dep IS_DEP=1 || exit $$?; \
			else \
				echo "ERROR: No Makefile to build dependency $$dep."; \
				exit 1; \
			fi \
		fi \
	done
endif

distclean:: distclean-deps distclean-pkg

# Deps related targets.

# @todo rename GNUmakefile and makefile into Makefile first, if they exist
# While Makefile file could be GNUmakefile or makefile,
# in practice only Makefile is needed so far.
define dep_autopatch
	if [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
		if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \
			$(call dep_autopatch2,$(1)); \
		elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \
			$(call dep_autopatch2,$(1)); \
		elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk | xargs -r grep -i rebar`" ]; then \
			$(call dep_autopatch2,$(1)); \
		else \
			if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \
				$(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
				$(call dep_autopatch_erlang_mk,$(1)); \
			else \
				$(call erlang,$(call dep_autopatch_app.erl,$(1))); \
			fi \
		fi \
	else \
		if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \
			$(call dep_autopatch_noop,$(1)); \
		else \
			$(call dep_autopatch2,$(1)); \
		fi \
	fi
endef

define dep_autopatch2
	$(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \
	if [ -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \
		$(call dep_autopatch_fetch_rebar); \
		$(call dep_autopatch_rebar,$(1)); \
	else \
		$(call dep_autopatch_gen,$(1)); \
	fi
endef

define dep_autopatch_noop
	printf "noop:\n" > $(DEPS_DIR)/$(1)/Makefile
endef

# Overwrite erlang.mk with the current file by default.
ifeq ($(NO_AUTOPATCH_ERLANG_MK),)
define dep_autopatch_erlang_mk
	echo "include $(ERLANG_MK_FILENAME)" > $(DEPS_DIR)/$(1)/erlang.mk
endef
else
define dep_autopatch_erlang_mk
	echo -n
endef
endif

define dep_autopatch_gen
	printf "%s\n" \
		"ERLC_OPTS = +debug_info" \
		"include ../../erlang.mk" > $(DEPS_DIR)/$(1)/Makefile
endef

define dep_autopatch_fetch_rebar
	mkdir -p $(ERLANG_MK_TMP); \
	if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
		git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
		cd $(ERLANG_MK_TMP)/rebar; \
		git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
		$(MAKE); \
		cd -; \
	fi
endef

define dep_autopatch_rebar
	if [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \
		mv $(DEPS_DIR)/$(1)/Makefile $(DEPS_DIR)/$(1)/Makefile.orig.mk; \
	fi; \
	$(call erlang,$(call dep_autopatch_rebar.erl,$(1))); \
	rm -f $(DEPS_DIR)/$(1)/ebin/$(1).app
endef

define dep_autopatch_rebar.erl
	application:set_env(rebar, log_level, debug),
	Conf1 = case file:consult("$(DEPS_DIR)/$(1)/rebar.config") of
		{ok, Conf0} -> Conf0;
		_ -> []
	end,
	{Conf, OsEnv} = fun() ->
		case filelib:is_file("$(DEPS_DIR)/$(1)/rebar.config.script") of
			false -> {Conf1, []};
			true ->
				Bindings0 = erl_eval:new_bindings(),
				Bindings1 = erl_eval:add_binding('CONFIG', Conf1, Bindings0),
				Bindings = erl_eval:add_binding('SCRIPT', "$(DEPS_DIR)/$(1)/rebar.config.script", Bindings1),
				Before = os:getenv(),
				{ok, Conf2} = file:script("$(DEPS_DIR)/$(1)/rebar.config.script", Bindings),
				{Conf2, lists:foldl(fun(E, Acc) -> lists:delete(E, Acc) end, os:getenv(), Before)}
		end
	end(),
	Write = fun (Text) ->
		file:write_file("$(DEPS_DIR)/$(1)/Makefile", Text, [append])
	end,
	Escape = fun (Text) ->
		re:replace(Text, "\\\\$$$$", "\$$$$$$$$", [global, {return, list}])
	end,
	Write("IGNORE_DEPS = edown eper eunit_formatters meck node_package "
		"rebar_lock_deps_plugin rebar_vsn_plugin reltool_util\n"),
	Write("C_SRC_DIR = /path/do/not/exist\n"),
	Write("DRV_CFLAGS = -fPIC\nexport DRV_CFLAGS\n"),
	Write(["ERLANG_ARCH = ", rebar_utils:wordsize(), "\nexport ERLANG_ARCH\n"]),
	fun() ->
		Write("ERLC_OPTS = +debug_info\nexport ERLC_OPTS\n"),
		case lists:keyfind(erl_opts, 1, Conf) of
			false -> ok;
			{_, ErlOpts} ->
				lists:foreach(fun
					({d, D}) ->
						Write("ERLC_OPTS += -D" ++ atom_to_list(D) ++ "=1\n");
					({i, I}) ->
						Write(["ERLC_OPTS += -I ", I, "\n"]);
					({platform_define, Regex, D}) ->
						case rebar_utils:is_arch(Regex) of
							true -> Write("ERLC_OPTS += -D" ++ atom_to_list(D) ++ "=1\n");
							false -> ok
						end;
					({parse_transform, PT}) ->
						Write("ERLC_OPTS += +'{parse_transform, " ++ atom_to_list(PT) ++ "}'\n");
					(_) -> ok
				end, ErlOpts)
		end,
		Write("\n")
	end(),
	fun() ->
		File = case lists:keyfind(deps, 1, Conf) of
			false -> [];
			{_, Deps} ->
				[begin case case Dep of
							{N, S} when is_atom(N), is_list(S) -> {N, {hex, S}};
							{N, S} when is_tuple(S) -> {N, S};
							{N, _, S} -> {N, S};
							{N, _, S, _} -> {N, S};
							_ -> false
						end of
					false -> ok;
					{Name, Source} ->
						{Method, Repo, Commit} = case Source of
							{hex, V} -> {hex, undefined, V};
							{git, R} -> {git, R, master};
							{M, R, {branch, C}} -> {M, R, C};
							{M, R, {ref, C}} -> {M, R, C};
							{M, R, {tag, C}} -> {M, R, C};
							{M, R, C} -> {M, R, C}
						end,
						Write(io_lib:format("DEPS += ~s\ndep_~s = ~s ~s ~s~n", [Name, Name, Method, Repo, Commit]))
				end end || Dep <- Deps]
		end
	end(),
	fun() ->
		case lists:keyfind(erl_first_files, 1, Conf) of
			false -> ok;
			{_, Files} ->
				Names = [[" ", case lists:reverse(F) of
					"lre." ++ Elif -> lists:reverse(Elif);
					Elif -> lists:reverse(Elif)
				end] || "src/" ++ F <- Files],
				Write(io_lib:format("COMPILE_FIRST +=~s\n", [Names]))
		end
	end(),
	FindFirst = fun(F, Fd) ->
		case io:parse_erl_form(Fd, undefined) of
			{ok, {attribute, _, compile, {parse_transform, PT}}, _} ->
				[PT, F(F, Fd)];
			{ok, {attribute, _, compile, CompileOpts}, _} when is_list(CompileOpts) ->
				case proplists:get_value(parse_transform, CompileOpts) of
					undefined -> [F(F, Fd)];
					PT -> [PT, F(F, Fd)]
				end;
			{ok, {attribute, _, include, Hrl}, _} ->
				case file:open("$(DEPS_DIR)/$(1)/include/" ++ Hrl, [read]) of
					{ok, HrlFd} -> [F(F, HrlFd), F(F, Fd)];
					_ ->
						case file:open("$(DEPS_DIR)/$(1)/src/" ++ Hrl, [read]) of
							{ok, HrlFd} -> [F(F, HrlFd), F(F, Fd)];
							_ -> [F(F, Fd)]
						end
				end;
			{ok, {attribute, _, include_lib, "$(1)/include/" ++ Hrl}, _} ->
				{ok, HrlFd} = file:open("$(DEPS_DIR)/$(1)/include/" ++ Hrl, [read]),
				[F(F, HrlFd), F(F, Fd)];
			{ok, {attribute, _, include_lib, Hrl}, _} ->
				case file:open("$(DEPS_DIR)/$(1)/include/" ++ Hrl, [read]) of
					{ok, HrlFd} -> [F(F, HrlFd), F(F, Fd)];
					_ -> [F(F, Fd)]
				end;
			{ok, {attribute, _, import, {Imp, _}}, _} ->
				case file:open("$(DEPS_DIR)/$(1)/src/" ++ atom_to_list(Imp) ++ ".erl", [read]) of
					{ok, ImpFd} -> [Imp, F(F, ImpFd), F(F, Fd)];
					_ -> [F(F, Fd)]
				end;
			{eof, _} ->
				file:close(Fd),
				[];
			_ ->
				F(F, Fd)
		end
	end,
	fun() ->
		ErlFiles = filelib:wildcard("$(DEPS_DIR)/$(1)/src/*.erl"),
		First0 = lists:usort(lists:flatten([begin
			{ok, Fd} = file:open(F, [read]),
			FindFirst(FindFirst, Fd)
		end || F <- ErlFiles])),
		First = lists:flatten([begin
			{ok, Fd} = file:open("$(DEPS_DIR)/$(1)/src/" ++ atom_to_list(M) ++ ".erl", [read]),
			FindFirst(FindFirst, Fd)
		end || M <- First0, lists:member("$(DEPS_DIR)/$(1)/src/" ++ atom_to_list(M) ++ ".erl", ErlFiles)]) ++ First0,
		Write(["COMPILE_FIRST +=", [[" ", atom_to_list(M)] || M <- First,
			lists:member("$(DEPS_DIR)/$(1)/src/" ++ atom_to_list(M) ++ ".erl", ErlFiles)], "\n"])
	end(),
	Write("\n\nrebar_dep: preprocess pre-deps deps pre-app app\n"),
	Write("\npreprocess::\n"),
	Write("\npre-deps::\n"),
	Write("\npre-app::\n"),
	PatchHook = fun(Cmd) ->
		case Cmd of
			"make -C" ++ Cmd1 -> "$$$$\(MAKE) -C" ++ Escape(Cmd1);
			"gmake -C" ++ Cmd1 -> "$$$$\(MAKE) -C" ++ Escape(Cmd1);
			"make " ++ Cmd1 -> "$$$$\(MAKE) -f Makefile.orig.mk " ++ Escape(Cmd1);
			"gmake " ++ Cmd1 -> "$$$$\(MAKE) -f Makefile.orig.mk " ++ Escape(Cmd1);
			_ -> Escape(Cmd)
		end
	end,
	fun() ->
		case lists:keyfind(pre_hooks, 1, Conf) of
			false -> ok;
			{_, Hooks} ->
				[case H of
					{'get-deps', Cmd} ->
						Write("\npre-deps::\n\t" ++ PatchHook(Cmd) ++ "\n");
					{compile, Cmd} ->
						Write("\npre-app::\n\tCC=$$$$\(CC) " ++ PatchHook(Cmd) ++ "\n");
					{Regex, compile, Cmd} ->
						case rebar_utils:is_arch(Regex) of
							true -> Write("\npre-app::\n\tCC=$$$$\(CC) " ++ PatchHook(Cmd) ++ "\n");
							false -> ok
						end;
					_ -> ok
				end || H <- Hooks]
		end
	end(),
	ShellToMk = fun(V) ->
		re:replace(re:replace(V, "(\\\\$$$$)(\\\\w*)", "\\\\1(\\\\2)", [global]),
			"-Werror\\\\b", "", [{return, list}, global])
	end,
	PortSpecs = fun() ->
		case lists:keyfind(port_specs, 1, Conf) of
			false ->
				case filelib:is_dir("$(DEPS_DIR)/$(1)/c_src") of
					false -> [];
					true ->
						[{"priv/" ++ proplists:get_value(so_name, Conf, "$(1)_drv.so"),
							proplists:get_value(port_sources, Conf, ["c_src/*.c"]), []}]
				end;
			{_, Specs} ->
				lists:flatten([case S of
					{Output, Input} -> {ShellToMk(Output), Input, []};
					{Regex, Output, Input} ->
						case rebar_utils:is_arch(Regex) of
							true -> {ShellToMk(Output), Input, []};
							false -> []
						end;
					{Regex, Output, Input, [{env, Env}]} ->
						case rebar_utils:is_arch(Regex) of
							true -> {ShellToMk(Output), Input, Env};
							false -> []
						end
				end || S <- Specs])
		end
	end(),
	PortSpecWrite = fun (Text) ->
		file:write_file("$(DEPS_DIR)/$(1)/c_src/Makefile.erlang.mk", Text, [append])
	end,
	case PortSpecs of
		[] -> ok;
		_ ->
			Write("\npre-app::\n\t$$$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
			PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I ~s/erts-~s/include -I ~s\n",
				[code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
			PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L ~s -lerl_interface -lei\n",
				[code:lib_dir(erl_interface, lib)])),
			[PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
			FilterEnv = fun(Env) ->
				lists:flatten([case E of
					{_, _} -> E;
					{Regex, K, V} ->
						case rebar_utils:is_arch(Regex) of
							true -> {K, V};
							false -> []
						end
				end || E <- Env])
			end,
			MergeEnv = fun(Env) ->
				lists:foldl(fun ({K, V}, Acc) ->
					case lists:keyfind(K, 1, Acc) of
						false -> [{K, rebar_utils:expand_env_variable(V, K, "")}|Acc];
						{_, V0} -> [{K, rebar_utils:expand_env_variable(V, K, V0)}|Acc]
					end
				end, [], Env)
			end,
			PortEnv = case lists:keyfind(port_env, 1, Conf) of
				false -> [];
				{_, PortEnv0} -> FilterEnv(PortEnv0)
			end,
			PortSpec = fun ({Output, Input0, Env}) ->
				filelib:ensure_dir("$(DEPS_DIR)/$(1)/" ++ Output),
				Input = [[" ", I] || I <- Input0],
				PortSpecWrite([
					[["\n", K, " = ", ShellToMk(V)] || {K, V} <- lists:reverse(MergeEnv(PortEnv))],
					case $(PLATFORM) of
						darwin -> "\n\nLDFLAGS += -flat_namespace -undefined suppress";
						_ -> ""
					end,
					"\n\nall:: ", Output, "\n\n",
					"%.o: %.c\n\t$$$$\(CC) -c -o $$$$\@ $$$$\< $$$$\(CFLAGS) $$$$\(ERL_CFLAGS) $$$$\(DRV_CFLAGS) $$$$\(EXE_CFLAGS)\n\n",
					"%.o: %.C\n\t$$$$\(CXX) -c -o $$$$\@ $$$$\< $$$$\(CXXFLAGS) $$$$\(ERL_CFLAGS) $$$$\(DRV_CFLAGS) $$$$\(EXE_CFLAGS)\n\n",
					"%.o: %.cc\n\t$$$$\(CXX) -c -o $$$$\@ $$$$\< $$$$\(CXXFLAGS) $$$$\(ERL_CFLAGS) $$$$\(DRV_CFLAGS) $$$$\(EXE_CFLAGS)\n\n",
					"%.o: %.cpp\n\t$$$$\(CXX) -c -o $$$$\@ $$$$\< $$$$\(CXXFLAGS) $$$$\(ERL_CFLAGS) $$$$\(DRV_CFLAGS) $$$$\(EXE_CFLAGS)\n\n",
					[[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
					Output, ": $$$$\(foreach ext,.c .C .cc .cpp,",
						"$$$$\(patsubst %$$$$\(ext),%.o,$$$$\(filter %$$$$\(ext),$$$$\(wildcard", Input, "))))\n",
					"\t$$$$\(CC) -o $$$$\@ $$$$\? $$$$\(LDFLAGS) $$$$\(ERL_LDFLAGS) $$$$\(DRV_LDFLAGS) $$$$\(EXE_LDFLAGS)",
					case filename:extension(Output) of
						[] -> "\n";
						_ -> " -shared\n"
					end])
			end,
			[PortSpec(S) || S <- PortSpecs]
	end,
	Write("\ninclude $(ERLANG_MK_FILENAME)"),
	RunPlugin = fun(Plugin, Step) ->
		case erlang:function_exported(Plugin, Step, 2) of
			false -> ok;
			true ->
				c:cd("$(DEPS_DIR)/$(1)/"),
				Ret = Plugin:Step({config, "", Conf, dict:new(), dict:new(), dict:new(),
					dict:store(base_dir, "", dict:new())}, undefined),
				io:format("rebar plugin ~p step ~p ret ~p~n", [Plugin, Step, Ret])
		end
	end,
	fun() ->
		case lists:keyfind(plugins, 1, Conf) of
			false -> ok;
			{_, Plugins} ->
				[begin
					case lists:keyfind(deps, 1, Conf) of
						false -> ok;
						{_, Deps} ->
							case lists:keyfind(P, 1, Deps) of
								false -> ok;
								_ ->
									Path = "$(DEPS_DIR)/" ++ atom_to_list(P),
									io:format("~s", [os:cmd("$(MAKE) -C $(DEPS_DIR)/$(1) " ++ Path)]),
									io:format("~s", [os:cmd("$(MAKE) -C " ++ Path ++ " IS_DEP=1")]),
									code:add_patha(Path ++ "/ebin")
							end
					end
				end || P <- Plugins],
				[case code:load_file(P) of
					{module, P} -> ok;
					_ ->
						case lists:keyfind(plugin_dir, 1, Conf) of
							false -> ok;
							{_, PluginsDir} ->
								ErlFile = "$(DEPS_DIR)/$(1)/" ++ PluginsDir ++ "/" ++ atom_to_list(P) ++ ".erl",
								{ok, P, Bin} = compile:file(ErlFile, [binary]),
								{module, P} = code:load_binary(P, ErlFile, Bin)
						end
				end || P <- Plugins],
				[RunPlugin(P, preprocess) || P <- Plugins],
				[RunPlugin(P, pre_compile) || P <- Plugins]
		end
	end(),
	halt()
endef

define dep_autopatch_app.erl
	UpdateModules = fun(App) ->
		case filelib:is_regular(App) of
			false -> ok;
			true ->
				{ok, [{application, $(1), L0}]} = file:consult(App),
				Mods = filelib:fold_files("$(DEPS_DIR)/$(1)/src", "\\\\.erl$$$$", true,
					fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []),
				L = lists:keystore(modules, 1, L0, {modules, Mods}),
				ok = file:write_file(App, io_lib:format("~p.~n", [{application, $(1), L}]))
		end
	end,
	UpdateModules("$(DEPS_DIR)/$(1)/ebin/$(1).app"),
	halt()
endef

define dep_autopatch_appsrc.erl
	AppSrcOut = "$(DEPS_DIR)/$(1)/src/$(1).app.src",
	AppSrcIn = case filelib:is_regular(AppSrcOut) of false -> "$(DEPS_DIR)/$(1)/ebin/$(1).app"; true -> AppSrcOut end,
	case filelib:is_regular(AppSrcIn) of
		false -> ok;
		true ->
			{ok, [{application, $(1), L0}]} = file:consult(AppSrcIn),
			L1 = lists:keystore(modules, 1, L0, {modules, []}),
			L2 = case lists:keyfind(vsn, 1, L1) of {_, git} -> lists:keyreplace(vsn, 1, L1, {vsn, "git"}); _ -> L1 end,
			L3 = case lists:keyfind(registered, 1, L2) of false -> [{registered, []}|L2]; _ -> L2 end,
			ok = file:write_file(AppSrcOut, io_lib:format("~p.~n", [{application, $(1), L3}])),
			case AppSrcOut of AppSrcIn -> ok; _ -> ok = file:delete(AppSrcIn) end
	end,
	halt()
endef

define hex_fetch.erl
	ssl:start(),
	inets:start(),
	{ok, {{_, 200, _}, _, Body}} = httpc:request(get,
		{"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
		[], [{body_format, binary}]),
	{ok, Files} = erl_tar:extract({binary, Body}, [memory]),
	{_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
	ok = erl_tar:extract({binary, Source}, [{cwd, "$(DEPS_DIR)/$(1)"}, compressed]),
	halt()
endef

define dep_fetch
	if [ "$(2)" = "git" ]; then \
		git clone -q -n -- $(3) $(DEPS_DIR)/$(1); \
		cd $(DEPS_DIR)/$(1) && git checkout -q $(4); \
	elif [ "$(2)" = "hg" ]; then \
		hg clone -q -U $(3) $(DEPS_DIR)/$(1); \
		cd $(DEPS_DIR)/$(1) && hg update -q $(4); \
	elif [ "$(2)" = "svn" ]; then \
		svn checkout -q $(3) $(DEPS_DIR)/$(1); \
	elif [ "$(2)" = "cp" ]; then \
		cp -R $(3) $(DEPS_DIR)/$(1); \
	elif [ "$(2)" = "hex" ]; then \
		$(call erlang,$(call hex_fetch.erl,$(1),$(strip $(4)))); \
	else \
		echo "Unknown or invalid dependency: $(1). Please consult the erlang.mk README for instructions." >&2; \
		exit 78; \
	fi
endef

define dep_target
$(DEPS_DIR)/$(1):
	$(verbose) mkdir -p $(DEPS_DIR)
ifeq (,$(dep_$(1)))
	$(dep_verbose) $(call dep_fetch,$(pkg_$(1)_name),$(pkg_$(1)_fetch), \
		$(patsubst git://github.com/%,https://github.com/%,$(pkg_$(1)_repo)), \
		$(pkg_$(1)_commit))
else
ifeq (1,$(words $(dep_$(1))))
	$(dep_verbose) $(call dep_fetch,$(1),git, \
		$(patsubst git://github.com/%,https://github.com/%,$(dep_$(1))), \
		master)
else
ifeq (2,$(words $(dep_$(1))))
	$(dep_verbose) $(call dep_fetch,$(1),git, \
		$(patsubst git://github.com/%,https://github.com/%,$(word 1,$(dep_$(1)))), \
		$(word 2,$(dep_$(1))))
else
	$(dep_verbose) $(call dep_fetch,$(1),$(word 1,$(dep_$(1))), \
		$(patsubst git://github.com/%,https://github.com/%,$(word 2,$(dep_$(1)))), \
		$(word 3,$(dep_$(1))))
endif
endif
endif
	$(verbose) if [ -f $(DEPS_DIR)/$(1)/configure.ac -o -f $(DEPS_DIR)/$(1)/configure.in ]; then \
		echo " AUTO  " $(1); \
		cd $(DEPS_DIR)/$(1) && autoreconf -Wall -vif -I m4; \
	fi
	- $(verbose) if [ -f $(DEPS_DIR)/$(1)/configure ]; then \
		echo " CONF  " $(1); \
		cd $(DEPS_DIR)/$(1) && ./configure; \
	fi
ifeq ($(filter $(1),$(NO_AUTOPATCH)),)
	$(verbose) if [ "$(1)" = "amqp_client" -a "$(RABBITMQ_CLIENT_PATCH)" ]; then \
		if [ ! -d $(DEPS_DIR)/rabbitmq-codegen ]; then \
			echo " PATCH  Downloading rabbitmq-codegen"; \
			git clone https://github.com/rabbitmq/rabbitmq-codegen.git $(DEPS_DIR)/rabbitmq-codegen; \
		fi; \
		if [ ! -d $(DEPS_DIR)/rabbitmq-server ]; then \
			echo " PATCH  Downloading rabbitmq-server"; \
			git clone https://github.com/rabbitmq/rabbitmq-server.git $(DEPS_DIR)/rabbitmq-server; \
		fi; \
		ln -s $(DEPS_DIR)/amqp_client/deps/rabbit_common-0.0.0 $(DEPS_DIR)/rabbit_common; \
	elif [ "$(1)" = "rabbit" -a "$(RABBITMQ_SERVER_PATCH)" ]; then \
		if [ ! -d $(DEPS_DIR)/rabbitmq-codegen ]; then \
			echo " PATCH  Downloading rabbitmq-codegen"; \
			git clone https://github.com/rabbitmq/rabbitmq-codegen.git $(DEPS_DIR)/rabbitmq-codegen; \
		fi \
	else \
		$(call dep_autopatch,$(1)) \
	fi
endif
endef

$(foreach dep,$(DEPS),$(eval $(call dep_target,$(dep))))

distclean-deps:
	$(gen_verbose) rm -rf $(DEPS_DIR)

# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

# Verbosity.

proto_verbose_0 = @echo " PROTO " $(filter %.proto,$(?F));
proto_verbose = $(proto_verbose_$(V))

# Core targets.

define compile_proto
	$(verbose) mkdir -p ebin/ include/
	$(proto_verbose) $(call erlang,$(call compile_proto.erl,$(1)))
	$(proto_verbose) erlc +debug_info -o ebin/ ebin/*.erl
	$(verbose) rm ebin/*.erl
endef

define compile_proto.erl
	[begin
		Dir = filename:dirname(filename:dirname(F)),
		protobuffs_compile:generate_source(F,
			[{output_include_dir, Dir ++ "/include"},
				{output_src_dir, Dir ++ "/ebin"}])
	end || F <- string:tokens("$(1)", " ")],
	halt().
endef

ifneq ($(wildcard src/),)
ebin/$(PROJECT).app:: $(sort $(call core_find,src/,*.proto))
	$(if $(strip $?),$(call compile_proto,$?))
endif

# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: clean-app

# Configuration.

ERLC_OPTS ?= -Werror +debug_info +warn_export_vars +warn_shadow_vars \
	+warn_obsolete_guard # +bin_opt_info +warn_export_all +warn_missing_spec
COMPILE_FIRST ?=
COMPILE_FIRST_PATHS = $(addprefix src/,$(addsuffix .erl,$(COMPILE_FIRST)))
ERLC_EXCLUDE ?=
ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE)))

ERLC_MIB_OPTS ?=
COMPILE_MIB_FIRST ?=
COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST)))

# Verbosity.

app_verbose_0 = @echo " APP   " $(PROJECT);
app_verbose = $(app_verbose_$(V))

appsrc_verbose_0 = @echo " APP   " $(PROJECT).app.src;
appsrc_verbose = $(appsrc_verbose_$(V))

erlc_verbose_0 = @echo " ERLC  " $(filter-out $(patsubst %,%.erl,$(ERLC_EXCLUDE)),\
	$(filter %.erl %.core,$(?F)));
erlc_verbose = $(erlc_verbose_$(V))

xyrl_verbose_0 = @echo " XYRL  " $(filter %.xrl %.yrl,$(?F));
xyrl_verbose = $(xyrl_verbose_$(V))

asn1_verbose_0 = @echo " ASN1  " $(filter %.asn1,$(?F));
asn1_verbose = $(asn1_verbose_$(V))

mib_verbose_0 = @echo " MIB   " $(filter %.bin %.mib,$(?F));
mib_verbose = $(mib_verbose_$(V))

# Targets.

ifeq ($(wildcard ebin/test),)
app:: app-build
else
app:: clean app-build
endif

ifeq ($(wildcard src/$(PROJECT)_app.erl),)
define app_file
{application, $(PROJECT), [
	{description, "$(PROJECT_DESCRIPTION)"},
	{vsn, "$(PROJECT_VERSION)"},
	{id, "$(1)"},
	{modules, [$(call comma_list,$(2))]},
	{registered, []},
	{applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(DEPS))]}
]}.
endef
else
define app_file
{application, $(PROJECT), [
	{description, "$(PROJECT_DESCRIPTION)"},
	{vsn, "$(PROJECT_VERSION)"},
	{id, "$(1)"},
	{modules, [$(call comma_list,$(2))]},
	{registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
	{applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(DEPS))]},
	{mod, {$(PROJECT)_app, []}}
]}.
endef
endif

app-build: erlc-include ebin/$(PROJECT).app
	$(eval GITDESCRIBE := $(shell git describe --dirty --abbrev=7 --tags --always --first-parent 2>/dev/null || true))
	$(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename $(wildcard ebin/*.beam))))))
ifeq ($(wildcard src/$(PROJECT).app.src),)
	$(app_verbose) echo $(subst $(newline),,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES)))) \
		> ebin/$(PROJECT).app
else
	$(verbose) if [ -z "$$(grep -E '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
		echo "Empty modules entry not found in $(PROJECT).app.src. Please consult the erlang.mk README for instructions." >&2; \
		exit 1; \
	fi
	$(appsrc_verbose) cat src/$(PROJECT).app.src \
		| sed "s/{[[:space:]]*modules[[:space:]]*,[[:space:]]*\[\]}/{modules, \[$(call comma_list,$(MODULES))\]}/" \
		| sed "s/{id,[[:space:]]*\"git\"}/{id, \"$(GITDESCRIBE)\"}/" \
		> ebin/$(PROJECT).app
endif

erlc-include:
	- $(verbose) if [ -d ebin/ ]; then \
		find include/ src/ -type f -name \*.hrl -newer ebin -exec touch $(shell find src/ -type f -name "*.erl") \; 2>/dev/null || printf ''; \
	fi

define compile_erl
	$(erlc_verbose) erlc -v $(if $(IS_DEP),$(filter-out -Werror,$(ERLC_OPTS)),$(ERLC_OPTS)) -o ebin/ \
		-pa ebin/ -I include/ $(filter-out $(ERLC_EXCLUDE_PATHS),\
		$(COMPILE_FIRST_PATHS) $(1))
endef

define compile_xyrl
	$(xyrl_verbose) erlc -v -o ebin/ $(1)
	$(xyrl_verbose) erlc $(ERLC_OPTS) -o ebin/ ebin/*.erl
	$(verbose) rm ebin/*.erl
endef

define compile_asn1
	$(asn1_verbose) erlc -v -I include/ -o ebin/ $(1)
	$(verbose) mv ebin/*.hrl include/
	$(verbose) mv ebin/*.asn1db include/
	$(verbose) rm ebin/*.erl
endef

define compile_mib
	$(mib_verbose) erlc -v $(ERLC_MIB_OPTS) -o priv/mibs/ \
		-I priv/mibs/ $(COMPILE_MIB_FIRST_PATHS) $(1)
	$(mib_verbose) erlc -o include/ -- priv/mibs/*.bin
endef

ifneq ($(wildcard src/),)
ebin/$(PROJECT).app::
	$(verbose) mkdir -p ebin/

ifneq ($(wildcard asn1/),)
ebin/$(PROJECT).app:: $(sort $(call core_find,asn1/,*.asn1))
	$(verbose) mkdir -p include
	$(if $(strip $?),$(call compile_asn1,$?))
endif

ifneq ($(wildcard mibs/),)
ebin/$(PROJECT).app:: $(sort $(call core_find,mibs/,*.mib))
	$(verbose) mkdir -p priv/mibs/ include
	$(if $(strip $?),$(call compile_mib,$?))
endif

ebin/$(PROJECT).app:: $(sort $(call core_find,src/,*.erl *.core))
	$(if $(strip $?),$(call compile_erl,$?))

ebin/$(PROJECT).app:: $(sort $(call core_find,src/,*.xrl *.yrl))
	$(if $(strip $?),$(call compile_xyrl,$?))
endif

clean:: clean-app

clean-app:
	$(gen_verbose) rm -rf ebin/ priv/mibs/ \
		$(addprefix include/,$(addsuffix .hrl,$(notdir $(basename $(call core_find,mibs/,*.mib)))))

# Copyright (c) 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: docs-deps

# Configuration.

ALL_DOC_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(DOC_DEPS))

# Targets.

$(foreach dep,$(DOC_DEPS),$(eval $(call dep_target,$(dep))))

ifneq ($(SKIP_DEPS),)
doc-deps:
else
doc-deps: $(ALL_DOC_DEPS_DIRS)
	$(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
endif

# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: test-deps test-dir test-build clean-test-dir

# Configuration.

TEST_DIR ?= $(CURDIR)/test

ALL_TEST_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(TEST_DEPS))

TEST_ERLC_OPTS ?= +debug_info +warn_export_vars +warn_shadow_vars +warn_obsolete_guard
TEST_ERLC_OPTS += -DTEST=1

# Targets.

$(foreach dep,$(TEST_DEPS),$(eval $(call dep_target,$(dep))))

ifneq ($(SKIP_DEPS),)
test-deps:
else
test-deps: $(ALL_TEST_DEPS_DIRS)
	$(verbose) for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep IS_DEP=1; done
endif

ifneq ($(wildcard $(TEST_DIR)),)
test-dir:
	$(gen_verbose) erlc -v $(TEST_ERLC_OPTS) -I include/ -o $(TEST_DIR) \
		$(call core_find,$(TEST_DIR)/,*.erl) -pa ebin/
endif

ifeq ($(wildcard ebin/test),)
test-build:: ERLC_OPTS=$(TEST_ERLC_OPTS)
test-build:: clean deps test-deps
	$(verbose) $(MAKE) --no-print-directory app-build test-dir ERLC_OPTS="$(TEST_ERLC_OPTS)"
	$(gen_verbose) touch ebin/test
else
test-build:: ERLC_OPTS=$(TEST_ERLC_OPTS)
test-build:: deps test-deps
	$(verbose) $(MAKE) --no-print-directory app-build test-dir ERLC_OPTS="$(TEST_ERLC_OPTS)"
endif

clean:: clean-test-dir

clean-test-dir:
ifneq ($(wildcard $(TEST_DIR)/*.beam),)
	$(gen_verbose) rm -f $(TEST_DIR)/*.beam
endif

# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc

MAN_INSTALL_PATH ?= /usr/local/share/man
MAN_SECTIONS ?= 3 7

docs:: asciidoc

asciidoc: distclean-asciidoc doc-deps asciidoc-guide asciidoc-manual

ifeq ($(wildcard doc/src/guide/book.asciidoc),)
asciidoc-guide:
else
asciidoc-guide:
	a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
	a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
endif

ifeq ($(wildcard doc/src/manual/*.asciidoc),)
asciidoc-manual:
else
asciidoc-manual:
	for f in doc/src/manual/*.asciidoc ; do \
		a2x -v -f manpage $$f ; \
	done
	for s in $(MAN_SECTIONS); do \
		mkdir -p doc/man$$s/ ; \
		mv doc/src/manual/*.$$s doc/man$$s/ ; \
		gzip doc/man$$s/*.$$s ; \
	done

install-docs:: install-asciidoc

install-asciidoc: asciidoc-manual
	for s in $(MAN_SECTIONS); do \
		mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
		install -g 0 -o 0 -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
	done
endif

distclean:: distclean-asciidoc

distclean-asciidoc:
	$(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/

# Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates

# Core targets.

help::
	$(verbose) printf "%s\n" "" \
		"Bootstrap targets:" \
		"  bootstrap          Generate a skeleton of an OTP application" \
		"  bootstrap-lib      Generate a skeleton of an OTP library" \
		"  bootstrap-rel      Generate the files needed to build a release" \
		"  new t=TPL n=NAME   Generate a module NAME based on the template TPL" \
		"  list-templates     List available templates"

# Bootstrap templates.

define bs_appsrc
{application, $(PROJECT), [
	{description, ""},
	{vsn, "0.1.0"},
	{id, "git"},
	{modules, []},
	{registered, []},
	{applications, [
		kernel,
		stdlib
	]},
	{mod, {$(PROJECT)_app, []}},
	{env, []}
]}.
endef

define bs_appsrc_lib
{application, $(PROJECT), [
	{description, ""},
	{vsn, "0.1.0"},
	{id, "git"},
	{modules, []},
	{registered, []},
	{applications, [
		kernel,
		stdlib
	]}
]}.
endef

ifdef SP
define bs_Makefile
PROJECT = $(PROJECT)

# Whitespace to be used when creating files from templates.
SP = $(SP)

include erlang.mk
endef
else
define bs_Makefile
PROJECT = $(PROJECT)
include erlang.mk
endef
endif

define bs_app
-module($(PROJECT)_app).
-behaviour(application).

-export([start/2]).
-export([stop/1]).

start(_Type, _Args) ->
	$(PROJECT)_sup:start_link().

stop(_State) ->
	ok.
endef

define bs_relx_config
{release, {$(PROJECT)_release, "1"}, [$(PROJECT)]}.
{extended_start_script, true}.
{sys_config, "rel/sys.config"}.
{vm_args, "rel/vm.args"}.
endef

define bs_sys_config
[
].
endef

define bs_vm_args
-name $(PROJECT)@127.0.0.1
-setcookie $(PROJECT)
-heart
endef

# Normal templates.

define tpl_supervisor
-module($(n)).
-behaviour(supervisor).

-export([start_link/0]).
-export([init/1]).

start_link() ->
	supervisor:start_link({local, ?MODULE}, ?MODULE, []).

init([]) ->
	Procs = [],
	{ok, {{one_for_one, 1, 5}, Procs}}.
endef

define tpl_gen_server
-module($(n)).
-behaviour(gen_server).

%% API.
-export([start_link/0]).

%% gen_server.
-export([init/1]).
-export([handle_call/3]).
-export([handle_cast/2]).
-export([handle_info/2]).
-export([terminate/2]).
-export([code_change/3]).

-record(state, {
}).

%% API.

-spec start_link() -> {ok, pid()}.
start_link() ->
	gen_server:start_link(?MODULE, [], []).

%% gen_server.

init([]) ->
	{ok, #state{}}.

handle_call(_Request, _From, State) ->
	{reply, ignored, State}.

handle_cast(_Msg, State) ->
	{noreply, State}.

handle_info(_Info, State) ->
	{noreply, State}.

terminate(_Reason, _State) ->
	ok.

code_change(_OldVsn, State, _Extra) ->
	{ok, State}.
endef

define tpl_cowboy_http
-module($(n)).
-behaviour(cowboy_http_handler).

-export([init/3]).
-export([handle/2]).
-export([terminate/3]).

-record(state, {
}).

init(_, Req, _Opts) ->
	{ok, Req, #state{}}.

handle(Req, State=#state{}) ->
	{ok, Req2} = cowboy_req:reply(200, Req),
	{ok, Req2, State}.

terminate(_Reason, _Req, _State) ->
	ok.
endef

define tpl_gen_fsm
-module($(n)).
-behaviour(gen_fsm).

%% API.
-export([start_link/0]).

%% gen_fsm.
-export([init/1]).
-export([state_name/2]).
-export([handle_event/3]).
-export([state_name/3]).
-export([handle_sync_event/4]).
-export([handle_info/3]).
-export([terminate/3]).
-export([code_change/4]).

-record(state, {
}).

%% API.

-spec start_link() -> {ok, pid()}.
start_link() ->
	gen_fsm:start_link(?MODULE, [], []).

%% gen_fsm.

init([]) ->
	{ok, state_name, #state{}}.

state_name(_Event, StateData) ->
	{next_state, state_name, StateData}.

handle_event(_Event, StateName, StateData) ->
	{next_state, StateName, StateData}.

state_name(_Event, _From, StateData) ->
	{reply, ignored, state_name, StateData}.

handle_sync_event(_Event, _From, StateName, StateData) ->
	{reply, ignored, StateName, StateData}.

handle_info(_Info, StateName, StateData) ->
	{next_state, StateName, StateData}.

terminate(_Reason, _StateName, _StateData) ->
	ok.

code_change(_OldVsn, StateName, StateData, _Extra) ->
	{ok, StateName, StateData}.
endef

define tpl_cowboy_loop
-module($(n)).
-behaviour(cowboy_loop_handler).

-export([init/3]).
-export([info/3]).
-export([terminate/3]).

-record(state, {
}).

init(_, Req, _Opts) ->
	{loop, Req, #state{}, 5000, hibernate}.

info(_Info, Req, State) ->
	{loop, Req, State, hibernate}.

terminate(_Reason, _Req, _State) ->
	ok.
endef

define tpl_cowboy_rest
-module($(n)).

-export([init/3]).
-export([content_types_provided/2]).
-export([get_html/2]).

init(_, _Req, _Opts) ->
	{upgrade, protocol, cowboy_rest}.

content_types_provided(Req, State) ->
	{[{{<<"text">>, <<"html">>, '*'}, get_html}], Req, State}.

get_html(Req, State) ->
	{<<"<html><body>This is REST!</body></html>">>, Req, State}.
endef

define tpl_cowboy_ws
-module($(n)).
-behaviour(cowboy_websocket_handler).

-export([init/3]).
-export([websocket_init/3]).
-export([websocket_handle/3]).
-export([websocket_info/3]).
-export([websocket_terminate/3]).

-record(state, {
}).

init(_, _, _) ->
	{upgrade, protocol, cowboy_websocket}.

websocket_init(_, Req, _Opts) ->
	Req2 = cowboy_req:compact(Req),
	{ok, Req2, #state{}}.

websocket_handle({text, Data}, Req, State) ->
	{reply, {text, Data}, Req, State};
websocket_handle({binary, Data}, Req, State) ->
	{reply, {binary, Data}, Req, State};
websocket_handle(_Frame, Req, State) ->
	{ok, Req, State}.

websocket_info(_Info, Req, State) ->
	{ok, Req, State}.

websocket_terminate(_Reason, _Req, _State) ->
	ok.
endef

define tpl_ranch_protocol
-module($(n)).
-behaviour(ranch_protocol).

-export([start_link/4]).
-export([init/4]).

-type opts() :: [].
-export_type([opts/0]).

-record(state, {
	socket :: inet:socket(),
	transport :: module()
}).

start_link(Ref, Socket, Transport, Opts) ->
	Pid = spawn_link(?MODULE, init, [Ref, Socket, Transport, Opts]),
	{ok, Pid}.

-spec init(ranch:ref(), inet:socket(), module(), opts()) -> ok.
init(Ref, Socket, Transport, _Opts) ->
	ok = ranch:accept_ack(Ref),
	loop(#state{socket=Socket, transport=Transport}).

loop(State) ->
	loop(State).
endef

# Plugin-specific targets.

define render_template
	$(verbose) echo "$${_$(1)}" > $(2)
endef

ifndef WS
ifdef SP
WS = $(subst a,,a $(wordlist 1,$(SP),a a a a a a a a a a a a a a a a a a a a))
else
WS = $(tab)
endif
endif

$(foreach template,$(filter bs_% tpl_%,$(.VARIABLES)), \
	$(eval _$(template) = $$(subst $$(tab),$$(WS),$$($(template)))) \
	$(eval export _$(template)))

bootstrap:
ifneq ($(wildcard src/),)
	$(error Error: src/ directory already exists)
endif
	$(call render_template,bs_Makefile,Makefile)
	$(verbose) mkdir src/
	$(call render_template,bs_appsrc,src/$(PROJECT).app.src)
	$(call render_template,bs_app,src/$(PROJECT)_app.erl)
	$(eval n := $(PROJECT)_sup)
	$(call render_template,tpl_supervisor,src/$(PROJECT)_sup.erl)

bootstrap-lib:
ifneq ($(wildcard src/),)
	$(error Error: src/ directory already exists)
endif
	$(call render_template,bs_Makefile,Makefile)
	$(verbose) mkdir src/
	$(call render_template,bs_appsrc_lib,src/$(PROJECT).app.src)

bootstrap-rel:
ifneq ($(wildcard relx.config),)
	$(error Error: relx.config already exists)
endif
ifneq ($(wildcard rel/),)
	$(error Error: rel/ directory already exists)
endif
	$(call render_template,bs_relx_config,relx.config)
	$(verbose) mkdir rel/
	$(call render_template,bs_sys_config,rel/sys.config)
	$(call render_template,bs_vm_args,rel/vm.args)

new:
ifeq ($(wildcard src/),)
	$(error Error: src/ directory does not exist)
endif
ifndef t
	$(error Usage: $(MAKE) new t=TEMPLATE n=NAME)
endif
ifndef tpl_$(t)
	$(error Unknown template)
endif
ifndef n
	$(error Usage: $(MAKE) new t=TEMPLATE n=NAME)
endif
	$(call render_template,tpl_$(t),src/$(n).erl)

list-templates:
	$(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))

# Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: clean-c_src distclean-c_src-env

# Configuration.

C_SRC_DIR ?= $(CURDIR)/c_src
C_SRC_ENV ?= $(C_SRC_DIR)/env.mk
C_SRC_OUTPUT ?= $(CURDIR)/priv/$(PROJECT).so
C_SRC_TYPE ?= shared

# System type and C compiler/flags.

ifeq ($(PLATFORM),darwin)
	CC ?= cc
	CFLAGS ?= -O3 -std=c99 -arch x86_64 -finline-functions -Wall -Wmissing-prototypes
	CXXFLAGS ?= -O3 -arch x86_64 -finline-functions -Wall
	LDFLAGS ?= -arch x86_64 -flat_namespace -undefined suppress
else ifeq ($(PLATFORM),freebsd)
	CC ?= cc
	CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes
	CXXFLAGS ?= -O3 -finline-functions -Wall
else ifeq ($(PLATFORM),linux)
	CC ?= gcc
	CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes
	CXXFLAGS ?= -O3 -finline-functions -Wall
endif

CFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR)
CXXFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR)

LDLIBS += -L $(ERL_INTERFACE_LIB_DIR) -lerl_interface -lei

ifeq ($(C_SRC_TYPE),shared)
LDFLAGS += -shared
endif

# Verbosity.

c_verbose_0 = @echo " C     " $(?F);
c_verbose = $(c_verbose_$(V))

cpp_verbose_0 = @echo " CPP   " $(?F);
cpp_verbose = $(cpp_verbose_$(V))

link_verbose_0 = @echo " LD    " $(@F);
link_verbose = $(link_verbose_$(V))

# Targets.

ifeq ($(wildcard $(C_SRC_DIR)),)
else ifneq ($(wildcard $(C_SRC_DIR)/Makefile),)
app:: app-c_src

test-build:: app-c_src

app-c_src:
	$(MAKE) -C $(C_SRC_DIR)

clean::
	$(MAKE) -C $(C_SRC_DIR) clean

else

ifeq ($(SOURCES),)
SOURCES := $(sort $(call core_find,$(C_SRC_DIR)/,*.c *.C *.cc *.cpp))
endif
OBJECTS = $(addsuffix .o, $(basename $(SOURCES)))

COMPILE_C = $(c_verbose) $(CC) $(CFLAGS) $(CPPFLAGS) -c
COMPILE_CPP = $(cpp_verbose) $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c

app:: $(C_SRC_ENV) $(C_SRC_OUTPUT)

test-build:: $(C_SRC_ENV) $(C_SRC_OUTPUT)

$(C_SRC_OUTPUT): $(OBJECTS)
	$(verbose) mkdir -p priv/
	$(link_verbose) $(CC) $(OBJECTS) $(LDFLAGS) $(LDLIBS) -o $(C_SRC_OUTPUT)

%.o: %.c
	$(COMPILE_C) $(OUTPUT_OPTION) $<

%.o: %.cc
	$(COMPILE_CPP) $(OUTPUT_OPTION) $<

%.o: %.C
	$(COMPILE_CPP) $(OUTPUT_OPTION) $<

%.o: %.cpp
	$(COMPILE_CPP) $(OUTPUT_OPTION) $<

clean:: clean-c_src

clean-c_src:
	$(gen_verbose) rm -f $(C_SRC_OUTPUT) $(OBJECTS)

endif

ifneq ($(wildcard $(C_SRC_DIR)),)
$(C_SRC_ENV):
	$(verbose) $(ERL) -eval "file:write_file(\"$(C_SRC_ENV)\", \
		io_lib:format( \
			\"ERTS_INCLUDE_DIR ?= ~s/erts-~s/include/~n\" \
			\"ERL_INTERFACE_INCLUDE_DIR ?= ~s~n\" \
			\"ERL_INTERFACE_LIB_DIR ?= ~s~n\", \
			[code:root_dir(), erlang:system_info(version), \
			code:lib_dir(erl_interface, include), \
			code:lib_dir(erl_interface, lib)])), \
		halt()."

distclean:: distclean-c_src-env

distclean-c_src-env:
	$(gen_verbose) rm -f $(C_SRC_ENV)

-include $(C_SRC_ENV)
endif

# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: ci ci-setup distclean-kerl

KERL ?= $(CURDIR)/kerl
export KERL

KERL_URL ?= https://raw.githubusercontent.com/yrashk/kerl/master/kerl

OTP_GIT ?= https://github.com/erlang/otp

CI_INSTALL_DIR ?= $(HOME)/erlang
CI_OTP ?=

ifeq ($(strip $(CI_OTP)),)
ci::
else
ci:: $(addprefix ci-,$(CI_OTP))

ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))

ci-setup::

ci_verbose_0 = @echo " CI    " $(1);
ci_verbose = $(ci_verbose_$(V))

define ci_target
ci-$(1): $(CI_INSTALL_DIR)/$(1)
	$(ci_verbose) \
		PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
		CI_OTP_RELEASE="$(1)" \
		CT_OPTS="-label $(1)" \
		$(MAKE) clean ci-setup tests
endef

$(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))

define ci_otp_target
ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
$(CI_INSTALL_DIR)/$(1): $(KERL)
	$(KERL) build git $(OTP_GIT) $(1) $(1)
	$(KERL) install $(1) $(CI_INSTALL_DIR)/$(1)
endif
endef

$(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))

$(KERL):
	$(gen_verbose) $(call core_http_get,$(KERL),$(KERL_URL))
	$(verbose) chmod +x $(KERL)

help::
	$(verbose) printf "%s\n" "" \
		"Continuous Integration targets:" \
		"  ci          Run '$(MAKE) tests' on all configured Erlang versions." \
		"" \
		"The CI_OTP variable must be defined with the Erlang versions" \
		"that must be tested. For example: CI_OTP = OTP-17.3.4 OTP-17.5.3"

distclean:: distclean-kerl

distclean-kerl:
	$(gen_verbose) rm -rf $(KERL)
endif

# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: ct distclean-ct

# Configuration.

CT_OPTS ?=
ifneq ($(wildcard $(TEST_DIR)),)
	CT_SUITES ?= $(sort $(subst _SUITE.erl,,$(notdir $(call core_find,$(TEST_DIR)/,*_SUITE.erl))))
else
	CT_SUITES ?=
endif

# Core targets.

tests:: ct

distclean:: distclean-ct

help::
	$(verbose) printf "%s\n" "" \
		"Common_test targets:" \
		"  ct          Run all the common_test suites for this project" \
		"" \
		"All your common_test suites have their associated targets." \
		"A suite named http_SUITE can be ran using the ct-http target."

# Plugin-specific targets.

CT_RUN = ct_run \
	-no_auto_compile \
	-noinput \
	-pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(TEST_DIR) \
	-dir $(TEST_DIR) \
	-logdir $(CURDIR)/logs

ifeq ($(CT_SUITES),)
ct:
else
ct: test-build
	$(verbose) mkdir -p $(CURDIR)/logs/
	$(gen_verbose) $(CT_RUN) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS)
endif

define ct_suite_target
ct-$(1): test-build
	$(verbose) mkdir -p $(CURDIR)/logs/
	$(gen_verbose) $(CT_RUN) -suite $(addsuffix _SUITE,$(1)) $(CT_OPTS)
endef

$(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))

distclean-ct:
	$(gen_verbose) rm -rf $(CURDIR)/logs/

# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: plt distclean-plt dialyze

# Configuration.

DIALYZER_PLT ?= $(CURDIR)/.$(PROJECT).plt
export DIALYZER_PLT

PLT_APPS ?=
DIALYZER_DIRS ?= --src -r src
DIALYZER_OPTS ?= -Werror_handling -Wrace_conditions \
	-Wunmatched_returns # -Wunderspecs

# Core targets.

check:: dialyze

distclean:: distclean-plt

help::
	$(verbose) printf "%s\n" "" \
		"Dialyzer targets:" \
		"  plt         Build a PLT file for this project" \
		"  dialyze     Analyze the project using Dialyzer"

# Plugin-specific targets.

$(DIALYZER_PLT): deps app
	$(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(ALL_DEPS_DIRS)

plt: $(DIALYZER_PLT)

distclean-plt:
	$(gen_verbose) rm -f $(DIALYZER_PLT)

ifneq ($(wildcard $(DIALYZER_PLT)),)
dialyze:
else
dialyze: $(DIALYZER_PLT)
endif
	$(verbose) dialyzer --no_native $(DIALYZER_DIRS) $(DIALYZER_OPTS)

# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: distclean-edoc edoc

# Configuration.

EDOC_OPTS ?=

# Core targets.

docs:: distclean-edoc edoc

distclean:: distclean-edoc

# Plugin-specific targets.

edoc: doc-deps
	$(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().'

distclean-edoc:
	$(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info

# Copyright (c) 2015, Erlang Solutions Ltd.
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: elvis distclean-elvis

# Configuration.

ELVIS_CONFIG ?= $(CURDIR)/elvis.config

ELVIS ?= $(CURDIR)/elvis
export ELVIS

ELVIS_URL ?= https://github.com/inaka/elvis/releases/download/0.2.5-beta2/elvis
ELVIS_CONFIG_URL ?= https://github.com/inaka/elvis/releases/download/0.2.5-beta2/elvis.config
ELVIS_OPTS ?=

# Core targets.

help::
	$(verbose) printf "%s\n" "" \
		"Elvis targets:" \
		"  elvis       Run Elvis using the local elvis.config or download the default otherwise"

distclean:: distclean-elvis

# Plugin-specific targets.

$(ELVIS):
	$(gen_verbose) $(call core_http_get,$(ELVIS),$(ELVIS_URL))
	$(verbose) chmod +x $(ELVIS)

$(ELVIS_CONFIG):
	$(verbose) $(call core_http_get,$(ELVIS_CONFIG),$(ELVIS_CONFIG_URL))

elvis: $(ELVIS) $(ELVIS_CONFIG)
	$(verbose) $(ELVIS) rock -c $(ELVIS_CONFIG) $(ELVIS_OPTS)

distclean-elvis:
	$(gen_verbose) rm -rf $(ELVIS)

# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

# Configuration.

DTL_FULL_PATH ?= 0

# Verbosity.

dtl_verbose_0 = @echo " DTL   " $(filter %.dtl,$(?F));
dtl_verbose = $(dtl_verbose_$(V))

# Core targets.

define compile_erlydtl
	$(dtl_verbose) $(ERL) -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/ -eval ' \
		Compile = fun(F) -> \
			S = fun (1) -> re:replace(filename:rootname(string:sub_string(F, 11), ".dtl"), "/",  "_",  [{return, list}, global]); \
				(0) -> filename:basename(F, ".dtl") \
			end, \
			Module = list_to_atom(string:to_lower(S($(DTL_FULL_PATH))) ++ "_dtl"), \
			{ok, _} = erlydtl:compile(F, Module, [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) \
		end, \
		_ = [Compile(F) || F <- string:tokens("$(1)", " ")], \
		halt().'
endef

ifneq ($(wildcard src/),)
ebin/$(PROJECT).app:: $(sort $(call core_find,templates/,*.dtl))
	$(if $(strip $?),$(call compile_erlydtl,$?))
endif

# Copyright (c) 2014 Dave Cottlehuber <dch@skunkwerks.at>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: distclean-escript escript

# Configuration.

ESCRIPT_NAME ?= $(PROJECT)
ESCRIPT_COMMENT ?= This is an -*- erlang -*- file

ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
ESCRIPT_EMU_ARGS ?= -pa . \
	-sasl errlog_type error \
	-escript main $(ESCRIPT_NAME)
ESCRIPT_SHEBANG ?= /usr/bin/env escript
ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"

# Core targets.

distclean:: distclean-escript

help::
	$(verbose) printf "%s\n" "" \
		"Escript targets:" \
		"  escript     Build an executable escript archive" \

# Plugin-specific targets.

# Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
# Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
# Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
# Software may only be used for the great good and the true happiness of all
# sentient beings.

define ESCRIPT_RAW
'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
'  [F || F <- A, not filelib:is_dir(F) ] end,'\
'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
'Ez = fun(Escript) ->'\
'  Static = Files([$(ESCRIPT_STATIC)]),'\
'  Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
'  Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
'  escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
'    {archive, Archive, [memory]},'\
'    {shebang, "$(ESCRIPT_SHEBANG)"},'\
'    {comment, "$(ESCRIPT_COMMENT)"},'\
'    {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
'  ]),'\
'  file:change_mode(Escript, 8#755)'\
'end,'\
'Ez("$(ESCRIPT_NAME)"),'\
'halt().'
endef

ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))

escript:: distclean-escript deps app
	$(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)

distclean-escript:
	$(gen_verbose) rm -f $(ESCRIPT_NAME)

# Copyright (c) 2014, Enrique Fernandez <enrique.fernandez@erlang-solutions.com>
# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
# This file is contributed to erlang.mk and subject to the terms of the ISC License.

.PHONY: eunit

# Configuration

EUNIT_OPTS ?=

# Core targets.

tests:: eunit

help::
	$(verbose) printf "%s\n" "" \
		"EUnit targets:" \
		"  eunit       Run all the EUnit tests for this project"

# Plugin-specific targets.

define eunit.erl
	case "$(COVER)" of
		"" -> ok;
		_ ->
			case cover:compile_beam_directory("ebin") of
				{error, _} -> halt(1);
				_ -> ok
			end
	end,
	case eunit:test([$(call comma_list,$(1))], [$(EUNIT_OPTS)]) of
		ok -> ok;
		error -> halt(2)
	end,
	case "$(COVER)" of
		"" -> ok;
		_ ->
			cover:export("eunit.coverdata")
	end,
	halt()
endef

EUNIT_EBIN_MODS = $(notdir $(basename $(call core_find,ebin/,*.beam)))
EUNIT_TEST_MODS = $(notdir $(basename $(call core_find,$(TEST_DIR)/,*.beam)))
EUNIT_MODS = $(foreach mod,$(EUNIT_EBIN_MODS) $(filter-out \
	$(patsubst %,%_tests,$(EUNIT_EBIN_MODS)),$(EUNIT_TEST_MODS)),{module,'$(mod)'})

eunit: test-build
	$(gen_verbose) $(ERL) -pa $(TEST_DIR) $(DEPS_DIR)/*/ebin ebin \
		-eval "$(subst $(newline),,$(subst ",\",$(call eunit.erl,$(EUNIT_MODS))))"

# Copyright (c) 2013-2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: relx-rel distclean-relx-rel distclean-relx run

# Configuration.

RELX_CONFIG ?= $(CURDIR)/relx.config

RELX ?= $(CURDIR)/relx
export RELX

RELX_URL ?= https://github.com/erlware/relx/releases/download/v2.0.0/relx
RELX_OPTS ?=
RELX_OUTPUT_DIR ?= _rel

ifeq ($(firstword $(RELX_OPTS)),-o)
	RELX_OUTPUT_DIR = $(word 2,$(RELX_OPTS))
else
	RELX_OPTS += -o $(RELX_OUTPUT_DIR)
endif

# Core targets.

ifeq ($(IS_DEP),)
ifneq ($(wildcard $(RELX_CONFIG)),)
rel:: distclean-relx-rel relx-rel
endif
endif

distclean:: distclean-relx-rel distclean-relx

# Plugin-specific targets.

$(RELX):
	$(gen_verbose) $(call core_http_get,$(RELX),$(RELX_URL))
	$(verbose) chmod +x $(RELX)

relx-rel: $(RELX)
	$(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)

distclean-relx-rel:
	$(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)

distclean-relx:
	$(gen_verbose) rm -rf $(RELX)

# Run target.

ifeq ($(wildcard $(RELX_CONFIG)),)
run:
else

define get_relx_release.erl
	{ok, Config} = file:consult("$(RELX_CONFIG)"),
	{release, {Name, _}, _} = lists:keyfind(release, 1, Config),
	io:format("~s", [Name]),
	halt(0).
endef

RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`

run: all
	$(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console

help::
	$(verbose) printf "%s\n" "" \
		"Relx targets:" \
		"  run         Compile the project, build the release and run it"

endif

# Copyright (c) 2014, M Robert Martin <rob@version2beta.com>
# This file is contributed to erlang.mk and subject to the terms of the ISC License.

.PHONY: shell

# Configuration.

SHELL_PATH ?= -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin
SHELL_OPTS ?=

ALL_SHELL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(SHELL_DEPS))

# Core targets

help::
	$(verbose) printf "%s\n" "" \
		"Shell targets:" \
		"  shell       Run an erlang shell with SHELL_OPTS or reasonable default"

# Plugin-specific targets.

$(foreach dep,$(SHELL_DEPS),$(eval $(call dep_target,$(dep))))

build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
	$(verbose) for dep in $(ALL_SHELL_DEPS_DIRS) ; do $(MAKE) -C $$dep ; done

shell: build-shell-deps
	$(gen_verbose) erl $(SHELL_PATH) $(SHELL_OPTS)

# Copyright (c) 2015, Loïc Hoguin <essen@ninenines.eu>
# This file is part of erlang.mk and subject to the terms of the ISC License.

ifneq ($(wildcard $(DEPS_DIR)/triq),)
.PHONY: triq

# Targets.

tests:: triq

define triq_check.erl
	code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
	try
		case $(1) of
			all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
			module -> triq:check($(2));
			function -> triq:check($(2))
		end
	of
		true -> halt(0);
		_ -> halt(1)
	catch error:undef ->
		io:format("Undefined property or module~n"),
		halt(0)
	end.
endef

ifdef t
ifeq (,$(findstring :,$(t)))
triq: test-build
	$(verbose) $(call erlang,$(call triq_check.erl,module,$(t)))
else
triq: test-build
	$(verbose) echo Testing $(t)/0
	$(verbose) $(call erlang,$(call triq_check.erl,function,$(t)()))
endif
else
triq: test-build
	$(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename $(wildcard ebin/*.beam))))))
	$(gen_verbose) $(call erlang,$(call triq_check.erl,all,undefined,$(MODULES)))
endif
endif

# Copyright (c) 2015, Erlang Solutions Ltd.
# This file is part of erlang.mk and subject to the terms of the ISC License.

.PHONY: xref distclean-xref

# Configuration.

ifeq ($(XREF_CONFIG),)
	XREF_ARGS :=
else
	XREF_ARGS := -c $(XREF_CONFIG)
endif

XREFR ?= $(CURDIR)/xrefr
export XREFR

XREFR_URL ?= https://github.com/inaka/xref_runner/releases/download/0.2.2/xrefr

# Core targets.

help::
	$(verbose) printf "%s\n" "" \
		"Xref targets:" \
		"  xref        Run Xrefr using $XREF_CONFIG as config file if defined"

distclean:: distclean-xref

# Plugin-specific targets.

$(XREFR):
	$(gen_verbose) $(call core_http_get,$(XREFR),$(XREFR_URL))
	$(verbose) chmod +x $(XREFR)

xref: deps app $(XREFR)
	$(gen_verbose) $(XREFR) $(XREFR_ARGS)

distclean-xref:
	$(gen_verbose) rm -rf $(XREFR)

# Copyright 2015, Viktor Söderqvist <viktor@zuiderkwast.se>
# This file is part of erlang.mk and subject to the terms of the ISC License.

COVER_REPORT_DIR = cover

# Hook in coverage to ct

ifdef COVER
ifdef CT_RUN
# All modules in 'ebin'
COVER_MODS = $(notdir $(basename $(call core_ls,ebin/*.beam)))

test-build:: $(TEST_DIR)/ct.cover.spec

$(TEST_DIR)/ct.cover.spec:
	$(verbose) echo Cover mods: $(COVER_MODS)
	$(gen_verbose) printf "%s\n" \
		'{incl_mods,[$(subst $(space),$(comma),$(COVER_MODS))]}.' \
		'{export,"$(CURDIR)/ct.coverdata"}.' > $@

CT_RUN += -cover $(TEST_DIR)/ct.cover.spec
endif
endif

# Core targets

ifdef COVER
ifneq ($(COVER_REPORT_DIR),)
tests::
	$(verbose) $(MAKE) --no-print-directory cover-report
endif
endif

clean:: coverdata-clean

ifneq ($(COVER_REPORT_DIR),)
distclean:: cover-report-clean
endif

help::
	$(verbose) printf "%s\n" "" \
		"Cover targets:" \
		"  cover-report  Generate a HTML coverage report from previously collected" \
		"                cover data." \
		"  all.coverdata Merge {eunit,ct}.coverdata into one coverdata file." \
		"" \
		"If COVER=1 is set, coverage data is generated by the targets eunit and ct. The" \
		"target tests additionally generates a HTML coverage report from the combined" \
		"coverdata files from each of these testing tools. HTML reports can be disabled" \
		"by setting COVER_REPORT_DIR to empty."

# Plugin specific targets

COVERDATA = $(filter-out all.coverdata,$(wildcard *.coverdata))

.PHONY: coverdata-clean
coverdata-clean:
	$(gen_verbose) rm -f *.coverdata ct.cover.spec

# Merge all coverdata files into one.
all.coverdata: $(COVERDATA)
	$(gen_verbose) $(ERL) -eval ' \
		$(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),) \
		cover:export("$@"), halt(0).'

# These are only defined if COVER_REPORT_DIR is non-empty. Set COVER_REPORT_DIR to
# empty if you want the coverdata files but not the HTML report.
ifneq ($(COVER_REPORT_DIR),)

.PHONY: cover-report-clean cover-report

cover-report-clean:
	$(gen_verbose) rm -rf $(COVER_REPORT_DIR)

ifeq ($(COVERDATA),)
cover-report:
else

# Modules which include eunit.hrl always contain one line without coverage
# because eunit defines test/0 which is never called. We compensate for this.
EUNIT_HRL_MODS = $(subst $(space),$(comma),$(shell \
	grep -e '^\s*-include.*include/eunit\.hrl"' src/*.erl \
	| sed "s/^src\/\(.*\)\.erl:.*/'\1'/" | uniq))

define cover_report.erl
	$(foreach f,$(COVERDATA),cover:import("$(f)") == ok orelse halt(1),)
	Ms = cover:imported_modules(),
	[cover:analyse_to_file(M, "$(COVER_REPORT_DIR)/" ++ atom_to_list(M)
		++ ".COVER.html", [html])  || M <- Ms],
	Report = [begin {ok, R} = cover:analyse(M, module), R end || M <- Ms],
	EunitHrlMods = [$(EUNIT_HRL_MODS)],
	Report1 = [{M, {Y, case lists:member(M, EunitHrlMods) of
		true -> N - 1; false -> N end}} || {M, {Y, N}} <- Report],
	TotalY = lists:sum([Y || {_, {Y, _}} <- Report1]),
	TotalN = lists:sum([N || {_, {_, N}} <- Report1]),
	TotalPerc = round(100 * TotalY / (TotalY + TotalN)),
	{ok, F} = file:open("$(COVER_REPORT_DIR)/index.html", [write]),
	io:format(F, "<!DOCTYPE html><html>~n"
		"<head><meta charset=\"UTF-8\">~n"
		"<title>Coverage report</title></head>~n"
		"<body>~n", []),
	io:format(F, "<h1>Coverage</h1>~n<p>Total: ~p%</p>~n", [TotalPerc]),
	io:format(F, "<table><tr><th>Module</th><th>Coverage</th></tr>~n", []),
	[io:format(F, "<tr><td><a href=\"~p.COVER.html\">~p</a></td>"
		"<td>~p%</td></tr>~n",
		[M, M, round(100 * Y / (Y + N))]) || {M, {Y, N}} <- Report1],
	How = "$(subst $(space),$(comma)$(space),$(basename $(COVERDATA)))",
	Date = "$(shell date -u "+%Y-%m-%dT%H:%M:%SZ")",
	io:format(F, "</table>~n"
		"<p>Generated using ~s and erlang.mk on ~s.</p>~n"
		"</body></html>", [How, Date]),
	halt().
endef

cover-report:
	$(gen_verbose) mkdir -p $(COVER_REPORT_DIR)
	$(gen_verbose) $(call erlang,$(cover_report.erl))

endif
endif # ifneq ($(COVER_REPORT_DIR),)
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted deps/ranch/src/ranch.app.src.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
%% Copyright (c) 2011-2015, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

{application, ranch, [
	{description, "Socket acceptor pool for TCP protocols."},
	{vsn, "1.2.0"},
	{id, "git"},
	{modules, []},
	{registered, [ranch_sup, ranch_server]},
	{applications, [
		kernel,
		stdlib
	]},
	{mod, {ranch_app, []}},
	{env, []}
]}.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






















































Deleted deps/ranch/src/ranch.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
%% Copyright (c) 2011-2015, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(ranch).

-export([start_listener/6]).
-export([stop_listener/1]).
-export([child_spec/6]).
-export([accept_ack/1]).
-export([remove_connection/1]).
-export([get_addr/1]).
-export([get_port/1]).
-export([get_max_connections/1]).
-export([set_max_connections/2]).
-export([get_protocol_options/1]).
-export([set_protocol_options/2]).
-export([filter_options/3]).
-export([set_option_default/3]).
-export([require/1]).

-type max_conns() :: non_neg_integer() | infinity.
-export_type([max_conns/0]).

-type opt() :: {ack_timeout, timeout()}
	| {connection_type, worker | supervisor}
	| {max_connections, max_conns()}
	| {shutdown, timeout() | brutal_kill}
	| {socket, any()}.
-export_type([opt/0]).

-type ref() :: any().
-export_type([ref/0]).

-spec start_listener(ref(), non_neg_integer(), module(), any(), module(), any())
	-> supervisor:startchild_ret().
start_listener(Ref, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts)
		when is_integer(NbAcceptors) andalso is_atom(Transport)
		andalso is_atom(Protocol) ->
	_ = code:ensure_loaded(Transport),
	case erlang:function_exported(Transport, name, 0) of
		false ->
			{error, badarg};
		true ->
			Res = supervisor:start_child(ranch_sup, child_spec(Ref, NbAcceptors,
					Transport, TransOpts, Protocol, ProtoOpts)),
			Socket = proplists:get_value(socket, TransOpts),
			case Res of
				{ok, Pid} when Socket =/= undefined ->
					%% Give ownership of the socket to ranch_acceptors_sup
					%% to make sure the socket stays open as long as the
					%% listener is alive. If the socket closes however there
					%% will be no way to recover because we don't know how
					%% to open it again.
					Children = supervisor:which_children(Pid),
					{_, AcceptorsSup, _, _}
						= lists:keyfind(ranch_acceptors_sup, 1, Children),
					%%% Note: the catch is here because SSL crashes when you change
					%%% the controlling process of a listen socket because of a bug.
					%%% The bug will be fixed in R16.
					catch Transport:controlling_process(Socket, AcceptorsSup);
				_ ->
					ok
			end,
			Res
	end.

-spec stop_listener(ref()) -> ok | {error, not_found}.
stop_listener(Ref) ->
	case supervisor:terminate_child(ranch_sup, {ranch_listener_sup, Ref}) of
		ok ->
			_ = supervisor:delete_child(ranch_sup, {ranch_listener_sup, Ref}),
			ranch_server:cleanup_listener_opts(Ref);
		{error, Reason} ->
			{error, Reason}
	end.

-spec child_spec(ref(), non_neg_integer(), module(), any(), module(), any())
	-> supervisor:child_spec().
child_spec(Ref, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts)
		when is_integer(NbAcceptors) andalso is_atom(Transport)
		andalso is_atom(Protocol) ->
	{{ranch_listener_sup, Ref}, {ranch_listener_sup, start_link, [
		Ref, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts
	]}, permanent, infinity, supervisor, [ranch_listener_sup]}.

-spec accept_ack(ref()) -> ok.
accept_ack(Ref) ->
	receive {shoot, Ref, Transport, Socket, AckTimeout} ->
		Transport:accept_ack(Socket, AckTimeout)
	end.

-spec remove_connection(ref()) -> ok.
remove_connection(Ref) ->
	ConnsSup = ranch_server:get_connections_sup(Ref),
	ConnsSup ! {remove_connection, Ref},
	ok.

-spec get_addr(ref()) -> {inet:ip_address(), inet:port_number()}.
get_addr(Ref) ->
	ranch_server:get_addr(Ref).

-spec get_port(ref()) -> inet:port_number().
get_port(Ref) ->
	{_, Port} = get_addr(Ref),
	Port.

-spec get_max_connections(ref()) -> max_conns().
get_max_connections(Ref) ->
	ranch_server:get_max_connections(Ref).

-spec set_max_connections(ref(), max_conns()) -> ok.
set_max_connections(Ref, MaxConnections) ->
	ranch_server:set_max_connections(Ref, MaxConnections).

-spec get_protocol_options(ref()) -> any().
get_protocol_options(Ref) ->
	ranch_server:get_protocol_options(Ref).

-spec set_protocol_options(ref(), any()) -> ok.
set_protocol_options(Ref, Opts) ->
	ranch_server:set_protocol_options(Ref, Opts).

-spec filter_options([inet | inet6 | {atom(), any()} | {raw, any(), any(), any()}],
	[atom()], Acc) -> Acc when Acc :: [any()].
filter_options(UserOptions, AllowedKeys, DefaultOptions) ->
	AllowedOptions = filter_user_options(UserOptions, AllowedKeys),
	lists:foldl(fun merge_options/2, DefaultOptions, AllowedOptions).

%% 2-tuple options.
filter_user_options([Opt = {Key, _}|Tail], AllowedKeys) ->
	case lists:member(Key, AllowedKeys) of
		true ->
			[Opt|filter_user_options(Tail, AllowedKeys)];
		false ->
			filter_options_warning(Opt),
			filter_user_options(Tail, AllowedKeys)
	end;
%% Special option forms.
filter_user_options([inet|Tail], AllowedKeys) ->
	[inet|filter_user_options(Tail, AllowedKeys)];
filter_user_options([inet6|Tail], AllowedKeys) ->
	[inet6|filter_user_options(Tail, AllowedKeys)];
filter_user_options([Opt = {raw, _, _, _}|Tail], AllowedKeys) ->
	[Opt|filter_user_options(Tail, AllowedKeys)];
filter_user_options([Opt|Tail], AllowedKeys) ->
	filter_options_warning(Opt),
	filter_user_options(Tail, AllowedKeys);
filter_user_options([], _) ->
	[].

filter_options_warning(Opt) ->
	error_logger:warning_msg("Transport option ~p unknown or invalid.~n", [Opt]).

merge_options({Key, _} = Option, OptionList) ->
	lists:keystore(Key, 1, OptionList, Option);
merge_options(Option, OptionList) ->
	[Option|OptionList].

-spec set_option_default(Opts, atom(), any())
	-> Opts when Opts :: [{atom(), any()}].
set_option_default(Opts, Key, Value) ->
	case lists:keymember(Key, 1, Opts) of
		true -> Opts;
		false -> [{Key, Value}|Opts]
	end.

-spec require([atom()]) -> ok.
require([]) ->
	ok;
require([App|Tail]) ->
	case application:start(App) of
		ok -> ok;
		{error, {already_started, App}} -> ok
	end,
	require(Tail).
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




















































































































































































































































































































































































Deleted deps/ranch/src/ranch_acceptor.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
%% Copyright (c) 2011-2015, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(ranch_acceptor).

-export([start_link/3]).
-export([loop/3]).

-spec start_link(inet:socket(), module(), pid())
	-> {ok, pid()}.
start_link(LSocket, Transport, ConnsSup) ->
	Pid = spawn_link(?MODULE, loop, [LSocket, Transport, ConnsSup]),
	{ok, Pid}.

-spec loop(inet:socket(), module(), pid()) -> no_return().
loop(LSocket, Transport, ConnsSup) ->
	_ = case Transport:accept(LSocket, infinity) of
		{ok, CSocket} ->
			case Transport:controlling_process(CSocket, ConnsSup) of
				ok ->
					%% This call will not return until process has been started
					%% AND we are below the maximum number of connections.
					ranch_conns_sup:start_protocol(ConnsSup, CSocket);
				{error, _} ->
					Transport:close(CSocket)
			end;
		%% Reduce the accept rate if we run out of file descriptors.
		%% We can't accept anymore anyway, so we might as well wait
		%% a little for the situation to resolve itself.
		{error, emfile} ->
			receive after 100 -> ok end;
		%% We want to crash if the listening socket got closed.
		{error, Reason} when Reason =/= closed ->
			ok
	end,
	flush(),
	?MODULE:loop(LSocket, Transport, ConnsSup).

flush() ->
	receive Msg ->
		error_logger:error_msg(
			"Ranch acceptor received unexpected message: ~p~n",
			[Msg]),
		flush()
	after 0 ->
		ok
	end.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




















































































































Deleted deps/ranch/src/ranch_acceptors_sup.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
%% Copyright (c) 2011-2015, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(ranch_acceptors_sup).
-behaviour(supervisor).

-export([start_link/4]).
-export([init/1]).

-spec start_link(ranch:ref(), non_neg_integer(), module(), any())
	-> {ok, pid()}.
start_link(Ref, NbAcceptors, Transport, TransOpts) ->
	supervisor:start_link(?MODULE, [Ref, NbAcceptors, Transport, TransOpts]).

init([Ref, NbAcceptors, Transport, TransOpts]) ->
	ConnsSup = ranch_server:get_connections_sup(Ref),
	LSocket = case proplists:get_value(socket, TransOpts) of
		undefined ->
			TransOpts2 = proplists:delete(ack_timeout,
				proplists:delete(connection_type,
				proplists:delete(max_connections,
				proplists:delete(shutdown,
				proplists:delete(socket, TransOpts))))),
			case Transport:listen(TransOpts2) of
				{ok, Socket} -> Socket;
				{error, Reason} -> listen_error(Ref, Transport, TransOpts2, Reason)
			end;
		Socket ->
			Socket
	end,
	{ok, Addr} = Transport:sockname(LSocket),
	ranch_server:set_addr(Ref, Addr),
	Procs = [
		{{acceptor, self(), N}, {ranch_acceptor, start_link, [
			LSocket, Transport, ConnsSup
		]}, permanent, brutal_kill, worker, []}
			|| N <- lists:seq(1, NbAcceptors)],
	{ok, {{one_for_one, 10, 10}, Procs}}.

-spec listen_error(any(), module(), any(), atom()) -> no_return().
listen_error(Ref, Transport, TransOpts2, Reason) ->
	error_logger:error_msg(
		"Failed to start Ranch listener ~p in ~p:listen(~p) for reason ~p (~s)~n",
		[Ref, Transport, TransOpts2, Reason, inet:format_error(Reason)]),
	exit({listen_error, Ref, Reason}).
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
















































































































Deleted deps/ranch/src/ranch_app.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
%% Copyright (c) 2011-2015, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(ranch_app).
-behaviour(application).

-export([start/2]).
-export([stop/1]).
-export([profile_output/0]).

start(_, _) ->
	_ = consider_profiling(),
	ranch_sup:start_link().

stop(_) ->
	ok.

-spec profile_output() -> ok.
profile_output() ->
	eprof:stop_profiling(),
	eprof:log("procs.profile"),
	eprof:analyze(procs),
	eprof:log("total.profile"),
	eprof:analyze(total).

consider_profiling() ->
	case application:get_env(profile) of
		{ok, true} ->
			{ok, _Pid} = eprof:start(),
			eprof:start_profiling([self()]);
		_ ->
			not_profiling
	end.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
























































































Deleted deps/ranch/src/ranch_conns_sup.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
%% Copyright (c) 2011-2015, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

%% Make sure to never reload this module outside a release upgrade,
%% as calling l(ranch_conns_sup) twice will kill the process and all
%% the currently open connections.
-module(ranch_conns_sup).

%% API.
-export([start_link/6]).
-export([start_protocol/2]).
-export([active_connections/1]).

%% Supervisor internals.
-export([init/7]).
-export([system_continue/3]).
-export([system_terminate/4]).
-export([system_code_change/4]).

-type conn_type() :: worker | supervisor.
-type shutdown() :: brutal_kill | timeout().

-record(state, {
	parent = undefined :: pid(),
	ref :: ranch:ref(),
	conn_type :: conn_type(),
	shutdown :: shutdown(),
	transport = undefined :: module(),
	protocol = undefined :: module(),
	opts :: any(),
	ack_timeout :: timeout(),
	max_conns = undefined :: ranch:max_conns()
}).

%% API.

-spec start_link(ranch:ref(), conn_type(), shutdown(), module(),
	timeout(), module()) -> {ok, pid()}.
start_link(Ref, ConnType, Shutdown, Transport, AckTimeout, Protocol) ->
	proc_lib:start_link(?MODULE, init,
		[self(), Ref, ConnType, Shutdown, Transport, AckTimeout, Protocol]).

%% We can safely assume we are on the same node as the supervisor.
%%
%% We can also safely avoid having a monitor and a timeout here
%% because only three things can happen:
%%  *  The supervisor died; rest_for_one strategy killed all acceptors
%%     so this very calling process is going to di--
%%  *  There's too many connections, the supervisor will resume the
%%     acceptor only when we get below the limit again.
%%  *  The supervisor is overloaded, there's either too many acceptors
%%     or the max_connections limit is too large. It's better if we
%%     don't keep accepting connections because this leaves
%%     more room for the situation to be resolved.
%%
%% We do not need the reply, we only need the ok from the supervisor
%% to continue. The supervisor sends its own pid when the acceptor can
%% continue.
-spec start_protocol(pid(), inet:socket()) -> ok.
start_protocol(SupPid, Socket) ->
	SupPid ! {?MODULE, start_protocol, self(), Socket},
	receive SupPid -> ok end.

%% We can't make the above assumptions here. This function might be
%% called from anywhere.
-spec active_connections(pid()) -> non_neg_integer().
active_connections(SupPid) ->
	Tag = erlang:monitor(process, SupPid),
	catch erlang:send(SupPid, {?MODULE, active_connections, self(), Tag},
		[noconnect]),
	receive
		{Tag, Ret} ->
			erlang:demonitor(Tag, [flush]),
			Ret;
		{'DOWN', Tag, _, _, noconnection} ->
			exit({nodedown, node(SupPid)});
		{'DOWN', Tag, _, _, Reason} ->
			exit(Reason)
	after 5000 ->
		erlang:demonitor(Tag, [flush]),
		exit(timeout)
	end.

%% Supervisor internals.

-spec init(pid(), ranch:ref(), conn_type(), shutdown(),
	module(), timeout(), module()) -> no_return().
init(Parent, Ref, ConnType, Shutdown, Transport, AckTimeout, Protocol) ->
	process_flag(trap_exit, true),
	ok = ranch_server:set_connections_sup(Ref, self()),
	MaxConns = ranch_server:get_max_connections(Ref),
	Opts = ranch_server:get_protocol_options(Ref),
	ok = proc_lib:init_ack(Parent, {ok, self()}),
	loop(#state{parent=Parent, ref=Ref, conn_type=ConnType,
		shutdown=Shutdown, transport=Transport, protocol=Protocol,
		opts=Opts, ack_timeout=AckTimeout, max_conns=MaxConns}, 0, 0, []).

loop(State=#state{parent=Parent, ref=Ref, conn_type=ConnType,
		transport=Transport, protocol=Protocol, opts=Opts,
		max_conns=MaxConns}, CurConns, NbChildren, Sleepers) ->
	receive
		{?MODULE, start_protocol, To, Socket} ->
			try Protocol:start_link(Ref, Socket, Transport, Opts) of
				{ok, Pid} ->
					shoot(State, CurConns, NbChildren, Sleepers, To, Socket, Pid, Pid);
				{ok, SupPid, ProtocolPid} when ConnType =:= supervisor ->
					shoot(State, CurConns, NbChildren, Sleepers, To, Socket, SupPid, ProtocolPid);
				Ret ->
					To ! self(),
					error_logger:error_msg(
						"Ranch listener ~p connection process start failure; "
						"~p:start_link/4 returned: ~999999p~n",
						[Ref, Protocol, Ret]),
					Transport:close(Socket),
					loop(State, CurConns, NbChildren, Sleepers)
			catch Class:Reason ->
				To ! self(),
				error_logger:error_msg(
					"Ranch listener ~p connection process start failure; "
					"~p:start_link/4 crashed with reason: ~p:~999999p~n",
					[Ref, Protocol, Class, Reason]),
				loop(State, CurConns, NbChildren, Sleepers)
			end;
		{?MODULE, active_connections, To, Tag} ->
			To ! {Tag, CurConns},
			loop(State, CurConns, NbChildren, Sleepers);
		%% Remove a connection from the count of connections.
		{remove_connection, Ref} ->
			loop(State, CurConns - 1, NbChildren, Sleepers);
		%% Upgrade the max number of connections allowed concurrently.
		%% We resume all sleeping acceptors if this number increases.
		{set_max_conns, MaxConns2} when MaxConns2 > MaxConns ->
			_ = [To ! self() || To <- Sleepers],
			loop(State#state{max_conns=MaxConns2},
				CurConns, NbChildren, []);
		{set_max_conns, MaxConns2} ->
			loop(State#state{max_conns=MaxConns2},
				CurConns, NbChildren, Sleepers);
		%% Upgrade the protocol options.
		{set_opts, Opts2} ->
			loop(State#state{opts=Opts2},
				CurConns, NbChildren, Sleepers);
		{'EXIT', Parent, Reason} ->
			terminate(State, Reason, NbChildren);
		{'EXIT', Pid, Reason} when Sleepers =:= [] ->
			report_error(Ref, Protocol, Pid, Reason),
			erase(Pid),
			loop(State, CurConns - 1, NbChildren - 1, Sleepers);
		%% Resume a sleeping acceptor if needed.
		{'EXIT', Pid, Reason} ->
			report_error(Ref, Protocol, Pid, Reason),
			erase(Pid),
			[To|Sleepers2] = Sleepers,
			To ! self(),
			loop(State, CurConns - 1, NbChildren - 1, Sleepers2);
		{system, From, Request} ->
			sys:handle_system_msg(Request, From, Parent, ?MODULE, [],
				{State, CurConns, NbChildren, Sleepers});
		%% Calls from the supervisor module.
		{'$gen_call', {To, Tag}, which_children} ->
			Pids = get_keys(true),
			Children = [{Protocol, Pid, ConnType, [Protocol]}
				|| Pid <- Pids, is_pid(Pid)],
			To ! {Tag, Children},
			loop(State, CurConns, NbChildren, Sleepers);
		{'$gen_call', {To, Tag}, count_children} ->
			Counts = case ConnType of
				worker -> [{supervisors, 0}, {workers, NbChildren}];
				supervisor -> [{supervisors, NbChildren}, {workers, 0}]
			end,
			Counts2 = [{specs, 1}, {active, NbChildren}|Counts],
			To ! {Tag, Counts2},
			loop(State, CurConns, NbChildren, Sleepers);
		{'$gen_call', {To, Tag}, _} ->
			To ! {Tag, {error, ?MODULE}},
			loop(State, CurConns, NbChildren, Sleepers);
		Msg ->
			error_logger:error_msg(
				"Ranch listener ~p received unexpected message ~p~n",
				[Ref, Msg])
	end.

shoot(State=#state{ref=Ref, transport=Transport, ack_timeout=AckTimeout, max_conns=MaxConns},
		CurConns, NbChildren, Sleepers, To, Socket, SupPid, ProtocolPid) ->
	case Transport:controlling_process(Socket, ProtocolPid) of
		ok ->
			ProtocolPid ! {shoot, Ref, Transport, Socket, AckTimeout},
			put(SupPid, true),
			CurConns2 = CurConns + 1,
			if CurConns2 < MaxConns ->
					To ! self(),
					loop(State, CurConns2, NbChildren + 1, Sleepers);
				true ->
					loop(State, CurConns2, NbChildren + 1, [To|Sleepers])
			end;
		{error, _} ->
			Transport:close(Socket),
			%% Only kill the supervised pid, because the connection's pid,
			%% when different, is supposed to be sitting under it and linked.
			exit(SupPid, kill),
			loop(State, CurConns, NbChildren, Sleepers)
	end.

-spec terminate(#state{}, any(), non_neg_integer()) -> no_return().
%% Kill all children and then exit. We unlink first to avoid
%% getting a message for each child getting killed.
terminate(#state{shutdown=brutal_kill}, Reason, _) ->
	Pids = get_keys(true),
	_ = [begin
		unlink(P),
		exit(P, kill)
	end || P <- Pids],
	exit(Reason);
%% Attempt to gracefully shutdown all children.
terminate(#state{shutdown=Shutdown}, Reason, NbChildren) ->
	shutdown_children(),
	_ = if
		Shutdown =:= infinity ->
			ok;
		true ->
			erlang:send_after(Shutdown, self(), kill)
	end,
	wait_children(NbChildren),
	exit(Reason).

%% Monitor processes so we can know which ones have shutdown
%% before the timeout. Unlink so we avoid receiving an extra
%% message. Then send a shutdown exit signal.
shutdown_children() ->
	Pids = get_keys(true),
	_ = [begin
		monitor(process, P),
		unlink(P),
		exit(P, shutdown)
	end || P <- Pids],
	ok.

wait_children(0) ->
	ok;
wait_children(NbChildren) ->
	receive
        {'DOWN', _, process, Pid, _} ->
			_ = erase(Pid),
			wait_children(NbChildren - 1);
		kill ->
			Pids = get_keys(true),
			_ = [exit(P, kill) || P <- Pids],
			ok
	end.

system_continue(_, _, {State, CurConns, NbChildren, Sleepers}) ->
	loop(State, CurConns, NbChildren, Sleepers).

-spec system_terminate(any(), _, _, _) -> no_return().
system_terminate(Reason, _, _, {State, _, NbChildren, _}) ->
	terminate(State, Reason, NbChildren).

system_code_change(Misc, _, _, _) ->
	{ok, Misc}.

%% We use ~999999p here instead of ~w because the latter doesn't
%% support printable strings.
report_error(_, _, _, normal) ->
	ok;
report_error(_, _, _, shutdown) ->
	ok;
report_error(_, _, _, {shutdown, _}) ->
	ok;
report_error(Ref, Protocol, Pid, Reason) ->
	error_logger:error_msg(
		"Ranch listener ~p had connection process started with "
		"~p:start_link/4 at ~p exit with reason: ~999999p~n",
		[Ref, Protocol, Pid, Reason]).
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
























































































































































































































































































































































































































































































































































































Deleted deps/ranch/src/ranch_listener_sup.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
%% Copyright (c) 2011-2015, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(ranch_listener_sup).
-behaviour(supervisor).

-export([start_link/6]).
-export([init/1]).

-spec start_link(ranch:ref(), non_neg_integer(), module(), any(), module(), any())
	-> {ok, pid()}.
start_link(Ref, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts) ->
	MaxConns = proplists:get_value(max_connections, TransOpts, 1024),
	ranch_server:set_new_listener_opts(Ref, MaxConns, ProtoOpts),
	supervisor:start_link(?MODULE, {
		Ref, NbAcceptors, Transport, TransOpts, Protocol
	}).

init({Ref, NbAcceptors, Transport, TransOpts, Protocol}) ->
	AckTimeout = proplists:get_value(ack_timeout, TransOpts, 5000),
	ConnType = proplists:get_value(connection_type, TransOpts, worker),
	Shutdown = proplists:get_value(shutdown, TransOpts, 5000),
	ChildSpecs = [
		{ranch_conns_sup, {ranch_conns_sup, start_link,
				[Ref, ConnType, Shutdown, Transport, AckTimeout, Protocol]},
			permanent, infinity, supervisor, [ranch_conns_sup]},
		{ranch_acceptors_sup, {ranch_acceptors_sup, start_link,
				[Ref, NbAcceptors, Transport, TransOpts]},
			permanent, infinity, supervisor, [ranch_acceptors_sup]}
	],
	{ok, {{rest_for_one, 10, 10}, ChildSpecs}}.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




















































































Deleted deps/ranch/src/ranch_protocol.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
%% Copyright (c) 2012-2015, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(ranch_protocol).

%% Start a new connection process for the given socket.
-callback start_link(
		Ref::ranch:ref(),
		Socket::any(),
		Transport::module(),
		ProtocolOptions::any())
	-> {ok, ConnectionPid::pid()}
	| {ok, SupPid::pid(), ConnectionPid::pid()}.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
















































Deleted deps/ranch/src/ranch_server.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
%% Copyright (c) 2012-2015, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(ranch_server).
-behaviour(gen_server).

%% API.
-export([start_link/0]).
-export([set_new_listener_opts/3]).
-export([cleanup_listener_opts/1]).
-export([set_connections_sup/2]).
-export([get_connections_sup/1]).
-export([set_addr/2]).
-export([get_addr/1]).
-export([set_max_connections/2]).
-export([get_max_connections/1]).
-export([set_protocol_options/2]).
-export([get_protocol_options/1]).
-export([count_connections/1]).

%% gen_server.
-export([init/1]).
-export([handle_call/3]).
-export([handle_cast/2]).
-export([handle_info/2]).
-export([terminate/2]).
-export([code_change/3]).

-define(TAB, ?MODULE).

-type monitors() :: [{{reference(), pid()}, any()}].
-record(state, {
	monitors = [] :: monitors()
}).

%% API.

-spec start_link() -> {ok, pid()}.
start_link() ->
	gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

-spec set_new_listener_opts(ranch:ref(), ranch:max_conns(), any()) -> ok.
set_new_listener_opts(Ref, MaxConns, Opts) ->
	gen_server:call(?MODULE, {set_new_listener_opts, Ref, MaxConns, Opts}).

-spec cleanup_listener_opts(ranch:ref()) -> ok.
cleanup_listener_opts(Ref) ->
	_ = ets:delete(?TAB, {addr, Ref}),
	_ = ets:delete(?TAB, {max_conns, Ref}),
	_ = ets:delete(?TAB, {opts, Ref}),
	ok.

-spec set_connections_sup(ranch:ref(), pid()) -> ok.
set_connections_sup(Ref, Pid) ->
	true = gen_server:call(?MODULE, {set_connections_sup, Ref, Pid}),
	ok.

-spec get_connections_sup(ranch:ref()) -> pid().
get_connections_sup(Ref) ->
	ets:lookup_element(?TAB, {conns_sup, Ref}, 2).

-spec set_addr(ranch:ref(), {inet:ip_address(), inet:port_number()}) -> ok.
set_addr(Ref, Addr) ->
	gen_server:call(?MODULE, {set_addr, Ref, Addr}).

-spec get_addr(ranch:ref()) -> {inet:ip_address(), inet:port_number()}.
get_addr(Ref) ->
	ets:lookup_element(?TAB, {addr, Ref}, 2).

-spec set_max_connections(ranch:ref(), ranch:max_conns()) -> ok.
set_max_connections(Ref, MaxConnections) ->
	gen_server:call(?MODULE, {set_max_conns, Ref, MaxConnections}).

-spec get_max_connections(ranch:ref()) -> ranch:max_conns().
get_max_connections(Ref) ->
	ets:lookup_element(?TAB, {max_conns, Ref}, 2).

-spec set_protocol_options(ranch:ref(), any()) -> ok.
set_protocol_options(Ref, ProtoOpts) ->
	gen_server:call(?MODULE, {set_opts, Ref, ProtoOpts}).

-spec get_protocol_options(ranch:ref()) -> any().
get_protocol_options(Ref) ->
	ets:lookup_element(?TAB, {opts, Ref}, 2).

-spec count_connections(ranch:ref()) -> non_neg_integer().
count_connections(Ref) ->
	ranch_conns_sup:active_connections(get_connections_sup(Ref)).

%% gen_server.

init([]) ->
	Monitors = [{{erlang:monitor(process, Pid), Pid}, Ref} ||
		[Ref, Pid] <- ets:match(?TAB, {{conns_sup, '$1'}, '$2'})],
	{ok, #state{monitors=Monitors}}.

handle_call({set_new_listener_opts, Ref, MaxConns, Opts}, _, State) ->
	ets:insert(?TAB, {{max_conns, Ref}, MaxConns}),
	ets:insert(?TAB, {{opts, Ref}, Opts}),
	{reply, ok, State};
handle_call({set_connections_sup, Ref, Pid}, _,
		State=#state{monitors=Monitors}) ->
	case ets:insert_new(?TAB, {{conns_sup, Ref}, Pid}) of
		true ->
			MonitorRef = erlang:monitor(process, Pid),
			{reply, true,
				State#state{monitors=[{{MonitorRef, Pid}, Ref}|Monitors]}};
		false ->
			{reply, false, State}
	end;
handle_call({set_addr, Ref, Addr}, _, State) ->
	true = ets:insert(?TAB, {{addr, Ref}, Addr}),
	{reply, ok, State};
handle_call({set_max_conns, Ref, MaxConns}, _, State) ->
	ets:insert(?TAB, {{max_conns, Ref}, MaxConns}),
	ConnsSup = get_connections_sup(Ref),
	ConnsSup ! {set_max_conns, MaxConns},
	{reply, ok, State};
handle_call({set_opts, Ref, Opts}, _, State) ->
	ets:insert(?TAB, {{opts, Ref}, Opts}),
	ConnsSup = get_connections_sup(Ref),
	ConnsSup ! {set_opts, Opts},
	{reply, ok, State};
handle_call(_Request, _From, State) ->
	{reply, ignore, State}.

handle_cast(_Request, State) ->
	{noreply, State}.

handle_info({'DOWN', MonitorRef, process, Pid, _},
		State=#state{monitors=Monitors}) ->
	{_, Ref} = lists:keyfind({MonitorRef, Pid}, 1, Monitors),
	true = ets:delete(?TAB, {conns_sup, Ref}),
	Monitors2 = lists:keydelete({MonitorRef, Pid}, 1, Monitors),
	{noreply, State#state{monitors=Monitors2}};
handle_info(_Info, State) ->
	{noreply, State}.

terminate(_Reason, _State) ->
	ok.

code_change(_OldVsn, State, _Extra) ->
	{ok, State}.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




















































































































































































































































































































Deleted deps/ranch/src/ranch_ssl.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
%% Copyright (c) 2011-2015, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(ranch_ssl).
-behaviour(ranch_transport).

-export([name/0]).
-export([secure/0]).
-export([messages/0]).
-export([listen/1]).
-export([listen_options/0]).
-export([accept/2]).
-export([accept_ack/2]).
-export([connect/3]).
-export([connect/4]).
-export([recv/3]).
-export([send/2]).
-export([sendfile/2]).
-export([sendfile/4]).
-export([sendfile/5]).
-export([setopts/2]).
-export([controlling_process/2]).
-export([peername/1]).
-export([sockname/1]).
-export([shutdown/2]).
-export([close/1]).

-type ssl_opt() :: {alpn_preferred_protocols, [binary()]}
	| {cacertfile, string()}
	| {cacerts, [public_key:der_encoded()]}
	| {cert, public_key:der_encoded()}
	| {certfile, string()}
	| {ciphers, [ssl:erl_cipher_suite()] | string()}
	| {client_renegotiation, boolean()}
	| {crl_cache, {module(), {internal | any(), list()}}}
	| {crl_check, boolean() | peer | best_effort}
	| {depth, 0..255}
	| {dh, public_key:der_encoded()}
	| {dhfile, string()}
	| {fail_if_no_peer_cert, boolean()}
	| {hibernate_after, integer() | undefined}
	| {honor_cipher_order, boolean()}
	| {key, {'RSAPrivateKey' | 'DSAPrivateKey' | 'PrivateKeyInfo', public_key:der_encoded()}}
	| {keyfile, string()}
	| {log_alert, boolean()}
	| {next_protocols_advertised, [binary()]}
	| {partial_chain, fun(([public_key:der_encoded()]) -> {trusted_ca, public_key:der_encoded()} | unknown_ca)}
	| {password, string()}
	| {psk_identity, string()}
	| {reuse_session, fun()}
	| {reuse_sessions, boolean()}
	| {secure_renegotiate, boolean()}
	| {sni_fun, fun()}
	| {sni_hosts, [{string(), ssl_opt()}]}
	| {user_lookup_fun, {fun(), any()}}
	| {verify, ssl:verify_type()}
	| {verify_fun, {fun(), any()}}
	| {versions, [atom()]}.
-export_type([ssl_opt/0]).

-type opt() :: ranch_tcp:opt() | ssl_opt().
-export_type([opt/0]).

-type opts() :: [opt()].
-export_type([opts/0]).

name() -> ssl.

-spec secure() -> boolean().
secure() ->
    true.

messages() -> {ssl, ssl_closed, ssl_error}.

-spec listen(opts()) -> {ok, ssl:sslsocket()} | {error, atom()}.
listen(Opts) ->
	ranch:require([crypto, asn1, public_key, ssl]),
	true = lists:keymember(cert, 1, Opts)
		orelse lists:keymember(certfile, 1, Opts),
	Opts2 = ranch:set_option_default(Opts, backlog, 1024),
	Opts3 = ranch:set_option_default(Opts2, ciphers, unbroken_cipher_suites()),
	Opts4 = ranch:set_option_default(Opts3, nodelay, true),
	Opts5 = ranch:set_option_default(Opts4, send_timeout, 30000),
	Opts6 = ranch:set_option_default(Opts5, send_timeout_close, true),
	%% We set the port to 0 because it is given in the Opts directly.
	%% The port in the options takes precedence over the one in the
	%% first argument.
	ssl:listen(0, ranch:filter_options(Opts6, listen_options(),
		[binary, {active, false}, {packet, raw},
			{reuseaddr, true}, {nodelay, true}])).

listen_options() ->
	[alpn_preferred_protocols, cacertfile, cacerts, cert, certfile,
		ciphers, client_renegotiation, crl_cache, crl_check, depth,
		dh, dhfile, fail_if_no_peer_cert, hibernate_after, honor_cipher_order,
		key, keyfile, log_alert, next_protocols_advertised, partial_chain,
		password, psk_identity, reuse_session, reuse_sessions, secure_renegotiate,
		sni_fun, sni_hosts, user_lookup_fun, verify, verify_fun, versions
		|ranch_tcp:listen_options()].

-spec accept(ssl:sslsocket(), timeout())
	-> {ok, ssl:sslsocket()} | {error, closed | timeout | atom()}.
accept(LSocket, Timeout) ->
	ssl:transport_accept(LSocket, Timeout).

-spec accept_ack(ssl:sslsocket(), timeout()) -> ok.
accept_ack(CSocket, Timeout) ->
	case ssl:ssl_accept(CSocket, Timeout) of
		ok ->
			ok;
		%% Garbage was most likely sent to the socket, don't error out.
		{error, {tls_alert, _}} ->
			ok = close(CSocket),
			exit(normal);
		%% Socket most likely stopped responding, don't error out.
		{error, Reason} when Reason =:= timeout; Reason =:= closed ->
			ok = close(CSocket),
			exit(normal);
		{error, Reason} ->
			ok = close(CSocket),
			error(Reason)
	end.

%% @todo Probably filter Opts?
-spec connect(inet:ip_address() | inet:hostname(),
	inet:port_number(), any())
	-> {ok, inet:socket()} | {error, atom()}.
connect(Host, Port, Opts) when is_integer(Port) ->
	ssl:connect(Host, Port,
		Opts ++ [binary, {active, false}, {packet, raw}]).

%% @todo Probably filter Opts?
-spec connect(inet:ip_address() | inet:hostname(),
	inet:port_number(), any(), timeout())
	-> {ok, inet:socket()} | {error, atom()}.
connect(Host, Port, Opts, Timeout) when is_integer(Port) ->
	ssl:connect(Host, Port,
		Opts ++ [binary, {active, false}, {packet, raw}],
		Timeout).

-spec recv(ssl:sslsocket(), non_neg_integer(), timeout())
	-> {ok, any()} | {error, closed | atom()}.
recv(Socket, Length, Timeout) ->
	ssl:recv(Socket, Length, Timeout).

-spec send(ssl:sslsocket(), iodata()) -> ok | {error, atom()}.
send(Socket, Packet) ->
	ssl:send(Socket, Packet).

-spec sendfile(ssl:sslsocket(), file:name_all() | file:fd())
	-> {ok, non_neg_integer()} | {error, atom()}.
sendfile(Socket, Filename) ->
	sendfile(Socket, Filename, 0, 0, []).

-spec sendfile(ssl:sslsocket(), file:name_all() | file:fd(),
		non_neg_integer(), non_neg_integer())
	-> {ok, non_neg_integer()} | {error, atom()}.
sendfile(Socket, File, Offset, Bytes) ->
	sendfile(Socket, File, Offset, Bytes, []).

%% Unlike with TCP, no syscall can be used here, so sending files
%% through SSL will be much slower in comparison. Note that unlike
%% file:sendfile/5 this function accepts either a file or a file name.
-spec sendfile(ssl:sslsocket(), file:name_all() | file:fd(),
		non_neg_integer(), non_neg_integer(), ranch_transport:sendfile_opts())
	-> {ok, non_neg_integer()} | {error, atom()}.
sendfile(Socket, File, Offset, Bytes, Opts) ->
	ranch_transport:sendfile(?MODULE, Socket, File, Offset, Bytes, Opts).

%% @todo Probably filter Opts?
-spec setopts(ssl:sslsocket(), list()) -> ok | {error, atom()}.
setopts(Socket, Opts) ->
	ssl:setopts(Socket, Opts).

-spec controlling_process(ssl:sslsocket(), pid())
	-> ok | {error, closed | not_owner | atom()}.
controlling_process(Socket, Pid) ->
	ssl:controlling_process(Socket, Pid).

-spec peername(ssl:sslsocket())
	-> {ok, {inet:ip_address(), inet:port_number()}} | {error, atom()}.
peername(Socket) ->
	ssl:peername(Socket).

-spec sockname(ssl:sslsocket())
	-> {ok, {inet:ip_address(), inet:port_number()}} | {error, atom()}.
sockname(Socket) ->
	ssl:sockname(Socket).

-spec shutdown(ssl:sslsocket(), read | write | read_write)
	-> ok | {error, atom()}.
shutdown(Socket, How) ->
	ssl:shutdown(Socket, How).

-spec close(ssl:sslsocket()) -> ok.
close(Socket) ->
	ssl:close(Socket).

%% Internal.

%% Unfortunately the implementation of elliptic-curve ciphers that has
%% been introduced in R16B01 is incomplete.  Depending on the particular
%% client, this can cause the TLS handshake to break during key
%% agreement.  Depending on the ssl application version, this function
%% returns a list of all cipher suites that are supported by default,
%% minus the elliptic-curve ones.
-spec unbroken_cipher_suites() -> [ssl:erl_cipher_suite()].
unbroken_cipher_suites() ->
	case proplists:get_value(ssl_app, ssl:versions()) of
		Version when Version =:= "5.3"; Version =:= "5.3.1" ->
			lists:filter(fun(Suite) ->
				string:left(atom_to_list(element(1, Suite)), 4) =/= "ecdh"
			end, ssl:cipher_suites());
		_ ->
			ssl:cipher_suites()
	end.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






































































































































































































































































































































































































































































Deleted deps/ranch/src/ranch_sup.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
%% Copyright (c) 2011-2015, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(ranch_sup).
-behaviour(supervisor).

-export([start_link/0]).
-export([init/1]).

-spec start_link() -> {ok, pid()}.
start_link() ->
	supervisor:start_link({local, ?MODULE}, ?MODULE, []).

init([]) ->
	ranch_server = ets:new(ranch_server, [
		ordered_set, public, named_table]),
	Procs = [
		{ranch_server, {ranch_server, start_link, []},
			permanent, 5000, worker, [ranch_server]}
	],
	{ok, {{one_for_one, 10, 10}, Procs}}.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
































































Deleted deps/ranch/src/ranch_tcp.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
%% Copyright (c) 2011-2015, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(ranch_tcp).
-behaviour(ranch_transport).

-export([name/0]).
-export([secure/0]).
-export([messages/0]).
-export([listen/1]).
-export([listen_options/0]).
-export([accept/2]).
-export([accept_ack/2]).
-export([connect/3]).
-export([connect/4]).
-export([recv/3]).
-export([send/2]).
-export([sendfile/2]).
-export([sendfile/4]).
-export([sendfile/5]).
-export([setopts/2]).
-export([controlling_process/2]).
-export([peername/1]).
-export([sockname/1]).
-export([shutdown/2]).
-export([close/1]).

-type opt() :: {backlog, non_neg_integer()}
	| {buffer, non_neg_integer()}
	| {delay_send, boolean()}
	| {dontroute, boolean()}
	| {exit_on_close, boolean()}
	| {fd, non_neg_integer()}
	| {high_msgq_watermark, non_neg_integer()}
	| {high_watermark, non_neg_integer()}
	| inet
	| inet6
	| {ip, inet:ip_address()}
	| {keepalive, boolean()}
	| {linger, {boolean(), non_neg_integer()}}
	| {low_msgq_watermark, non_neg_integer()}
	| {low_watermark, non_neg_integer()}
	| {nodelay, boolean()}
	| {port, inet:port_number()}
	| {priority, integer()}
	| {raw, non_neg_integer(), non_neg_integer(), binary()}
	| {recbuf, non_neg_integer()}
	| {send_timeout, timeout()}
	| {send_timeout_close, boolean()}
	| {sndbuf, non_neg_integer()}
	| {tos, integer()}.
-export_type([opt/0]).

-type opts() :: [opt()].
-export_type([opts/0]).

name() -> tcp.

-spec secure() -> boolean().
secure() ->
    false.

messages() -> {tcp, tcp_closed, tcp_error}.

-spec listen(opts()) -> {ok, inet:socket()} | {error, atom()}.
listen(Opts) ->
	Opts2 = ranch:set_option_default(Opts, backlog, 1024),
	Opts3 = ranch:set_option_default(Opts2, nodelay, true),
	Opts4 = ranch:set_option_default(Opts3, send_timeout, 30000),
	Opts5 = ranch:set_option_default(Opts4, send_timeout_close, true),
	%% We set the port to 0 because it is given in the Opts directly.
	%% The port in the options takes precedence over the one in the
	%% first argument.
	gen_tcp:listen(0, ranch:filter_options(Opts5, listen_options(),
		[binary, {active, false}, {packet, raw}, {reuseaddr, true}])).

%% 'inet' and 'inet6' are also allowed but they are handled
%% specifically as they do not have 2-tuple equivalents.
%%
%% The 4-tuple 'raw' option is also handled specifically.
listen_options() ->
	[backlog, buffer, delay_send, dontroute, exit_on_close, fd,
		high_msgq_watermark, high_watermark, ip,
		keepalive, linger, low_msgq_watermark,
		low_watermark, nodelay, port, priority, recbuf,
		send_timeout, send_timeout_close, sndbuf, tos].

-spec accept(inet:socket(), timeout())
	-> {ok, inet:socket()} | {error, closed | timeout | atom()}.
accept(LSocket, Timeout) ->
	gen_tcp:accept(LSocket, Timeout).

-spec accept_ack(inet:socket(), timeout()) -> ok.
accept_ack(_, _) ->
	ok.

%% @todo Probably filter Opts?
-spec connect(inet:ip_address() | inet:hostname(),
	inet:port_number(), any())
	-> {ok, inet:socket()} | {error, atom()}.
connect(Host, Port, Opts) when is_integer(Port) ->
	gen_tcp:connect(Host, Port,
		Opts ++ [binary, {active, false}, {packet, raw}]).

%% @todo Probably filter Opts?
-spec connect(inet:ip_address() | inet:hostname(),
	inet:port_number(), any(), timeout())
	-> {ok, inet:socket()} | {error, atom()}.
connect(Host, Port, Opts, Timeout) when is_integer(Port) ->
	gen_tcp:connect(Host, Port,
		Opts ++ [binary, {active, false}, {packet, raw}],
		Timeout).

-spec recv(inet:socket(), non_neg_integer(), timeout())
	-> {ok, any()} | {error, closed | atom()}.
recv(Socket, Length, Timeout) ->
	gen_tcp:recv(Socket, Length, Timeout).

-spec send(inet:socket(), iodata()) -> ok | {error, atom()}.
send(Socket, Packet) ->
	gen_tcp:send(Socket, Packet).

-spec sendfile(inet:socket(), file:name_all() | file:fd())
	-> {ok, non_neg_integer()} | {error, atom()}.
sendfile(Socket, Filename) ->
	sendfile(Socket, Filename, 0, 0, []).

-spec sendfile(inet:socket(), file:name_all() | file:fd(), non_neg_integer(),
		non_neg_integer())
	-> {ok, non_neg_integer()} | {error, atom()}.
sendfile(Socket, File, Offset, Bytes) ->
	sendfile(Socket, File, Offset, Bytes, []).

-spec sendfile(inet:socket(), file:name_all() | file:fd(), non_neg_integer(),
		non_neg_integer(), [{chunk_size, non_neg_integer()}])
	-> {ok, non_neg_integer()} | {error, atom()}.
sendfile(Socket, Filename, Offset, Bytes, Opts)
		when is_list(Filename) orelse is_atom(Filename)
		orelse is_binary(Filename) ->
	case file:open(Filename, [read, raw, binary]) of
		{ok, RawFile} ->
			try sendfile(Socket, RawFile, Offset, Bytes, Opts) of
				Result -> Result
			after
				ok = file:close(RawFile)
			end;
		{error, _} = Error ->
			Error
	end;
sendfile(Socket, RawFile, Offset, Bytes, Opts) ->
	Opts2 = case Opts of
		[] -> [{chunk_size, 16#1FFF}];
		_ -> Opts
	end,
	try file:sendfile(RawFile, Socket, Offset, Bytes, Opts2) of
		Result -> Result
	catch
		error:{badmatch, {error, enotconn}} ->
			%% file:sendfile/5 might fail by throwing a
			%% {badmatch, {error, enotconn}}. This is because its
			%% implementation fails with a badmatch in
			%% prim_file:sendfile/10 if the socket is not connected.
			{error, closed}
	end.

%% @todo Probably filter Opts?
-spec setopts(inet:socket(), list()) -> ok | {error, atom()}.
setopts(Socket, Opts) ->
	inet:setopts(Socket, Opts).

-spec controlling_process(inet:socket(), pid())
	-> ok | {error, closed | not_owner | atom()}.
controlling_process(Socket, Pid) ->
	gen_tcp:controlling_process(Socket, Pid).

-spec peername(inet:socket())
	-> {ok, {inet:ip_address(), inet:port_number()}} | {error, atom()}.
peername(Socket) ->
	inet:peername(Socket).

-spec sockname(inet:socket())
	-> {ok, {inet:ip_address(), inet:port_number()}} | {error, atom()}.
sockname(Socket) ->
	inet:sockname(Socket).

-spec shutdown(inet:socket(), read | write | read_write)
	-> ok | {error, atom()}.
shutdown(Socket, How) ->
	gen_tcp:shutdown(Socket, How).

-spec close(inet:socket()) -> ok.
close(Socket) ->
	gen_tcp:close(Socket).
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
























































































































































































































































































































































































































Deleted deps/ranch/src/ranch_transport.erl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
%% Copyright (c) 2012-2015, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(ranch_transport).

-export([sendfile/6]).

-type socket() :: any().
-type opts() :: any().
-type sendfile_opts() :: [{chunk_size, non_neg_integer()}].
-export_type([sendfile_opts/0]).

-callback name() -> atom().
-callback secure() -> boolean().
-callback messages() -> {OK::atom(), Closed::atom(), Error::atom()}.
-callback listen(opts()) -> {ok, socket()} | {error, atom()}.
-callback accept(socket(), timeout())
	-> {ok, socket()} | {error, closed | timeout | atom()}.
-callback accept_ack(socket(), timeout()) -> ok.
-callback connect(string(), inet:port_number(), opts())
	-> {ok, socket()} | {error, atom()}.
-callback connect(string(), inet:port_number(), opts(), timeout())
	-> {ok, socket()} | {error, atom()}.
-callback recv(socket(), non_neg_integer(), timeout())
	-> {ok, any()} | {error, closed | timeout | atom()}.
-callback send(socket(), iodata()) -> ok | {error, atom()}.
-callback sendfile(socket(), file:name() | file:fd())
	-> {ok, non_neg_integer()} | {error, atom()}.
-callback sendfile(socket(), file:name() | file:fd(), non_neg_integer(),
		non_neg_integer()) -> {ok, non_neg_integer()} | {error, atom()}.
-callback sendfile(socket(), file:name() | file:fd(), non_neg_integer(),
		non_neg_integer(), sendfile_opts())
	-> {ok, non_neg_integer()} | {error, atom()}.
-callback setopts(socket(), opts()) -> ok | {error, atom()}.
-callback controlling_process(socket(), pid())
	-> ok | {error, closed | not_owner | atom()}.
-callback peername(socket())
	-> {ok, {inet:ip_address(), inet:port_number()}} | {error, atom()}.
-callback sockname(socket())
	-> {ok, {inet:ip_address(), inet:port_number()}} | {error, atom()}.
-callback shutdown(socket(), read | write | read_write)
	-> ok | {error, atom()}.
-callback close(socket()) -> ok.

%% A fallback for transports that don't have a native sendfile implementation.
%% Note that the ordering of arguments is different from file:sendfile/5 and
%% that this function accepts either a raw file or a file name.
-spec sendfile(module(), socket(), file:filename_all() | file:fd(),
		non_neg_integer(), non_neg_integer(), sendfile_opts())
	-> {ok, non_neg_integer()} | {error, atom()}.
sendfile(Transport, Socket, Filename, Offset, Bytes, Opts)
		when is_list(Filename) orelse is_atom(Filename)
		orelse is_binary(Filename) ->
	ChunkSize = chunk_size(Opts),
	case file:open(Filename, [read, raw, binary]) of
		{ok, RawFile} ->
			_ = case Offset of
				0 ->
					ok;
				_ ->
					{ok, _} = file:position(RawFile, {bof, Offset})
			end,
			try
				sendfile_loop(Transport, Socket, RawFile, Bytes, 0, ChunkSize)
			after
				ok = file:close(RawFile)
			end;
		{error, _Reason} = Error ->
			Error
	end;
sendfile(Transport, Socket, RawFile, Offset, Bytes, Opts) ->
	ChunkSize = chunk_size(Opts),
	Initial2 = case file:position(RawFile, {cur, 0}) of
		{ok, Offset} ->
			Offset;
		{ok, Initial} ->
			{ok, _} = file:position(RawFile, {bof, Offset}),
			Initial
		end,
	case sendfile_loop(Transport, Socket, RawFile, Bytes, 0, ChunkSize) of
		{ok, _Sent} = Result ->
			{ok, _} = file:position(RawFile, {bof, Initial2}),
			Result;
		{error, _Reason} = Error ->
			Error
	end.

-spec chunk_size(sendfile_opts()) -> pos_integer().
chunk_size(Opts) ->
	case lists:keyfind(chunk_size, 1, Opts) of
		{chunk_size, ChunkSize}
				when is_integer(ChunkSize) andalso ChunkSize > 0 ->
			ChunkSize;
		{chunk_size, 0} ->
			16#1FFF;
		false ->
			16#1FFF
	end.

-spec sendfile_loop(module(), socket(), file:fd(), non_neg_integer(),
		non_neg_integer(), pos_integer())
	-> {ok, non_neg_integer()} | {error, term()}.
sendfile_loop(_Transport, _Socket, _RawFile, Sent, Sent, _ChunkSize)
		when Sent =/= 0 ->
	%% All requested data has been read and sent, return number of bytes sent.
	{ok, Sent};
sendfile_loop(Transport, Socket, RawFile, Bytes, Sent, ChunkSize) ->
	ReadSize = read_size(Bytes, Sent, ChunkSize),
	case file:read(RawFile, ReadSize) of
		{ok, IoData} ->
			case Transport:send(Socket, IoData) of
				ok ->
					Sent2 = iolist_size(IoData) + Sent,
					sendfile_loop(Transport, Socket, RawFile, Bytes, Sent2,
						ChunkSize);
				{error, _Reason} = Error ->
					Error
			end;
		eof ->
			{ok, Sent};
		{error, _Reason} = Error ->
			Error
	end.

-spec read_size(non_neg_integer(), non_neg_integer(), non_neg_integer()) ->
	non_neg_integer().
read_size(0, _Sent, ChunkSize) ->
	ChunkSize;
read_size(Bytes, Sent, ChunkSize) ->
	min(Bytes - Sent, ChunkSize).
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


























































































































































































































































































Deleted lib/antigone.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
defmodule Antigone do
  use Application

  def start(_type, _args) do
    import Supervisor.Spec, warn: false

    children = [
      worker(__MODULE__, [], function: :serve),
    ]

    opts = [strategy: :one_for_one, name: Antigone.Supervisor]
    Supervisor.start_link(children, opts)
  end

  def serve do
    {:ok,_} = Plug.Adapters.Cowboy.http Router, []
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




































Deleted lib/router.ex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
defmodule Router do
  use Plug.Builder

  if Mix.env == :dev do
    use Plug.Debugger
  end
 
  plug Plug.Static, at: "/", from: "www"
  plug :not_found

  def not_found(conn, _) do
    send_file(conn, 404, "www/404.html")
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<




























Deleted mix.exs.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
defmodule Antigone.Mixfile do
  use Mix.Project

  def project do
    [app: :antigone,
     version: "0.0.1",
     elixir: "~> 1.1",
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     deps: deps]
  end

  def application do
    [applications: [:logger, :cowboy, :plug],
     mod: {Antigone, []}]
  end

  defp deps do
    [
      {:cowboy, "~> 1.0.4"},
      {:plug,   "~> 1.0.3"}
    ]
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
















































Deleted mix.lock.

1
2
3
4
%{"cowboy": {:hex, :cowboy, "1.0.4"},
  "cowlib": {:hex, :cowlib, "1.0.2"},
  "plug": {:hex, :plug, "1.0.3"},
  "ranch": {:hex, :ranch, "1.2.0"}}
<
<
<
<








Deleted test/antigone_test.exs.

1
2
3
4
5
6
7
8
defmodule AntigoneTest do
  use ExUnit.Case
  doctest Antigone

  test "the truth" do
    assert 1 + 1 == 2
  end
end
<
<
<
<
<
<
<
<
















Deleted test/test_helper.exs.

1
ExUnit.start()
<


Deleted www/404.html.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html>
  <head>
    <title>Error: 404</title>
  </head>
  <body>
    <header>
      <h1>Error</h1>
    </header>
    <main>
      <h2>404: File Not Found</h2>
      <p>We could not find the content you requested.</p>
      <p>Please check your URL and try again.</p>
    </main>
    <footer>
      <p>This is an example file. Please change to suit your need</p>
    </footer>
  </body>
</html>
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<