transmission 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +0 -0
- data/LICENSE +23 -0
- data/README +50 -0
- data/Rakefile +41 -0
- data/ext/bencode.c +335 -0
- data/ext/bencode.h +64 -0
- data/ext/choking.c +335 -0
- data/ext/choking.h +30 -0
- data/ext/clients.c +90 -0
- data/ext/clients.h +25 -0
- data/ext/completion.c +266 -0
- data/ext/completion.h +67 -0
- data/ext/extconf.rb +3 -0
- data/ext/fastresume.h +375 -0
- data/ext/fdlimit.c +297 -0
- data/ext/fdlimit.h +36 -0
- data/ext/inout.c +620 -0
- data/ext/inout.h +38 -0
- data/ext/internal.h +202 -0
- data/ext/metainfo.c +339 -0
- data/ext/metainfo.h +44 -0
- data/ext/net.c +405 -0
- data/ext/net.h +54 -0
- data/ext/peer.c +584 -0
- data/ext/peer.h +55 -0
- data/ext/peermessages.h +326 -0
- data/ext/peerparse.h +564 -0
- data/ext/peerutils.h +394 -0
- data/ext/platform.c +196 -0
- data/ext/platform.h +63 -0
- data/ext/r_transmission.c +1135 -0
- data/ext/ratecontrol.c +170 -0
- data/ext/ratecontrol.h +33 -0
- data/ext/sha1.c +235 -0
- data/ext/sha1.h +68 -0
- data/ext/tracker.c +767 -0
- data/ext/tracker.h +53 -0
- data/ext/transmission.c +776 -0
- data/ext/transmission.h +317 -0
- data/ext/utils.c +142 -0
- data/ext/utils.h +164 -0
- data/lib/transmission.rb +75 -0
- metadata +98 -0
data/ext/transmission.h
ADDED
@@ -0,0 +1,317 @@
|
|
1
|
+
/******************************************************************************
|
2
|
+
* $Id: transmission.h 405 2006-06-20 02:34:34Z titer $
|
3
|
+
*
|
4
|
+
* Copyright (c) 2005-2006 Transmission authors and contributors
|
5
|
+
*
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining a
|
7
|
+
* copy of this software and associated documentation files (the "Software"),
|
8
|
+
* to deal in the Software without restriction, including without limitation
|
9
|
+
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
10
|
+
* and/or sell copies of the Software, and to permit persons to whom the
|
11
|
+
* Software is furnished to do so, subject to the following conditions:
|
12
|
+
*
|
13
|
+
* The above copyright notice and this permission notice shall be included in
|
14
|
+
* all copies or substantial portions of the Software.
|
15
|
+
*
|
16
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
22
|
+
* DEALINGS IN THE SOFTWARE.
|
23
|
+
*****************************************************************************/
|
24
|
+
|
25
|
+
#ifndef TR_TRANSMISSION_H
|
26
|
+
#define TR_TRANSMISSION_H 1
|
27
|
+
|
28
|
+
#ifdef __cplusplus
|
29
|
+
extern "C" {
|
30
|
+
#endif
|
31
|
+
|
32
|
+
/*#include "version.h"*/
|
33
|
+
#define VERSION_MAJOR 6
|
34
|
+
#define VERSION_MINOR 1
|
35
|
+
|
36
|
+
#include <inttypes.h>
|
37
|
+
#ifndef PRIu64
|
38
|
+
# define PRIu64 "lld"
|
39
|
+
#endif
|
40
|
+
|
41
|
+
#define SHA_DIGEST_LENGTH 20
|
42
|
+
#ifdef __BEOS__
|
43
|
+
# include <StorageDefs.h>
|
44
|
+
# define MAX_PATH_LENGTH B_FILE_NAME_LENGTH
|
45
|
+
#else
|
46
|
+
# define MAX_PATH_LENGTH 1024
|
47
|
+
#endif
|
48
|
+
|
49
|
+
#define TR_DEFAULT_PORT 9090
|
50
|
+
#define TR_NOERROR 0
|
51
|
+
|
52
|
+
/***********************************************************************
|
53
|
+
* tr_init
|
54
|
+
***********************************************************************
|
55
|
+
* Initializes a libtransmission instance. Returns a obscure handle to
|
56
|
+
* be passed to all functions below.
|
57
|
+
**********************************************************************/
|
58
|
+
typedef struct tr_handle_s tr_handle_t;
|
59
|
+
tr_handle_t * tr_init();
|
60
|
+
|
61
|
+
/***********************************************************************
|
62
|
+
* tr_getPrefsDirectory
|
63
|
+
***********************************************************************
|
64
|
+
* Returns the full path to a directory which can be used to store
|
65
|
+
* preferences. The string belongs to libtransmission, do not free it.
|
66
|
+
**********************************************************************/
|
67
|
+
char * tr_getPrefsDirectory();
|
68
|
+
|
69
|
+
/***********************************************************************
|
70
|
+
* tr_setBindPort
|
71
|
+
***********************************************************************
|
72
|
+
* Sets a "start" port: everytime we start a torrent, we try to bind
|
73
|
+
* this port, then the next one and so on until we are successful.
|
74
|
+
**********************************************************************/
|
75
|
+
void tr_setBindPort( tr_handle_t *, int );
|
76
|
+
|
77
|
+
/***********************************************************************
|
78
|
+
* tr_setUploadLimit
|
79
|
+
***********************************************************************
|
80
|
+
* Sets the total upload rate limit in KB/s
|
81
|
+
**********************************************************************/
|
82
|
+
void tr_setUploadLimit( tr_handle_t *, int );
|
83
|
+
|
84
|
+
/***********************************************************************
|
85
|
+
* tr_setDownloadLimit
|
86
|
+
***********************************************************************
|
87
|
+
* Sets the total download rate limit in KB/s
|
88
|
+
**********************************************************************/
|
89
|
+
void tr_setDownloadLimit( tr_handle_t *, int );
|
90
|
+
|
91
|
+
/***********************************************************************
|
92
|
+
* tr_torrentCount
|
93
|
+
***********************************************************************
|
94
|
+
* Returns the count of open torrents
|
95
|
+
**********************************************************************/
|
96
|
+
int tr_torrentCount( tr_handle_t * h );
|
97
|
+
|
98
|
+
/***********************************************************************
|
99
|
+
* tr_torrentIterate
|
100
|
+
***********************************************************************
|
101
|
+
* Iterates on open torrents
|
102
|
+
**********************************************************************/
|
103
|
+
typedef struct tr_torrent_s tr_torrent_t;
|
104
|
+
typedef void (*tr_callback_t) ( tr_torrent_t *, void * );
|
105
|
+
void tr_torrentIterate( tr_handle_t *, tr_callback_t, void * );
|
106
|
+
|
107
|
+
/***********************************************************************
|
108
|
+
* tr_torrentRates
|
109
|
+
***********************************************************************
|
110
|
+
* Gets the total download and upload rates
|
111
|
+
**********************************************************************/
|
112
|
+
void tr_torrentRates( tr_handle_t *, float *, float * );
|
113
|
+
|
114
|
+
/***********************************************************************
|
115
|
+
* tr_close
|
116
|
+
***********************************************************************
|
117
|
+
* Frees memory allocated by tr_init.
|
118
|
+
**********************************************************************/
|
119
|
+
void tr_close( tr_handle_t * );
|
120
|
+
|
121
|
+
/***********************************************************************
|
122
|
+
* tr_torrentInit
|
123
|
+
***********************************************************************
|
124
|
+
* Opens and parses torrent file at 'path'. If the file exists and is a
|
125
|
+
* valid torrent file, returns an handle and adds it to the list of
|
126
|
+
* torrents (but doesn't start it). Returns NULL and sets *error
|
127
|
+
* otherwise. If the TR_FSAVEPRIVATE flag is passed then a private copy
|
128
|
+
* of the torrent file will be saved.
|
129
|
+
**********************************************************************/
|
130
|
+
#define TR_EINVALID 1
|
131
|
+
#define TR_EUNSUPPORTED 2
|
132
|
+
#define TR_EDUPLICATE 3
|
133
|
+
#define TR_EOTHER 666
|
134
|
+
tr_torrent_t * tr_torrentInit( tr_handle_t *, const char * path,
|
135
|
+
int flags, int * error );
|
136
|
+
|
137
|
+
/***********************************************************************
|
138
|
+
* tr_torrentInitSaved
|
139
|
+
***********************************************************************
|
140
|
+
* Opens and parses a torrent file as with tr_torrentInit, only taking
|
141
|
+
* the hash string of a saved torrent file instead of a filename. There
|
142
|
+
* are currently no valid flags for this function.
|
143
|
+
**********************************************************************/
|
144
|
+
tr_torrent_t * tr_torrentInitSaved( tr_handle_t *, const char * hashStr,
|
145
|
+
int flags, int * error );
|
146
|
+
|
147
|
+
typedef struct tr_info_s tr_info_t;
|
148
|
+
tr_info_t * tr_torrentInfo( tr_torrent_t * );
|
149
|
+
|
150
|
+
/***********************************************************************
|
151
|
+
* tr_torrentScrape
|
152
|
+
***********************************************************************
|
153
|
+
* Asks the tracker for the count of seeders and leechers. Returns 0
|
154
|
+
* and fills 's' and 'l' if successful. Otherwise returns 1 if the
|
155
|
+
* tracker doesn't support the scrape protocol, is unreachable or
|
156
|
+
* replied with some error. tr_torrentScrape may block up to 20 seconds
|
157
|
+
* before returning.
|
158
|
+
**********************************************************************/
|
159
|
+
#define TR_SCRAPE_ERROR 1
|
160
|
+
#define TR_SCRAPE_RESOLVE 2
|
161
|
+
#define TR_SCRAPE_CONNECT 3
|
162
|
+
#define TR_SCRAPE_READ 4
|
163
|
+
int tr_torrentScrape( tr_torrent_t *, int * s, int * l );
|
164
|
+
|
165
|
+
/***********************************************************************
|
166
|
+
* tr_torrentStart
|
167
|
+
***********************************************************************
|
168
|
+
* Starts downloading. The download is launched in a seperate thread,
|
169
|
+
* therefore tr_torrentStart returns immediately.
|
170
|
+
**********************************************************************/
|
171
|
+
void tr_torrentSetFolder( tr_torrent_t *, const char * );
|
172
|
+
char * tr_torrentGetFolder( tr_torrent_t * );
|
173
|
+
void tr_torrentStart( tr_torrent_t * );
|
174
|
+
|
175
|
+
/***********************************************************************
|
176
|
+
* tr_torrentStop
|
177
|
+
***********************************************************************
|
178
|
+
* Stops downloading and notices the tracker that we are leaving. The
|
179
|
+
* thread keeps running while doing so.
|
180
|
+
* The thread will eventually be joined, either:
|
181
|
+
* - by tr_torrentStat when the tracker has been successfully noticed,
|
182
|
+
* - by tr_torrentStat if the tracker could not be noticed within 60s,
|
183
|
+
* - by tr_torrentClose if you choose to remove the torrent without
|
184
|
+
* waiting any further.
|
185
|
+
**********************************************************************/
|
186
|
+
void tr_torrentStop( tr_torrent_t * );
|
187
|
+
|
188
|
+
/***********************************************************************
|
189
|
+
* tr_getFinished
|
190
|
+
***********************************************************************
|
191
|
+
* The first call after a torrent is completed returns 1. Returns 0
|
192
|
+
* in other cases.
|
193
|
+
**********************************************************************/
|
194
|
+
int tr_getFinished( tr_torrent_t * );
|
195
|
+
|
196
|
+
/***********************************************************************
|
197
|
+
* tr_torrentStat
|
198
|
+
***********************************************************************
|
199
|
+
* Returns a pointer to an tr_stat_t structure with updated information
|
200
|
+
* on the torrent. The structure belongs to libtransmission (do not
|
201
|
+
* free it) and is guaranteed to be unchanged until the next call to
|
202
|
+
* tr_torrentStat.
|
203
|
+
* The interface should call this function every second or so in order
|
204
|
+
* to update itself.
|
205
|
+
**********************************************************************/
|
206
|
+
typedef struct tr_stat_s tr_stat_t;
|
207
|
+
tr_stat_t * tr_torrentStat( tr_torrent_t * );
|
208
|
+
|
209
|
+
/***********************************************************************
|
210
|
+
* tr_torrentAvailability
|
211
|
+
***********************************************************************
|
212
|
+
* Use this to draw an advanced progress bar which is 'size' pixels
|
213
|
+
* wide. Fills 'tab' which you must have allocated: each byte is set
|
214
|
+
* to either -1 if we have the piece, otherwise it is set to the number
|
215
|
+
* of connected peers who have the piece.
|
216
|
+
**********************************************************************/
|
217
|
+
void tr_torrentAvailability( tr_torrent_t *, int8_t * tab, int size );
|
218
|
+
|
219
|
+
/***********************************************************************
|
220
|
+
* tr_torrentRemoveSaved
|
221
|
+
***********************************************************************
|
222
|
+
* Removes the private saved copy of a torrent file for torrents which
|
223
|
+
* the TR_FSAVEPRIVATE flag is set.
|
224
|
+
**********************************************************************/
|
225
|
+
void tr_torrentRemoveSaved( tr_torrent_t * );
|
226
|
+
|
227
|
+
/***********************************************************************
|
228
|
+
* tr_torrentClose
|
229
|
+
***********************************************************************
|
230
|
+
* Frees memory allocated by tr_torrentInit. If the torrent was running,
|
231
|
+
* you must call tr_torrentStop() before closing it.
|
232
|
+
**********************************************************************/
|
233
|
+
void tr_torrentClose( tr_handle_t *, tr_torrent_t * );
|
234
|
+
|
235
|
+
/***********************************************************************
|
236
|
+
* tr_info_s
|
237
|
+
**********************************************************************/
|
238
|
+
typedef struct tr_file_s
|
239
|
+
{
|
240
|
+
uint64_t length; /* Length of the file, in bytes */
|
241
|
+
char name[MAX_PATH_LENGTH]; /* Path to the file */
|
242
|
+
}
|
243
|
+
tr_file_t;
|
244
|
+
struct tr_info_s
|
245
|
+
{
|
246
|
+
/* Path to torrent */
|
247
|
+
char torrent[MAX_PATH_LENGTH];
|
248
|
+
|
249
|
+
/* General info */
|
250
|
+
uint8_t hash[SHA_DIGEST_LENGTH];
|
251
|
+
char hashString[2*SHA_DIGEST_LENGTH+1];
|
252
|
+
char name[MAX_PATH_LENGTH];
|
253
|
+
|
254
|
+
/* Flags */
|
255
|
+
#define TR_FSAVEPRIVATE 0x01 /* save a private copy of the torrent */
|
256
|
+
int flags;
|
257
|
+
|
258
|
+
/* Tracker info */
|
259
|
+
char trackerAddress[256];
|
260
|
+
int trackerPort;
|
261
|
+
char trackerAnnounce[MAX_PATH_LENGTH];
|
262
|
+
|
263
|
+
/* Pieces info */
|
264
|
+
int pieceSize;
|
265
|
+
int pieceCount;
|
266
|
+
uint64_t totalSize;
|
267
|
+
uint8_t * pieces;
|
268
|
+
|
269
|
+
/* Files info */
|
270
|
+
int multifile;
|
271
|
+
int fileCount;
|
272
|
+
tr_file_t * files;
|
273
|
+
};
|
274
|
+
|
275
|
+
/***********************************************************************
|
276
|
+
* tr_stat_s
|
277
|
+
**********************************************************************/
|
278
|
+
struct tr_stat_s
|
279
|
+
{
|
280
|
+
#define TR_STATUS_CHECK 0x001 /* Checking files */
|
281
|
+
#define TR_STATUS_DOWNLOAD 0x002 /* Downloading */
|
282
|
+
#define TR_STATUS_SEED 0x004 /* Seeding */
|
283
|
+
#define TR_STATUS_STOPPING 0x008 /* Sending 'stopped' to the tracker */
|
284
|
+
#define TR_STATUS_STOPPED 0x010 /* Sent 'stopped' but thread still
|
285
|
+
running (for internal use only) */
|
286
|
+
#define TR_STATUS_PAUSE 0x020 /* Paused */
|
287
|
+
|
288
|
+
#define TR_STATUS_ACTIVE (TR_STATUS_CHECK|TR_STATUS_DOWNLOAD|TR_STATUS_SEED)
|
289
|
+
#define TR_STATUS_INACTIVE (TR_STATUS_STOPPING|TR_STATUS_STOPPED|TR_STATUS_PAUSE)
|
290
|
+
int status;
|
291
|
+
|
292
|
+
#define TR_ETRACKER 1
|
293
|
+
#define TR_EINOUT 2
|
294
|
+
int error;
|
295
|
+
char trackerError[128];
|
296
|
+
|
297
|
+
float progress;
|
298
|
+
float rateDownload;
|
299
|
+
float rateUpload;
|
300
|
+
int eta;
|
301
|
+
int peersTotal;
|
302
|
+
int peersUploading;
|
303
|
+
int peersDownloading;
|
304
|
+
int seeders;
|
305
|
+
int leechers;
|
306
|
+
|
307
|
+
uint64_t downloaded;
|
308
|
+
uint64_t uploaded;
|
309
|
+
};
|
310
|
+
|
311
|
+
#include "internal.h"
|
312
|
+
|
313
|
+
#ifdef __cplusplus
|
314
|
+
}
|
315
|
+
#endif
|
316
|
+
|
317
|
+
#endif
|
data/ext/utils.c
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
/******************************************************************************
|
2
|
+
* $Id: utils.c 310 2006-06-09 19:53:35Z joshe $
|
3
|
+
*
|
4
|
+
* Copyright (c) 2005-2006 Transmission authors and contributors
|
5
|
+
*
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining a
|
7
|
+
* copy of this software and associated documentation files (the "Software"),
|
8
|
+
* to deal in the Software without restriction, including without limitation
|
9
|
+
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
10
|
+
* and/or sell copies of the Software, and to permit persons to whom the
|
11
|
+
* Software is furnished to do so, subject to the following conditions:
|
12
|
+
*
|
13
|
+
* The above copyright notice and this permission notice shall be included in
|
14
|
+
* all copies or substantial portions of the Software.
|
15
|
+
*
|
16
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
22
|
+
* DEALINGS IN THE SOFTWARE.
|
23
|
+
*****************************************************************************/
|
24
|
+
|
25
|
+
#include "transmission.h"
|
26
|
+
|
27
|
+
void tr_msg( int level, char * msg, ... )
|
28
|
+
{
|
29
|
+
char string[256];
|
30
|
+
va_list args;
|
31
|
+
static int verboseLevel = 0;
|
32
|
+
|
33
|
+
if( !verboseLevel )
|
34
|
+
{
|
35
|
+
char * env;
|
36
|
+
env = getenv( "TR_DEBUG" );
|
37
|
+
verboseLevel = env ? atoi( env ) : -1;
|
38
|
+
verboseLevel = verboseLevel ? verboseLevel : -1;
|
39
|
+
}
|
40
|
+
|
41
|
+
if( verboseLevel < level )
|
42
|
+
{
|
43
|
+
return;
|
44
|
+
}
|
45
|
+
|
46
|
+
va_start( args, msg );
|
47
|
+
vsnprintf( string, sizeof( string ), msg, args );
|
48
|
+
va_end( args );
|
49
|
+
fprintf( stderr, "%s\n", string );
|
50
|
+
}
|
51
|
+
|
52
|
+
int tr_rand( int sup )
|
53
|
+
{
|
54
|
+
static int init = 0;
|
55
|
+
if( !init )
|
56
|
+
{
|
57
|
+
srand( tr_date() );
|
58
|
+
init = 1;
|
59
|
+
}
|
60
|
+
return rand() % sup;
|
61
|
+
}
|
62
|
+
|
63
|
+
void * tr_memmem( const void *vbig, size_t big_len,
|
64
|
+
const void *vlittle, size_t little_len )
|
65
|
+
{
|
66
|
+
const char *big = vbig;
|
67
|
+
const char *little = vlittle;
|
68
|
+
size_t ii, jj;
|
69
|
+
|
70
|
+
if( 0 == big_len || 0 == little_len )
|
71
|
+
{
|
72
|
+
return NULL;
|
73
|
+
}
|
74
|
+
|
75
|
+
for( ii = 0; ii + little_len <= big_len; ii++ )
|
76
|
+
{
|
77
|
+
for( jj = 0; jj < little_len; jj++ )
|
78
|
+
{
|
79
|
+
if( big[ii + jj] != little[jj] )
|
80
|
+
{
|
81
|
+
break;
|
82
|
+
}
|
83
|
+
}
|
84
|
+
if( jj == little_len )
|
85
|
+
{
|
86
|
+
return (char*)big + ii;
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
90
|
+
return NULL;
|
91
|
+
}
|
92
|
+
|
93
|
+
int tr_mkdir( char * path )
|
94
|
+
{
|
95
|
+
char * p, * pp;
|
96
|
+
struct stat sb;
|
97
|
+
int done;
|
98
|
+
|
99
|
+
p = path;
|
100
|
+
while( '/' == *p )
|
101
|
+
p++;
|
102
|
+
pp = p;
|
103
|
+
done = 0;
|
104
|
+
while( ( p = strchr( pp, '/' ) ) || ( p = strchr( pp, '\0' ) ) )
|
105
|
+
{
|
106
|
+
if( '\0' == *p)
|
107
|
+
{
|
108
|
+
done = 1;
|
109
|
+
}
|
110
|
+
else
|
111
|
+
{
|
112
|
+
*p = '\0';
|
113
|
+
}
|
114
|
+
if( stat( path, &sb ) )
|
115
|
+
{
|
116
|
+
/* Folder doesn't exist yet */
|
117
|
+
if( mkdir( path, 0777 ) )
|
118
|
+
{
|
119
|
+
tr_err( "Could not create directory %s (%s)", path,
|
120
|
+
strerror( errno ) );
|
121
|
+
*p = '/';
|
122
|
+
return 1;
|
123
|
+
}
|
124
|
+
}
|
125
|
+
else if( ( sb.st_mode & S_IFMT ) != S_IFDIR )
|
126
|
+
{
|
127
|
+
/* Node exists but isn't a folder */
|
128
|
+
tr_err( "Remove %s, it's in the way.", path );
|
129
|
+
*p = '/';
|
130
|
+
return 1;
|
131
|
+
}
|
132
|
+
if( done )
|
133
|
+
{
|
134
|
+
break;
|
135
|
+
}
|
136
|
+
*p = '/';
|
137
|
+
p++;
|
138
|
+
pp = p;
|
139
|
+
}
|
140
|
+
|
141
|
+
return 0;
|
142
|
+
}
|