##// END OF EJS Templates
specify C indention style using Emacs file local variables
Martin Geisler -
r13297:613b8bd2 default
parent child Browse files
Show More
@@ -1,440 +1,445 b''
1 /*
1 /*
2 * hgsh.c - restricted login shell for mercurial
2 * hgsh.c - restricted login shell for mercurial
3 *
3 *
4 * Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 * Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 *
5 *
6 * This software may be used and distributed according to the terms of the
6 * This software may be used and distributed according to the terms of the
7 * GNU General Public License, incorporated herein by reference.
7 * GNU General Public License, incorporated herein by reference.
8 *
8 *
9 * this program is login shell for dedicated mercurial user account. it
9 * this program is login shell for dedicated mercurial user account. it
10 * only allows few actions:
10 * only allows few actions:
11 *
11 *
12 * 1. run hg in server mode on specific repository. no other hg commands
12 * 1. run hg in server mode on specific repository. no other hg commands
13 * are allowed. we try to verify that repo to be accessed exists under
13 * are allowed. we try to verify that repo to be accessed exists under
14 * given top-level directory.
14 * given top-level directory.
15 *
15 *
16 * 2. (optional) forward ssh connection from firewall/gateway machine to
16 * 2. (optional) forward ssh connection from firewall/gateway machine to
17 * "real" mercurial host, to let users outside intranet pull and push
17 * "real" mercurial host, to let users outside intranet pull and push
18 * changes through firewall.
18 * changes through firewall.
19 *
19 *
20 * 3. (optional) run normal shell, to allow to "su" to mercurial user, use
20 * 3. (optional) run normal shell, to allow to "su" to mercurial user, use
21 * "sudo" to run programs as that user, or run cron jobs as that user.
21 * "sudo" to run programs as that user, or run cron jobs as that user.
22 *
22 *
23 * only tested on linux yet. patches for non-linux systems welcome.
23 * only tested on linux yet. patches for non-linux systems welcome.
24 */
24 */
25
25
26 #ifndef _GNU_SOURCE
26 #ifndef _GNU_SOURCE
27 #define _GNU_SOURCE /* for asprintf */
27 #define _GNU_SOURCE /* for asprintf */
28 #endif
28 #endif
29
29
30 #include <stdio.h>
30 #include <stdio.h>
31 #include <stdlib.h>
31 #include <stdlib.h>
32 #include <string.h>
32 #include <string.h>
33 #include <sys/stat.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
34 #include <sys/types.h>
35 #include <sysexits.h>
35 #include <sysexits.h>
36 #include <unistd.h>
36 #include <unistd.h>
37
37
38 /*
38 /*
39 * user config.
39 * user config.
40 *
40 *
41 * if you see a hostname below, just use first part of hostname. example,
41 * if you see a hostname below, just use first part of hostname. example,
42 * if you have host named foo.bar.com, use "foo".
42 * if you have host named foo.bar.com, use "foo".
43 */
43 */
44
44
45 /*
45 /*
46 * HG_GATEWAY: hostname of gateway/firewall machine that people outside your
46 * HG_GATEWAY: hostname of gateway/firewall machine that people outside your
47 * intranet ssh into if they need to ssh to other machines. if you do not
47 * intranet ssh into if they need to ssh to other machines. if you do not
48 * have such machine, set to NULL.
48 * have such machine, set to NULL.
49 */
49 */
50 #ifndef HG_GATEWAY
50 #ifndef HG_GATEWAY
51 #define HG_GATEWAY "gateway"
51 #define HG_GATEWAY "gateway"
52 #endif
52 #endif
53
53
54 /*
54 /*
55 * HG_HOST: hostname of mercurial server. if any machine is allowed, set to
55 * HG_HOST: hostname of mercurial server. if any machine is allowed, set to
56 * NULL.
56 * NULL.
57 */
57 */
58 #ifndef HG_HOST
58 #ifndef HG_HOST
59 #define HG_HOST "mercurial"
59 #define HG_HOST "mercurial"
60 #endif
60 #endif
61
61
62 /*
62 /*
63 * HG_USER: username to log in from HG_GATEWAY to HG_HOST. if gateway and
63 * HG_USER: username to log in from HG_GATEWAY to HG_HOST. if gateway and
64 * host username are same, set to NULL.
64 * host username are same, set to NULL.
65 */
65 */
66 #ifndef HG_USER
66 #ifndef HG_USER
67 #define HG_USER "hg"
67 #define HG_USER "hg"
68 #endif
68 #endif
69
69
70 /*
70 /*
71 * HG_ROOT: root of tree full of mercurial repos. if you do not want to
71 * HG_ROOT: root of tree full of mercurial repos. if you do not want to
72 * validate location of repo when someone is try to access, set to NULL.
72 * validate location of repo when someone is try to access, set to NULL.
73 */
73 */
74 #ifndef HG_ROOT
74 #ifndef HG_ROOT
75 #define HG_ROOT "/home/hg/repos"
75 #define HG_ROOT "/home/hg/repos"
76 #endif
76 #endif
77
77
78 /*
78 /*
79 * HG: path to the mercurial executable to run.
79 * HG: path to the mercurial executable to run.
80 */
80 */
81 #ifndef HG
81 #ifndef HG
82 #define HG "/home/hg/bin/hg"
82 #define HG "/home/hg/bin/hg"
83 #endif
83 #endif
84
84
85 /*
85 /*
86 * HG_SHELL: shell to use for actions like "sudo" and "su" access to
86 * HG_SHELL: shell to use for actions like "sudo" and "su" access to
87 * mercurial user, and cron jobs. if you want to make these things
87 * mercurial user, and cron jobs. if you want to make these things
88 * impossible, set to NULL.
88 * impossible, set to NULL.
89 */
89 */
90 #ifndef HG_SHELL
90 #ifndef HG_SHELL
91 #define HG_SHELL NULL
91 #define HG_SHELL NULL
92 /* #define HG_SHELL "/bin/bash" */
92 /* #define HG_SHELL "/bin/bash" */
93 #endif
93 #endif
94
94
95 /*
95 /*
96 * HG_HELP: some way for users to get support if they have problem. if they
96 * HG_HELP: some way for users to get support if they have problem. if they
97 * should not get helpful message, set to NULL.
97 * should not get helpful message, set to NULL.
98 */
98 */
99 #ifndef HG_HELP
99 #ifndef HG_HELP
100 #define HG_HELP "please contact support@example.com for help."
100 #define HG_HELP "please contact support@example.com for help."
101 #endif
101 #endif
102
102
103 /*
103 /*
104 * SSH: path to ssh executable to run, if forwarding from HG_GATEWAY to
104 * SSH: path to ssh executable to run, if forwarding from HG_GATEWAY to
105 * HG_HOST. if you want to use rsh instead (why?), you need to modify
105 * HG_HOST. if you want to use rsh instead (why?), you need to modify
106 * arguments it is called with. see forward_through_gateway.
106 * arguments it is called with. see forward_through_gateway.
107 */
107 */
108 #ifndef SSH
108 #ifndef SSH
109 #define SSH "/usr/bin/ssh"
109 #define SSH "/usr/bin/ssh"
110 #endif
110 #endif
111
111
112 /*
112 /*
113 * tell whether to print command that is to be executed. useful for
113 * tell whether to print command that is to be executed. useful for
114 * debugging. should not interfere with mercurial operation, since
114 * debugging. should not interfere with mercurial operation, since
115 * mercurial only cares about stdin and stdout, and this prints to stderr.
115 * mercurial only cares about stdin and stdout, and this prints to stderr.
116 */
116 */
117 static const int debug = 0;
117 static const int debug = 0;
118
118
119 static void print_cmdline(int argc, char **argv)
119 static void print_cmdline(int argc, char **argv)
120 {
120 {
121 FILE *fp = stderr;
121 FILE *fp = stderr;
122 int i;
122 int i;
123
123
124 fputs("command: ", fp);
124 fputs("command: ", fp);
125
125
126 for (i = 0; i < argc; i++) {
126 for (i = 0; i < argc; i++) {
127 char *spc = strpbrk(argv[i], " \t\r\n");
127 char *spc = strpbrk(argv[i], " \t\r\n");
128 if (spc) {
128 if (spc) {
129 fputc('\'', fp);
129 fputc('\'', fp);
130 }
130 }
131 fputs(argv[i], fp);
131 fputs(argv[i], fp);
132 if (spc) {
132 if (spc) {
133 fputc('\'', fp);
133 fputc('\'', fp);
134 }
134 }
135 if (i < argc - 1) {
135 if (i < argc - 1) {
136 fputc(' ', fp);
136 fputc(' ', fp);
137 }
137 }
138 }
138 }
139 fputc('\n', fp);
139 fputc('\n', fp);
140 fflush(fp);
140 fflush(fp);
141 }
141 }
142
142
143 static void usage(const char *reason, int exitcode)
143 static void usage(const char *reason, int exitcode)
144 {
144 {
145 char *hg_help = HG_HELP;
145 char *hg_help = HG_HELP;
146
146
147 if (reason) {
147 if (reason) {
148 fprintf(stderr, "*** Error: %s.\n", reason);
148 fprintf(stderr, "*** Error: %s.\n", reason);
149 }
149 }
150 fprintf(stderr, "*** This program has been invoked incorrectly.\n");
150 fprintf(stderr, "*** This program has been invoked incorrectly.\n");
151 if (hg_help) {
151 if (hg_help) {
152 fprintf(stderr, "*** %s\n", hg_help);
152 fprintf(stderr, "*** %s\n", hg_help);
153 }
153 }
154 exit(exitcode ? exitcode : EX_USAGE);
154 exit(exitcode ? exitcode : EX_USAGE);
155 }
155 }
156
156
157 /*
157 /*
158 * run on gateway host to make another ssh connection, to "real" mercurial
158 * run on gateway host to make another ssh connection, to "real" mercurial
159 * server. it sends its command line unmodified to far end.
159 * server. it sends its command line unmodified to far end.
160 *
160 *
161 * never called if HG_GATEWAY is NULL.
161 * never called if HG_GATEWAY is NULL.
162 */
162 */
163 static void forward_through_gateway(int argc, char **argv)
163 static void forward_through_gateway(int argc, char **argv)
164 {
164 {
165 char *ssh = SSH;
165 char *ssh = SSH;
166 char *hg_host = HG_HOST;
166 char *hg_host = HG_HOST;
167 char *hg_user = HG_USER;
167 char *hg_user = HG_USER;
168 char **nargv = alloca((10 + argc) * sizeof(char *));
168 char **nargv = alloca((10 + argc) * sizeof(char *));
169 int i = 0, j;
169 int i = 0, j;
170
170
171 nargv[i++] = ssh;
171 nargv[i++] = ssh;
172 nargv[i++] = "-q";
172 nargv[i++] = "-q";
173 nargv[i++] = "-T";
173 nargv[i++] = "-T";
174 nargv[i++] = "-x";
174 nargv[i++] = "-x";
175 if (hg_user) {
175 if (hg_user) {
176 nargv[i++] = "-l";
176 nargv[i++] = "-l";
177 nargv[i++] = hg_user;
177 nargv[i++] = hg_user;
178 }
178 }
179 nargv[i++] = hg_host;
179 nargv[i++] = hg_host;
180
180
181 /*
181 /*
182 * sshd called us with added "-c", because it thinks we are a shell.
182 * sshd called us with added "-c", because it thinks we are a shell.
183 * drop it if we find it.
183 * drop it if we find it.
184 */
184 */
185 j = 1;
185 j = 1;
186 if (j < argc && strcmp(argv[j], "-c") == 0) {
186 if (j < argc && strcmp(argv[j], "-c") == 0) {
187 j++;
187 j++;
188 }
188 }
189
189
190 for (; j < argc; i++, j++) {
190 for (; j < argc; i++, j++) {
191 nargv[i] = argv[j];
191 nargv[i] = argv[j];
192 }
192 }
193 nargv[i] = NULL;
193 nargv[i] = NULL;
194
194
195 if (debug) {
195 if (debug) {
196 print_cmdline(i, nargv);
196 print_cmdline(i, nargv);
197 }
197 }
198
198
199 execv(ssh, nargv);
199 execv(ssh, nargv);
200 perror(ssh);
200 perror(ssh);
201 exit(EX_UNAVAILABLE);
201 exit(EX_UNAVAILABLE);
202 }
202 }
203
203
204 /*
204 /*
205 * run shell. let administrator "su" to mercurial user's account to do
205 * run shell. let administrator "su" to mercurial user's account to do
206 * administrative works.
206 * administrative works.
207 *
207 *
208 * never called if HG_SHELL is NULL.
208 * never called if HG_SHELL is NULL.
209 */
209 */
210 static void run_shell(int argc, char **argv)
210 static void run_shell(int argc, char **argv)
211 {
211 {
212 char *hg_shell = HG_SHELL;
212 char *hg_shell = HG_SHELL;
213 char **nargv;
213 char **nargv;
214 char *c;
214 char *c;
215 int i;
215 int i;
216
216
217 nargv = alloca((argc + 3) * sizeof(char *));
217 nargv = alloca((argc + 3) * sizeof(char *));
218 c = strrchr(hg_shell, '/');
218 c = strrchr(hg_shell, '/');
219
219
220 /* tell "real" shell it is login shell, if needed. */
220 /* tell "real" shell it is login shell, if needed. */
221
221
222 if (argv[0][0] == '-' && c) {
222 if (argv[0][0] == '-' && c) {
223 nargv[0] = strdup(c);
223 nargv[0] = strdup(c);
224 if (nargv[0] == NULL) {
224 if (nargv[0] == NULL) {
225 perror("malloc");
225 perror("malloc");
226 exit(EX_OSERR);
226 exit(EX_OSERR);
227 }
227 }
228 nargv[0][0] = '-';
228 nargv[0][0] = '-';
229 } else {
229 } else {
230 nargv[0] = hg_shell;
230 nargv[0] = hg_shell;
231 }
231 }
232
232
233 for (i = 1; i < argc; i++) {
233 for (i = 1; i < argc; i++) {
234 nargv[i] = argv[i];
234 nargv[i] = argv[i];
235 }
235 }
236 nargv[i] = NULL;
236 nargv[i] = NULL;
237
237
238 if (debug) {
238 if (debug) {
239 print_cmdline(i, nargv);
239 print_cmdline(i, nargv);
240 }
240 }
241
241
242 execv(hg_shell, nargv);
242 execv(hg_shell, nargv);
243 perror(hg_shell);
243 perror(hg_shell);
244 exit(EX_OSFILE);
244 exit(EX_OSFILE);
245 }
245 }
246
246
247 enum cmdline {
247 enum cmdline {
248 hg_init,
248 hg_init,
249 hg_serve,
249 hg_serve,
250 };
250 };
251
251
252
252
253 /*
253 /*
254 * attempt to verify that a directory is really a hg repo, by testing
254 * attempt to verify that a directory is really a hg repo, by testing
255 * for the existence of a subdirectory.
255 * for the existence of a subdirectory.
256 */
256 */
257 static int validate_repo(const char *repo_root, const char *subdir)
257 static int validate_repo(const char *repo_root, const char *subdir)
258 {
258 {
259 char *abs_path;
259 char *abs_path;
260 struct stat st;
260 struct stat st;
261 int ret;
261 int ret;
262
262
263 if (asprintf(&abs_path, "%s.hg/%s", repo_root, subdir) == -1) {
263 if (asprintf(&abs_path, "%s.hg/%s", repo_root, subdir) == -1) {
264 ret = -1;
264 ret = -1;
265 goto bail;
265 goto bail;
266 }
266 }
267
267
268 /* verify that we really are looking at valid repo. */
268 /* verify that we really are looking at valid repo. */
269
269
270 if (stat(abs_path, &st) == -1) {
270 if (stat(abs_path, &st) == -1) {
271 ret = 0;
271 ret = 0;
272 } else {
272 } else {
273 ret = 1;
273 ret = 1;
274 }
274 }
275
275
276 bail:
276 bail:
277 return ret;
277 return ret;
278 }
278 }
279
279
280 /*
280 /*
281 * paranoid wrapper, runs hg executable in server mode.
281 * paranoid wrapper, runs hg executable in server mode.
282 */
282 */
283 static void serve_data(int argc, char **argv)
283 static void serve_data(int argc, char **argv)
284 {
284 {
285 char *hg_root = HG_ROOT;
285 char *hg_root = HG_ROOT;
286 char *repo, *repo_root;
286 char *repo, *repo_root;
287 enum cmdline cmd;
287 enum cmdline cmd;
288 char *nargv[6];
288 char *nargv[6];
289 size_t repolen;
289 size_t repolen;
290 int i;
290 int i;
291
291
292 /*
292 /*
293 * check argv for looking okay. we should be invoked with argv
293 * check argv for looking okay. we should be invoked with argv
294 * resembling like this:
294 * resembling like this:
295 *
295 *
296 * hgsh
296 * hgsh
297 * -c
297 * -c
298 * hg -R some/path serve --stdio
298 * hg -R some/path serve --stdio
299 *
299 *
300 * the "-c" is added by sshd, because it thinks we are login shell.
300 * the "-c" is added by sshd, because it thinks we are login shell.
301 */
301 */
302
302
303 if (argc != 3) {
303 if (argc != 3) {
304 goto badargs;
304 goto badargs;
305 }
305 }
306
306
307 if (strcmp(argv[1], "-c") != 0) {
307 if (strcmp(argv[1], "-c") != 0) {
308 goto badargs;
308 goto badargs;
309 }
309 }
310
310
311 if (sscanf(argv[2], "hg init %as", &repo) == 1) {
311 if (sscanf(argv[2], "hg init %as", &repo) == 1) {
312 cmd = hg_init;
312 cmd = hg_init;
313 }
313 }
314 else if (sscanf(argv[2], "hg -R %as serve --stdio", &repo) == 1) {
314 else if (sscanf(argv[2], "hg -R %as serve --stdio", &repo) == 1) {
315 cmd = hg_serve;
315 cmd = hg_serve;
316 } else {
316 } else {
317 goto badargs;
317 goto badargs;
318 }
318 }
319
319
320 repolen = repo ? strlen(repo) : 0;
320 repolen = repo ? strlen(repo) : 0;
321
321
322 if (repolen == 0) {
322 if (repolen == 0) {
323 goto badargs;
323 goto badargs;
324 }
324 }
325
325
326 if (hg_root) {
326 if (hg_root) {
327 if (asprintf(&repo_root, "%s/%s/", hg_root, repo) == -1) {
327 if (asprintf(&repo_root, "%s/%s/", hg_root, repo) == -1) {
328 goto badargs;
328 goto badargs;
329 }
329 }
330
330
331 /*
331 /*
332 * attempt to stop break out from inside the
332 * attempt to stop break out from inside the
333 * repository tree. could do something more clever
333 * repository tree. could do something more clever
334 * here, because e.g. we could traverse a symlink that
334 * here, because e.g. we could traverse a symlink that
335 * looks safe, but really breaks us out of tree.
335 * looks safe, but really breaks us out of tree.
336 */
336 */
337
337
338 if (strstr(repo_root, "/../") != NULL) {
338 if (strstr(repo_root, "/../") != NULL) {
339 goto badargs;
339 goto badargs;
340 }
340 }
341
341
342 /* only hg init expects no repo. */
342 /* only hg init expects no repo. */
343
343
344 if (cmd != hg_init) {
344 if (cmd != hg_init) {
345 int valid;
345 int valid;
346
346
347 valid = validate_repo(repo_root, "data");
347 valid = validate_repo(repo_root, "data");
348
348
349 if (valid == -1) {
349 if (valid == -1) {
350 goto badargs;
350 goto badargs;
351 }
351 }
352
352
353 if (valid == 0) {
353 if (valid == 0) {
354 valid = validate_repo(repo_root, "store");
354 valid = validate_repo(repo_root, "store");
355
355
356 if (valid == -1) {
356 if (valid == -1) {
357 goto badargs;
357 goto badargs;
358 }
358 }
359 }
359 }
360
360
361 if (valid == 0) {
361 if (valid == 0) {
362 perror(repo);
362 perror(repo);
363 exit(EX_DATAERR);
363 exit(EX_DATAERR);
364 }
364 }
365 }
365 }
366
366
367 if (chdir(hg_root) == -1) {
367 if (chdir(hg_root) == -1) {
368 perror(hg_root);
368 perror(hg_root);
369 exit(EX_SOFTWARE);
369 exit(EX_SOFTWARE);
370 }
370 }
371 }
371 }
372
372
373 i = 0;
373 i = 0;
374
374
375 switch (cmd) {
375 switch (cmd) {
376 case hg_serve:
376 case hg_serve:
377 nargv[i++] = HG;
377 nargv[i++] = HG;
378 nargv[i++] = "-R";
378 nargv[i++] = "-R";
379 nargv[i++] = repo;
379 nargv[i++] = repo;
380 nargv[i++] = "serve";
380 nargv[i++] = "serve";
381 nargv[i++] = "--stdio";
381 nargv[i++] = "--stdio";
382 break;
382 break;
383 case hg_init:
383 case hg_init:
384 nargv[i++] = HG;
384 nargv[i++] = HG;
385 nargv[i++] = "init";
385 nargv[i++] = "init";
386 nargv[i++] = repo;
386 nargv[i++] = repo;
387 break;
387 break;
388 }
388 }
389
389
390 nargv[i] = NULL;
390 nargv[i] = NULL;
391
391
392 if (debug) {
392 if (debug) {
393 print_cmdline(i, nargv);
393 print_cmdline(i, nargv);
394 }
394 }
395
395
396 execv(HG, nargv);
396 execv(HG, nargv);
397 perror(HG);
397 perror(HG);
398 exit(EX_UNAVAILABLE);
398 exit(EX_UNAVAILABLE);
399
399
400 badargs:
400 badargs:
401 /* print useless error message. */
401 /* print useless error message. */
402
402
403 usage("invalid arguments", EX_DATAERR);
403 usage("invalid arguments", EX_DATAERR);
404 }
404 }
405
405
406 int main(int argc, char **argv)
406 int main(int argc, char **argv)
407 {
407 {
408 char host[1024];
408 char host[1024];
409 char *c;
409 char *c;
410
410
411 if (gethostname(host, sizeof(host)) == -1) {
411 if (gethostname(host, sizeof(host)) == -1) {
412 perror("gethostname");
412 perror("gethostname");
413 exit(EX_OSERR);
413 exit(EX_OSERR);
414 }
414 }
415
415
416 if ((c = strchr(host, '.')) != NULL) {
416 if ((c = strchr(host, '.')) != NULL) {
417 *c = '\0';
417 *c = '\0';
418 }
418 }
419
419
420 if (getenv("SSH_CLIENT")) {
420 if (getenv("SSH_CLIENT")) {
421 char *hg_gateway = HG_GATEWAY;
421 char *hg_gateway = HG_GATEWAY;
422 char *hg_host = HG_HOST;
422 char *hg_host = HG_HOST;
423
423
424 if (hg_gateway && strcmp(host, hg_gateway) == 0) {
424 if (hg_gateway && strcmp(host, hg_gateway) == 0) {
425 forward_through_gateway(argc, argv);
425 forward_through_gateway(argc, argv);
426 }
426 }
427
427
428 if (hg_host && strcmp(host, hg_host) != 0) {
428 if (hg_host && strcmp(host, hg_host) != 0) {
429 usage("invoked on unexpected host", EX_USAGE);
429 usage("invoked on unexpected host", EX_USAGE);
430 }
430 }
431
431
432 serve_data(argc, argv);
432 serve_data(argc, argv);
433 } else if (HG_SHELL) {
433 } else if (HG_SHELL) {
434 run_shell(argc, argv);
434 run_shell(argc, argv);
435 } else {
435 } else {
436 usage("invalid arguments", EX_DATAERR);
436 usage("invalid arguments", EX_DATAERR);
437 }
437 }
438
438
439 return 0;
439 return 0;
440 }
440 }
441
442 /* Local Variables: */
443 /* c-file-style: "linux" */
444 /* indent-tabs-mode: t */
445 /* End: */
@@ -1,649 +1,654 b''
1 /*
1 /*
2 * _inotify.c - Python extension interfacing to the Linux inotify subsystem
2 * _inotify.c - Python extension interfacing to the Linux inotify subsystem
3 *
3 *
4 * Copyright 2006 Bryan O'Sullivan <bos@serpentine.com>
4 * Copyright 2006 Bryan O'Sullivan <bos@serpentine.com>
5 *
5 *
6 * This library is free software; you can redistribute it and/or
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of version 2.1 of the GNU Lesser General
7 * modify it under the terms of version 2.1 of the GNU Lesser General
8 * Public License or any later version.
8 * Public License or any later version.
9 */
9 */
10
10
11 #include <Python.h>
11 #include <Python.h>
12 #include <alloca.h>
12 #include <alloca.h>
13 #include <sys/inotify.h>
13 #include <sys/inotify.h>
14 #include <stdint.h>
14 #include <stdint.h>
15 #include <sys/ioctl.h>
15 #include <sys/ioctl.h>
16 #include <unistd.h>
16 #include <unistd.h>
17
17
18 #include <util.h>
18 #include <util.h>
19
19
20 /* Variables used in the event string representation */
20 /* Variables used in the event string representation */
21 static PyObject *join;
21 static PyObject *join;
22 static PyObject *er_wm;
22 static PyObject *er_wm;
23 static PyObject *er_wmc;
23 static PyObject *er_wmc;
24 static PyObject *er_wmn;
24 static PyObject *er_wmn;
25 static PyObject *er_wmcn;
25 static PyObject *er_wmcn;
26
26
27 static PyObject *init(PyObject *self, PyObject *args)
27 static PyObject *init(PyObject *self, PyObject *args)
28 {
28 {
29 PyObject *ret = NULL;
29 PyObject *ret = NULL;
30 int fd = -1;
30 int fd = -1;
31
31
32 if (!PyArg_ParseTuple(args, ":init"))
32 if (!PyArg_ParseTuple(args, ":init"))
33 goto bail;
33 goto bail;
34
34
35 Py_BEGIN_ALLOW_THREADS;
35 Py_BEGIN_ALLOW_THREADS;
36 fd = inotify_init();
36 fd = inotify_init();
37 Py_END_ALLOW_THREADS;
37 Py_END_ALLOW_THREADS;
38
38
39 if (fd == -1) {
39 if (fd == -1) {
40 PyErr_SetFromErrno(PyExc_OSError);
40 PyErr_SetFromErrno(PyExc_OSError);
41 goto bail;
41 goto bail;
42 }
42 }
43
43
44 ret = PyInt_FromLong(fd);
44 ret = PyInt_FromLong(fd);
45 if (ret == NULL)
45 if (ret == NULL)
46 goto bail;
46 goto bail;
47
47
48 goto done;
48 goto done;
49
49
50 bail:
50 bail:
51 if (fd != -1)
51 if (fd != -1)
52 close(fd);
52 close(fd);
53
53
54 Py_CLEAR(ret);
54 Py_CLEAR(ret);
55
55
56 done:
56 done:
57 return ret;
57 return ret;
58 }
58 }
59
59
60 PyDoc_STRVAR(
60 PyDoc_STRVAR(
61 init_doc,
61 init_doc,
62 "init() -> fd\n"
62 "init() -> fd\n"
63 "\n"
63 "\n"
64 "Initialise an inotify instance.\n"
64 "Initialise an inotify instance.\n"
65 "Return a file descriptor associated with a new inotify event queue.");
65 "Return a file descriptor associated with a new inotify event queue.");
66
66
67 static PyObject *add_watch(PyObject *self, PyObject *args)
67 static PyObject *add_watch(PyObject *self, PyObject *args)
68 {
68 {
69 PyObject *ret = NULL;
69 PyObject *ret = NULL;
70 uint32_t mask;
70 uint32_t mask;
71 int wd = -1;
71 int wd = -1;
72 char *path;
72 char *path;
73 int fd;
73 int fd;
74
74
75 if (!PyArg_ParseTuple(args, "isI:add_watch", &fd, &path, &mask))
75 if (!PyArg_ParseTuple(args, "isI:add_watch", &fd, &path, &mask))
76 goto bail;
76 goto bail;
77
77
78 Py_BEGIN_ALLOW_THREADS;
78 Py_BEGIN_ALLOW_THREADS;
79 wd = inotify_add_watch(fd, path, mask);
79 wd = inotify_add_watch(fd, path, mask);
80 Py_END_ALLOW_THREADS;
80 Py_END_ALLOW_THREADS;
81
81
82 if (wd == -1) {
82 if (wd == -1) {
83 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
83 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
84 goto bail;
84 goto bail;
85 }
85 }
86
86
87 ret = PyInt_FromLong(wd);
87 ret = PyInt_FromLong(wd);
88 if (ret == NULL)
88 if (ret == NULL)
89 goto bail;
89 goto bail;
90
90
91 goto done;
91 goto done;
92
92
93 bail:
93 bail:
94 if (wd != -1)
94 if (wd != -1)
95 inotify_rm_watch(fd, wd);
95 inotify_rm_watch(fd, wd);
96
96
97 Py_CLEAR(ret);
97 Py_CLEAR(ret);
98
98
99 done:
99 done:
100 return ret;
100 return ret;
101 }
101 }
102
102
103 PyDoc_STRVAR(
103 PyDoc_STRVAR(
104 add_watch_doc,
104 add_watch_doc,
105 "add_watch(fd, path, mask) -> wd\n"
105 "add_watch(fd, path, mask) -> wd\n"
106 "\n"
106 "\n"
107 "Add a watch to an inotify instance, or modify an existing watch.\n"
107 "Add a watch to an inotify instance, or modify an existing watch.\n"
108 "\n"
108 "\n"
109 " fd: file descriptor returned by init()\n"
109 " fd: file descriptor returned by init()\n"
110 " path: path to watch\n"
110 " path: path to watch\n"
111 " mask: mask of events to watch for\n"
111 " mask: mask of events to watch for\n"
112 "\n"
112 "\n"
113 "Return a unique numeric watch descriptor for the inotify instance\n"
113 "Return a unique numeric watch descriptor for the inotify instance\n"
114 "mapped by the file descriptor.");
114 "mapped by the file descriptor.");
115
115
116 static PyObject *remove_watch(PyObject *self, PyObject *args)
116 static PyObject *remove_watch(PyObject *self, PyObject *args)
117 {
117 {
118 uint32_t wd;
118 uint32_t wd;
119 int fd;
119 int fd;
120 int r;
120 int r;
121
121
122 if (!PyArg_ParseTuple(args, "iI:remove_watch", &fd, &wd))
122 if (!PyArg_ParseTuple(args, "iI:remove_watch", &fd, &wd))
123 return NULL;
123 return NULL;
124
124
125 Py_BEGIN_ALLOW_THREADS;
125 Py_BEGIN_ALLOW_THREADS;
126 r = inotify_rm_watch(fd, wd);
126 r = inotify_rm_watch(fd, wd);
127 Py_END_ALLOW_THREADS;
127 Py_END_ALLOW_THREADS;
128
128
129 if (r == -1) {
129 if (r == -1) {
130 PyErr_SetFromErrno(PyExc_OSError);
130 PyErr_SetFromErrno(PyExc_OSError);
131 return NULL;
131 return NULL;
132 }
132 }
133
133
134 Py_INCREF(Py_None);
134 Py_INCREF(Py_None);
135 return Py_None;
135 return Py_None;
136 }
136 }
137
137
138 PyDoc_STRVAR(
138 PyDoc_STRVAR(
139 remove_watch_doc,
139 remove_watch_doc,
140 "remove_watch(fd, wd)\n"
140 "remove_watch(fd, wd)\n"
141 "\n"
141 "\n"
142 " fd: file descriptor returned by init()\n"
142 " fd: file descriptor returned by init()\n"
143 " wd: watch descriptor returned by add_watch()\n"
143 " wd: watch descriptor returned by add_watch()\n"
144 "\n"
144 "\n"
145 "Remove a watch associated with the watch descriptor wd from the\n"
145 "Remove a watch associated with the watch descriptor wd from the\n"
146 "inotify instance associated with the file descriptor fd.\n"
146 "inotify instance associated with the file descriptor fd.\n"
147 "\n"
147 "\n"
148 "Removing a watch causes an IN_IGNORED event to be generated for this\n"
148 "Removing a watch causes an IN_IGNORED event to be generated for this\n"
149 "watch descriptor.");
149 "watch descriptor.");
150
150
151 #define bit_name(x) {x, #x}
151 #define bit_name(x) {x, #x}
152
152
153 static struct {
153 static struct {
154 int bit;
154 int bit;
155 const char *name;
155 const char *name;
156 PyObject *pyname;
156 PyObject *pyname;
157 } bit_names[] = {
157 } bit_names[] = {
158 bit_name(IN_ACCESS),
158 bit_name(IN_ACCESS),
159 bit_name(IN_MODIFY),
159 bit_name(IN_MODIFY),
160 bit_name(IN_ATTRIB),
160 bit_name(IN_ATTRIB),
161 bit_name(IN_CLOSE_WRITE),
161 bit_name(IN_CLOSE_WRITE),
162 bit_name(IN_CLOSE_NOWRITE),
162 bit_name(IN_CLOSE_NOWRITE),
163 bit_name(IN_OPEN),
163 bit_name(IN_OPEN),
164 bit_name(IN_MOVED_FROM),
164 bit_name(IN_MOVED_FROM),
165 bit_name(IN_MOVED_TO),
165 bit_name(IN_MOVED_TO),
166 bit_name(IN_CREATE),
166 bit_name(IN_CREATE),
167 bit_name(IN_DELETE),
167 bit_name(IN_DELETE),
168 bit_name(IN_DELETE_SELF),
168 bit_name(IN_DELETE_SELF),
169 bit_name(IN_MOVE_SELF),
169 bit_name(IN_MOVE_SELF),
170 bit_name(IN_UNMOUNT),
170 bit_name(IN_UNMOUNT),
171 bit_name(IN_Q_OVERFLOW),
171 bit_name(IN_Q_OVERFLOW),
172 bit_name(IN_IGNORED),
172 bit_name(IN_IGNORED),
173 bit_name(IN_ONLYDIR),
173 bit_name(IN_ONLYDIR),
174 bit_name(IN_DONT_FOLLOW),
174 bit_name(IN_DONT_FOLLOW),
175 bit_name(IN_MASK_ADD),
175 bit_name(IN_MASK_ADD),
176 bit_name(IN_ISDIR),
176 bit_name(IN_ISDIR),
177 bit_name(IN_ONESHOT),
177 bit_name(IN_ONESHOT),
178 {0}
178 {0}
179 };
179 };
180
180
181 static PyObject *decode_mask(int mask)
181 static PyObject *decode_mask(int mask)
182 {
182 {
183 PyObject *ret = PyList_New(0);
183 PyObject *ret = PyList_New(0);
184 int i;
184 int i;
185
185
186 if (ret == NULL)
186 if (ret == NULL)
187 goto bail;
187 goto bail;
188
188
189 for (i = 0; bit_names[i].bit; i++) {
189 for (i = 0; bit_names[i].bit; i++) {
190 if (mask & bit_names[i].bit) {
190 if (mask & bit_names[i].bit) {
191 if (bit_names[i].pyname == NULL) {
191 if (bit_names[i].pyname == NULL) {
192 bit_names[i].pyname = PyString_FromString(bit_names[i].name);
192 bit_names[i].pyname = PyString_FromString(bit_names[i].name);
193 if (bit_names[i].pyname == NULL)
193 if (bit_names[i].pyname == NULL)
194 goto bail;
194 goto bail;
195 }
195 }
196 Py_INCREF(bit_names[i].pyname);
196 Py_INCREF(bit_names[i].pyname);
197 if (PyList_Append(ret, bit_names[i].pyname) == -1)
197 if (PyList_Append(ret, bit_names[i].pyname) == -1)
198 goto bail;
198 goto bail;
199 }
199 }
200 }
200 }
201
201
202 goto done;
202 goto done;
203
203
204 bail:
204 bail:
205 Py_CLEAR(ret);
205 Py_CLEAR(ret);
206
206
207 done:
207 done:
208 return ret;
208 return ret;
209 }
209 }
210
210
211 static PyObject *pydecode_mask(PyObject *self, PyObject *args)
211 static PyObject *pydecode_mask(PyObject *self, PyObject *args)
212 {
212 {
213 int mask;
213 int mask;
214
214
215 if (!PyArg_ParseTuple(args, "i:decode_mask", &mask))
215 if (!PyArg_ParseTuple(args, "i:decode_mask", &mask))
216 return NULL;
216 return NULL;
217
217
218 return decode_mask(mask);
218 return decode_mask(mask);
219 }
219 }
220
220
221 PyDoc_STRVAR(
221 PyDoc_STRVAR(
222 decode_mask_doc,
222 decode_mask_doc,
223 "decode_mask(mask) -> list_of_strings\n"
223 "decode_mask(mask) -> list_of_strings\n"
224 "\n"
224 "\n"
225 "Decode an inotify mask value into a list of strings that give the\n"
225 "Decode an inotify mask value into a list of strings that give the\n"
226 "name of each bit set in the mask.");
226 "name of each bit set in the mask.");
227
227
228 static char doc[] = "Low-level inotify interface wrappers.";
228 static char doc[] = "Low-level inotify interface wrappers.";
229
229
230 static void define_const(PyObject *dict, const char *name, uint32_t val)
230 static void define_const(PyObject *dict, const char *name, uint32_t val)
231 {
231 {
232 PyObject *pyval = PyInt_FromLong(val);
232 PyObject *pyval = PyInt_FromLong(val);
233 PyObject *pyname = PyString_FromString(name);
233 PyObject *pyname = PyString_FromString(name);
234
234
235 if (!pyname || !pyval)
235 if (!pyname || !pyval)
236 goto bail;
236 goto bail;
237
237
238 PyDict_SetItem(dict, pyname, pyval);
238 PyDict_SetItem(dict, pyname, pyval);
239
239
240 bail:
240 bail:
241 Py_XDECREF(pyname);
241 Py_XDECREF(pyname);
242 Py_XDECREF(pyval);
242 Py_XDECREF(pyval);
243 }
243 }
244
244
245 static void define_consts(PyObject *dict)
245 static void define_consts(PyObject *dict)
246 {
246 {
247 define_const(dict, "IN_ACCESS", IN_ACCESS);
247 define_const(dict, "IN_ACCESS", IN_ACCESS);
248 define_const(dict, "IN_MODIFY", IN_MODIFY);
248 define_const(dict, "IN_MODIFY", IN_MODIFY);
249 define_const(dict, "IN_ATTRIB", IN_ATTRIB);
249 define_const(dict, "IN_ATTRIB", IN_ATTRIB);
250 define_const(dict, "IN_CLOSE_WRITE", IN_CLOSE_WRITE);
250 define_const(dict, "IN_CLOSE_WRITE", IN_CLOSE_WRITE);
251 define_const(dict, "IN_CLOSE_NOWRITE", IN_CLOSE_NOWRITE);
251 define_const(dict, "IN_CLOSE_NOWRITE", IN_CLOSE_NOWRITE);
252 define_const(dict, "IN_OPEN", IN_OPEN);
252 define_const(dict, "IN_OPEN", IN_OPEN);
253 define_const(dict, "IN_MOVED_FROM", IN_MOVED_FROM);
253 define_const(dict, "IN_MOVED_FROM", IN_MOVED_FROM);
254 define_const(dict, "IN_MOVED_TO", IN_MOVED_TO);
254 define_const(dict, "IN_MOVED_TO", IN_MOVED_TO);
255
255
256 define_const(dict, "IN_CLOSE", IN_CLOSE);
256 define_const(dict, "IN_CLOSE", IN_CLOSE);
257 define_const(dict, "IN_MOVE", IN_MOVE);
257 define_const(dict, "IN_MOVE", IN_MOVE);
258
258
259 define_const(dict, "IN_CREATE", IN_CREATE);
259 define_const(dict, "IN_CREATE", IN_CREATE);
260 define_const(dict, "IN_DELETE", IN_DELETE);
260 define_const(dict, "IN_DELETE", IN_DELETE);
261 define_const(dict, "IN_DELETE_SELF", IN_DELETE_SELF);
261 define_const(dict, "IN_DELETE_SELF", IN_DELETE_SELF);
262 define_const(dict, "IN_MOVE_SELF", IN_MOVE_SELF);
262 define_const(dict, "IN_MOVE_SELF", IN_MOVE_SELF);
263 define_const(dict, "IN_UNMOUNT", IN_UNMOUNT);
263 define_const(dict, "IN_UNMOUNT", IN_UNMOUNT);
264 define_const(dict, "IN_Q_OVERFLOW", IN_Q_OVERFLOW);
264 define_const(dict, "IN_Q_OVERFLOW", IN_Q_OVERFLOW);
265 define_const(dict, "IN_IGNORED", IN_IGNORED);
265 define_const(dict, "IN_IGNORED", IN_IGNORED);
266
266
267 define_const(dict, "IN_ONLYDIR", IN_ONLYDIR);
267 define_const(dict, "IN_ONLYDIR", IN_ONLYDIR);
268 define_const(dict, "IN_DONT_FOLLOW", IN_DONT_FOLLOW);
268 define_const(dict, "IN_DONT_FOLLOW", IN_DONT_FOLLOW);
269 define_const(dict, "IN_MASK_ADD", IN_MASK_ADD);
269 define_const(dict, "IN_MASK_ADD", IN_MASK_ADD);
270 define_const(dict, "IN_ISDIR", IN_ISDIR);
270 define_const(dict, "IN_ISDIR", IN_ISDIR);
271 define_const(dict, "IN_ONESHOT", IN_ONESHOT);
271 define_const(dict, "IN_ONESHOT", IN_ONESHOT);
272 define_const(dict, "IN_ALL_EVENTS", IN_ALL_EVENTS);
272 define_const(dict, "IN_ALL_EVENTS", IN_ALL_EVENTS);
273 }
273 }
274
274
275 struct event {
275 struct event {
276 PyObject_HEAD
276 PyObject_HEAD
277 PyObject *wd;
277 PyObject *wd;
278 PyObject *mask;
278 PyObject *mask;
279 PyObject *cookie;
279 PyObject *cookie;
280 PyObject *name;
280 PyObject *name;
281 };
281 };
282
282
283 static PyObject *event_wd(PyObject *self, void *x)
283 static PyObject *event_wd(PyObject *self, void *x)
284 {
284 {
285 struct event *evt = (struct event *)self;
285 struct event *evt = (struct event *)self;
286 Py_INCREF(evt->wd);
286 Py_INCREF(evt->wd);
287 return evt->wd;
287 return evt->wd;
288 }
288 }
289
289
290 static PyObject *event_mask(PyObject *self, void *x)
290 static PyObject *event_mask(PyObject *self, void *x)
291 {
291 {
292 struct event *evt = (struct event *)self;
292 struct event *evt = (struct event *)self;
293 Py_INCREF(evt->mask);
293 Py_INCREF(evt->mask);
294 return evt->mask;
294 return evt->mask;
295 }
295 }
296
296
297 static PyObject *event_cookie(PyObject *self, void *x)
297 static PyObject *event_cookie(PyObject *self, void *x)
298 {
298 {
299 struct event *evt = (struct event *)self;
299 struct event *evt = (struct event *)self;
300 Py_INCREF(evt->cookie);
300 Py_INCREF(evt->cookie);
301 return evt->cookie;
301 return evt->cookie;
302 }
302 }
303
303
304 static PyObject *event_name(PyObject *self, void *x)
304 static PyObject *event_name(PyObject *self, void *x)
305 {
305 {
306 struct event *evt = (struct event *)self;
306 struct event *evt = (struct event *)self;
307 Py_INCREF(evt->name);
307 Py_INCREF(evt->name);
308 return evt->name;
308 return evt->name;
309 }
309 }
310
310
311 static struct PyGetSetDef event_getsets[] = {
311 static struct PyGetSetDef event_getsets[] = {
312 {"wd", event_wd, NULL,
312 {"wd", event_wd, NULL,
313 "watch descriptor"},
313 "watch descriptor"},
314 {"mask", event_mask, NULL,
314 {"mask", event_mask, NULL,
315 "event mask"},
315 "event mask"},
316 {"cookie", event_cookie, NULL,
316 {"cookie", event_cookie, NULL,
317 "rename cookie, if rename-related event"},
317 "rename cookie, if rename-related event"},
318 {"name", event_name, NULL,
318 {"name", event_name, NULL,
319 "file name"},
319 "file name"},
320 {NULL}
320 {NULL}
321 };
321 };
322
322
323 PyDoc_STRVAR(
323 PyDoc_STRVAR(
324 event_doc,
324 event_doc,
325 "event: Structure describing an inotify event.");
325 "event: Structure describing an inotify event.");
326
326
327 static PyObject *event_new(PyTypeObject *t, PyObject *a, PyObject *k)
327 static PyObject *event_new(PyTypeObject *t, PyObject *a, PyObject *k)
328 {
328 {
329 return (*t->tp_alloc)(t, 0);
329 return (*t->tp_alloc)(t, 0);
330 }
330 }
331
331
332 static void event_dealloc(struct event *evt)
332 static void event_dealloc(struct event *evt)
333 {
333 {
334 Py_XDECREF(evt->wd);
334 Py_XDECREF(evt->wd);
335 Py_XDECREF(evt->mask);
335 Py_XDECREF(evt->mask);
336 Py_XDECREF(evt->cookie);
336 Py_XDECREF(evt->cookie);
337 Py_XDECREF(evt->name);
337 Py_XDECREF(evt->name);
338
338
339 Py_TYPE(evt)->tp_free(evt);
339 Py_TYPE(evt)->tp_free(evt);
340 }
340 }
341
341
342 static PyObject *event_repr(struct event *evt)
342 static PyObject *event_repr(struct event *evt)
343 {
343 {
344 int cookie = evt->cookie == Py_None ? -1 : PyInt_AsLong(evt->cookie);
344 int cookie = evt->cookie == Py_None ? -1 : PyInt_AsLong(evt->cookie);
345 PyObject *ret = NULL, *pymasks = NULL, *pymask = NULL;
345 PyObject *ret = NULL, *pymasks = NULL, *pymask = NULL;
346 PyObject *tuple = NULL, *formatstr = NULL;
346 PyObject *tuple = NULL, *formatstr = NULL;
347
347
348 pymasks = decode_mask(PyInt_AsLong(evt->mask));
348 pymasks = decode_mask(PyInt_AsLong(evt->mask));
349 if (pymasks == NULL)
349 if (pymasks == NULL)
350 goto bail;
350 goto bail;
351
351
352 pymask = _PyString_Join(join, pymasks);
352 pymask = _PyString_Join(join, pymasks);
353 if (pymask == NULL)
353 if (pymask == NULL)
354 goto bail;
354 goto bail;
355
355
356 if (evt->name != Py_None) {
356 if (evt->name != Py_None) {
357 if (cookie == -1) {
357 if (cookie == -1) {
358 formatstr = er_wmn;
358 formatstr = er_wmn;
359 tuple = PyTuple_Pack(3, evt->wd, pymask, evt->name);
359 tuple = PyTuple_Pack(3, evt->wd, pymask, evt->name);
360 }
360 }
361 else {
361 else {
362 formatstr = er_wmcn;
362 formatstr = er_wmcn;
363 tuple = PyTuple_Pack(4, evt->wd, pymask,
363 tuple = PyTuple_Pack(4, evt->wd, pymask,
364 evt->cookie, evt->name);
364 evt->cookie, evt->name);
365 }
365 }
366 } else {
366 } else {
367 if (cookie == -1) {
367 if (cookie == -1) {
368 formatstr = er_wm;
368 formatstr = er_wm;
369 tuple = PyTuple_Pack(2, evt->wd, pymask);
369 tuple = PyTuple_Pack(2, evt->wd, pymask);
370 }
370 }
371 else {
371 else {
372 formatstr = er_wmc;
372 formatstr = er_wmc;
373 tuple = PyTuple_Pack(3, evt->wd, pymask, evt->cookie);
373 tuple = PyTuple_Pack(3, evt->wd, pymask, evt->cookie);
374 }
374 }
375 }
375 }
376
376
377 if (tuple == NULL)
377 if (tuple == NULL)
378 goto bail;
378 goto bail;
379
379
380 ret = PyNumber_Remainder(formatstr, tuple);
380 ret = PyNumber_Remainder(formatstr, tuple);
381
381
382 if (ret == NULL)
382 if (ret == NULL)
383 goto bail;
383 goto bail;
384
384
385 goto done;
385 goto done;
386 bail:
386 bail:
387 Py_CLEAR(ret);
387 Py_CLEAR(ret);
388
388
389 done:
389 done:
390 Py_XDECREF(pymask);
390 Py_XDECREF(pymask);
391 Py_XDECREF(pymasks);
391 Py_XDECREF(pymasks);
392 Py_XDECREF(tuple);
392 Py_XDECREF(tuple);
393
393
394 return ret;
394 return ret;
395 }
395 }
396
396
397 static PyTypeObject event_type = {
397 static PyTypeObject event_type = {
398 PyVarObject_HEAD_INIT(NULL, 0)
398 PyVarObject_HEAD_INIT(NULL, 0)
399 "_inotify.event", /*tp_name*/
399 "_inotify.event", /*tp_name*/
400 sizeof(struct event), /*tp_basicsize*/
400 sizeof(struct event), /*tp_basicsize*/
401 0, /*tp_itemsize*/
401 0, /*tp_itemsize*/
402 (destructor)event_dealloc, /*tp_dealloc*/
402 (destructor)event_dealloc, /*tp_dealloc*/
403 0, /*tp_print*/
403 0, /*tp_print*/
404 0, /*tp_getattr*/
404 0, /*tp_getattr*/
405 0, /*tp_setattr*/
405 0, /*tp_setattr*/
406 0, /*tp_compare*/
406 0, /*tp_compare*/
407 (reprfunc)event_repr, /*tp_repr*/
407 (reprfunc)event_repr, /*tp_repr*/
408 0, /*tp_as_number*/
408 0, /*tp_as_number*/
409 0, /*tp_as_sequence*/
409 0, /*tp_as_sequence*/
410 0, /*tp_as_mapping*/
410 0, /*tp_as_mapping*/
411 0, /*tp_hash */
411 0, /*tp_hash */
412 0, /*tp_call*/
412 0, /*tp_call*/
413 0, /*tp_str*/
413 0, /*tp_str*/
414 0, /*tp_getattro*/
414 0, /*tp_getattro*/
415 0, /*tp_setattro*/
415 0, /*tp_setattro*/
416 0, /*tp_as_buffer*/
416 0, /*tp_as_buffer*/
417 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
417 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
418 event_doc, /* tp_doc */
418 event_doc, /* tp_doc */
419 0, /* tp_traverse */
419 0, /* tp_traverse */
420 0, /* tp_clear */
420 0, /* tp_clear */
421 0, /* tp_richcompare */
421 0, /* tp_richcompare */
422 0, /* tp_weaklistoffset */
422 0, /* tp_weaklistoffset */
423 0, /* tp_iter */
423 0, /* tp_iter */
424 0, /* tp_iternext */
424 0, /* tp_iternext */
425 0, /* tp_methods */
425 0, /* tp_methods */
426 0, /* tp_members */
426 0, /* tp_members */
427 event_getsets, /* tp_getset */
427 event_getsets, /* tp_getset */
428 0, /* tp_base */
428 0, /* tp_base */
429 0, /* tp_dict */
429 0, /* tp_dict */
430 0, /* tp_descr_get */
430 0, /* tp_descr_get */
431 0, /* tp_descr_set */
431 0, /* tp_descr_set */
432 0, /* tp_dictoffset */
432 0, /* tp_dictoffset */
433 0, /* tp_init */
433 0, /* tp_init */
434 0, /* tp_alloc */
434 0, /* tp_alloc */
435 event_new, /* tp_new */
435 event_new, /* tp_new */
436 };
436 };
437
437
438 PyObject *read_events(PyObject *self, PyObject *args)
438 PyObject *read_events(PyObject *self, PyObject *args)
439 {
439 {
440 PyObject *ctor_args = NULL;
440 PyObject *ctor_args = NULL;
441 PyObject *pybufsize = NULL;
441 PyObject *pybufsize = NULL;
442 PyObject *ret = NULL;
442 PyObject *ret = NULL;
443 int bufsize = 65536;
443 int bufsize = 65536;
444 char *buf = NULL;
444 char *buf = NULL;
445 int nread, pos;
445 int nread, pos;
446 int fd;
446 int fd;
447
447
448 if (!PyArg_ParseTuple(args, "i|O:read", &fd, &pybufsize))
448 if (!PyArg_ParseTuple(args, "i|O:read", &fd, &pybufsize))
449 goto bail;
449 goto bail;
450
450
451 if (pybufsize && pybufsize != Py_None)
451 if (pybufsize && pybufsize != Py_None)
452 bufsize = PyInt_AsLong(pybufsize);
452 bufsize = PyInt_AsLong(pybufsize);
453
453
454 ret = PyList_New(0);
454 ret = PyList_New(0);
455 if (ret == NULL)
455 if (ret == NULL)
456 goto bail;
456 goto bail;
457
457
458 if (bufsize <= 0) {
458 if (bufsize <= 0) {
459 int r;
459 int r;
460
460
461 Py_BEGIN_ALLOW_THREADS;
461 Py_BEGIN_ALLOW_THREADS;
462 r = ioctl(fd, FIONREAD, &bufsize);
462 r = ioctl(fd, FIONREAD, &bufsize);
463 Py_END_ALLOW_THREADS;
463 Py_END_ALLOW_THREADS;
464
464
465 if (r == -1) {
465 if (r == -1) {
466 PyErr_SetFromErrno(PyExc_OSError);
466 PyErr_SetFromErrno(PyExc_OSError);
467 goto bail;
467 goto bail;
468 }
468 }
469 if (bufsize == 0)
469 if (bufsize == 0)
470 goto done;
470 goto done;
471 }
471 }
472 else {
472 else {
473 static long name_max;
473 static long name_max;
474 static long name_fd = -1;
474 static long name_fd = -1;
475 long min;
475 long min;
476
476
477 if (name_fd != fd) {
477 if (name_fd != fd) {
478 name_fd = fd;
478 name_fd = fd;
479 Py_BEGIN_ALLOW_THREADS;
479 Py_BEGIN_ALLOW_THREADS;
480 name_max = fpathconf(fd, _PC_NAME_MAX);
480 name_max = fpathconf(fd, _PC_NAME_MAX);
481 Py_END_ALLOW_THREADS;
481 Py_END_ALLOW_THREADS;
482 }
482 }
483
483
484 min = sizeof(struct inotify_event) + name_max + 1;
484 min = sizeof(struct inotify_event) + name_max + 1;
485
485
486 if (bufsize < min) {
486 if (bufsize < min) {
487 PyErr_Format(PyExc_ValueError,
487 PyErr_Format(PyExc_ValueError,
488 "bufsize must be at least %d", (int)min);
488 "bufsize must be at least %d", (int)min);
489 goto bail;
489 goto bail;
490 }
490 }
491 }
491 }
492
492
493 buf = alloca(bufsize);
493 buf = alloca(bufsize);
494
494
495 Py_BEGIN_ALLOW_THREADS;
495 Py_BEGIN_ALLOW_THREADS;
496 nread = read(fd, buf, bufsize);
496 nread = read(fd, buf, bufsize);
497 Py_END_ALLOW_THREADS;
497 Py_END_ALLOW_THREADS;
498
498
499 if (nread == -1) {
499 if (nread == -1) {
500 PyErr_SetFromErrno(PyExc_OSError);
500 PyErr_SetFromErrno(PyExc_OSError);
501 goto bail;
501 goto bail;
502 }
502 }
503
503
504 ctor_args = PyTuple_New(0);
504 ctor_args = PyTuple_New(0);
505
505
506 if (ctor_args == NULL)
506 if (ctor_args == NULL)
507 goto bail;
507 goto bail;
508
508
509 pos = 0;
509 pos = 0;
510
510
511 while (pos < nread) {
511 while (pos < nread) {
512 struct inotify_event *in = (struct inotify_event *)(buf + pos);
512 struct inotify_event *in = (struct inotify_event *)(buf + pos);
513 struct event *evt;
513 struct event *evt;
514 PyObject *obj;
514 PyObject *obj;
515
515
516 obj = PyObject_CallObject((PyObject *)&event_type, ctor_args);
516 obj = PyObject_CallObject((PyObject *)&event_type, ctor_args);
517
517
518 if (obj == NULL)
518 if (obj == NULL)
519 goto bail;
519 goto bail;
520
520
521 evt = (struct event *)obj;
521 evt = (struct event *)obj;
522
522
523 evt->wd = PyInt_FromLong(in->wd);
523 evt->wd = PyInt_FromLong(in->wd);
524 evt->mask = PyInt_FromLong(in->mask);
524 evt->mask = PyInt_FromLong(in->mask);
525 if (in->mask & IN_MOVE)
525 if (in->mask & IN_MOVE)
526 evt->cookie = PyInt_FromLong(in->cookie);
526 evt->cookie = PyInt_FromLong(in->cookie);
527 else {
527 else {
528 Py_INCREF(Py_None);
528 Py_INCREF(Py_None);
529 evt->cookie = Py_None;
529 evt->cookie = Py_None;
530 }
530 }
531 if (in->len)
531 if (in->len)
532 evt->name = PyString_FromString(in->name);
532 evt->name = PyString_FromString(in->name);
533 else {
533 else {
534 Py_INCREF(Py_None);
534 Py_INCREF(Py_None);
535 evt->name = Py_None;
535 evt->name = Py_None;
536 }
536 }
537
537
538 if (!evt->wd || !evt->mask || !evt->cookie || !evt->name)
538 if (!evt->wd || !evt->mask || !evt->cookie || !evt->name)
539 goto mybail;
539 goto mybail;
540
540
541 if (PyList_Append(ret, obj) == -1)
541 if (PyList_Append(ret, obj) == -1)
542 goto mybail;
542 goto mybail;
543
543
544 pos += sizeof(struct inotify_event) + in->len;
544 pos += sizeof(struct inotify_event) + in->len;
545 continue;
545 continue;
546
546
547 mybail:
547 mybail:
548 Py_CLEAR(evt->wd);
548 Py_CLEAR(evt->wd);
549 Py_CLEAR(evt->mask);
549 Py_CLEAR(evt->mask);
550 Py_CLEAR(evt->cookie);
550 Py_CLEAR(evt->cookie);
551 Py_CLEAR(evt->name);
551 Py_CLEAR(evt->name);
552 Py_DECREF(obj);
552 Py_DECREF(obj);
553
553
554 goto bail;
554 goto bail;
555 }
555 }
556
556
557 goto done;
557 goto done;
558
558
559 bail:
559 bail:
560 Py_CLEAR(ret);
560 Py_CLEAR(ret);
561
561
562 done:
562 done:
563 Py_XDECREF(ctor_args);
563 Py_XDECREF(ctor_args);
564
564
565 return ret;
565 return ret;
566 }
566 }
567
567
568 static int init_globals(void)
568 static int init_globals(void)
569 {
569 {
570 join = PyString_FromString("|");
570 join = PyString_FromString("|");
571 er_wm = PyString_FromString("event(wd=%d, mask=%s)");
571 er_wm = PyString_FromString("event(wd=%d, mask=%s)");
572 er_wmn = PyString_FromString("event(wd=%d, mask=%s, name=%s)");
572 er_wmn = PyString_FromString("event(wd=%d, mask=%s, name=%s)");
573 er_wmc = PyString_FromString("event(wd=%d, mask=%s, cookie=0x%x)");
573 er_wmc = PyString_FromString("event(wd=%d, mask=%s, cookie=0x%x)");
574 er_wmcn = PyString_FromString("event(wd=%d, mask=%s, cookie=0x%x, name=%s)");
574 er_wmcn = PyString_FromString("event(wd=%d, mask=%s, cookie=0x%x, name=%s)");
575
575
576 return join && er_wm && er_wmn && er_wmc && er_wmcn;
576 return join && er_wm && er_wmn && er_wmc && er_wmcn;
577 }
577 }
578
578
579 PyDoc_STRVAR(
579 PyDoc_STRVAR(
580 read_doc,
580 read_doc,
581 "read(fd, bufsize[=65536]) -> list_of_events\n"
581 "read(fd, bufsize[=65536]) -> list_of_events\n"
582 "\n"
582 "\n"
583 "\nRead inotify events from a file descriptor.\n"
583 "\nRead inotify events from a file descriptor.\n"
584 "\n"
584 "\n"
585 " fd: file descriptor returned by init()\n"
585 " fd: file descriptor returned by init()\n"
586 " bufsize: size of buffer to read into, in bytes\n"
586 " bufsize: size of buffer to read into, in bytes\n"
587 "\n"
587 "\n"
588 "Return a list of event objects.\n"
588 "Return a list of event objects.\n"
589 "\n"
589 "\n"
590 "If bufsize is > 0, block until events are available to be read.\n"
590 "If bufsize is > 0, block until events are available to be read.\n"
591 "Otherwise, immediately return all events that can be read without\n"
591 "Otherwise, immediately return all events that can be read without\n"
592 "blocking.");
592 "blocking.");
593
593
594 static PyMethodDef methods[] = {
594 static PyMethodDef methods[] = {
595 {"init", init, METH_VARARGS, init_doc},
595 {"init", init, METH_VARARGS, init_doc},
596 {"add_watch", add_watch, METH_VARARGS, add_watch_doc},
596 {"add_watch", add_watch, METH_VARARGS, add_watch_doc},
597 {"remove_watch", remove_watch, METH_VARARGS, remove_watch_doc},
597 {"remove_watch", remove_watch, METH_VARARGS, remove_watch_doc},
598 {"read", read_events, METH_VARARGS, read_doc},
598 {"read", read_events, METH_VARARGS, read_doc},
599 {"decode_mask", pydecode_mask, METH_VARARGS, decode_mask_doc},
599 {"decode_mask", pydecode_mask, METH_VARARGS, decode_mask_doc},
600 {NULL},
600 {NULL},
601 };
601 };
602
602
603 #ifdef IS_PY3K
603 #ifdef IS_PY3K
604 static struct PyModuleDef _inotify_module = {
604 static struct PyModuleDef _inotify_module = {
605 PyModuleDef_HEAD_INIT,
605 PyModuleDef_HEAD_INIT,
606 "_inotify",
606 "_inotify",
607 doc,
607 doc,
608 -1,
608 -1,
609 methods
609 methods
610 };
610 };
611
611
612 PyMODINIT_FUNC PyInit__inotify(void)
612 PyMODINIT_FUNC PyInit__inotify(void)
613 {
613 {
614 PyObject *mod, *dict;
614 PyObject *mod, *dict;
615
615
616 mod = PyModule_Create(&_inotify_module);
616 mod = PyModule_Create(&_inotify_module);
617
617
618 if (mod == NULL)
618 if (mod == NULL)
619 return NULL;
619 return NULL;
620
620
621 if (!init_globals())
621 if (!init_globals())
622 return;
622 return;
623
623
624 dict = PyModule_GetDict(mod);
624 dict = PyModule_GetDict(mod);
625
625
626 if (dict)
626 if (dict)
627 define_consts(dict);
627 define_consts(dict);
628
628
629 return mod;
629 return mod;
630 }
630 }
631 #else
631 #else
632 void init_inotify(void)
632 void init_inotify(void)
633 {
633 {
634 PyObject *mod, *dict;
634 PyObject *mod, *dict;
635
635
636 if (PyType_Ready(&event_type) == -1)
636 if (PyType_Ready(&event_type) == -1)
637 return;
637 return;
638
638
639 if (!init_globals())
639 if (!init_globals())
640 return;
640 return;
641
641
642 mod = Py_InitModule3("_inotify", methods, doc);
642 mod = Py_InitModule3("_inotify", methods, doc);
643
643
644 dict = PyModule_GetDict(mod);
644 dict = PyModule_GetDict(mod);
645
645
646 if (dict)
646 if (dict)
647 define_consts(dict);
647 define_consts(dict);
648 }
648 }
649 #endif
649 #endif
650
651 /* Local Variables: */
652 /* c-file-style: "linux" */
653 /* indent-tabs-mode: t */
654 /* End: */
@@ -1,180 +1,185 b''
1 /*
1 /*
2 base85 codec
2 base85 codec
3
3
4 Copyright 2006 Brendan Cully <brendan@kublai.com>
4 Copyright 2006 Brendan Cully <brendan@kublai.com>
5
5
6 This software may be used and distributed according to the terms of
6 This software may be used and distributed according to the terms of
7 the GNU General Public License, incorporated herein by reference.
7 the GNU General Public License, incorporated herein by reference.
8
8
9 Largely based on git's implementation
9 Largely based on git's implementation
10 */
10 */
11
11
12 #include <Python.h>
12 #include <Python.h>
13
13
14 #include "util.h"
14 #include "util.h"
15
15
16 static const char b85chars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
16 static const char b85chars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
17 "abcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
17 "abcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
18 static char b85dec[256];
18 static char b85dec[256];
19
19
20 static void
20 static void
21 b85prep(void)
21 b85prep(void)
22 {
22 {
23 int i;
23 int i;
24
24
25 memset(b85dec, 0, sizeof(b85dec));
25 memset(b85dec, 0, sizeof(b85dec));
26 for (i = 0; i < sizeof(b85chars); i++)
26 for (i = 0; i < sizeof(b85chars); i++)
27 b85dec[(int)(b85chars[i])] = i + 1;
27 b85dec[(int)(b85chars[i])] = i + 1;
28 }
28 }
29
29
30 static PyObject *
30 static PyObject *
31 b85encode(PyObject *self, PyObject *args)
31 b85encode(PyObject *self, PyObject *args)
32 {
32 {
33 const unsigned char *text;
33 const unsigned char *text;
34 PyObject *out;
34 PyObject *out;
35 char *dst;
35 char *dst;
36 int len, olen, i;
36 int len, olen, i;
37 unsigned int acc, val, ch;
37 unsigned int acc, val, ch;
38 int pad = 0;
38 int pad = 0;
39
39
40 if (!PyArg_ParseTuple(args, "s#|i", &text, &len, &pad))
40 if (!PyArg_ParseTuple(args, "s#|i", &text, &len, &pad))
41 return NULL;
41 return NULL;
42
42
43 if (pad)
43 if (pad)
44 olen = ((len + 3) / 4 * 5) - 3;
44 olen = ((len + 3) / 4 * 5) - 3;
45 else {
45 else {
46 olen = len % 4;
46 olen = len % 4;
47 if (olen)
47 if (olen)
48 olen++;
48 olen++;
49 olen += len / 4 * 5;
49 olen += len / 4 * 5;
50 }
50 }
51 if (!(out = PyBytes_FromStringAndSize(NULL, olen + 3)))
51 if (!(out = PyBytes_FromStringAndSize(NULL, olen + 3)))
52 return NULL;
52 return NULL;
53
53
54 dst = PyBytes_AsString(out);
54 dst = PyBytes_AsString(out);
55
55
56 while (len) {
56 while (len) {
57 acc = 0;
57 acc = 0;
58 for (i = 24; i >= 0; i -= 8) {
58 for (i = 24; i >= 0; i -= 8) {
59 ch = *text++;
59 ch = *text++;
60 acc |= ch << i;
60 acc |= ch << i;
61 if (--len == 0)
61 if (--len == 0)
62 break;
62 break;
63 }
63 }
64 for (i = 4; i >= 0; i--) {
64 for (i = 4; i >= 0; i--) {
65 val = acc % 85;
65 val = acc % 85;
66 acc /= 85;
66 acc /= 85;
67 dst[i] = b85chars[val];
67 dst[i] = b85chars[val];
68 }
68 }
69 dst += 5;
69 dst += 5;
70 }
70 }
71
71
72 if (!pad)
72 if (!pad)
73 _PyBytes_Resize(&out, olen);
73 _PyBytes_Resize(&out, olen);
74
74
75 return out;
75 return out;
76 }
76 }
77
77
78 static PyObject *
78 static PyObject *
79 b85decode(PyObject *self, PyObject *args)
79 b85decode(PyObject *self, PyObject *args)
80 {
80 {
81 PyObject *out;
81 PyObject *out;
82 const char *text;
82 const char *text;
83 char *dst;
83 char *dst;
84 int len, i, j, olen, c, cap;
84 int len, i, j, olen, c, cap;
85 unsigned int acc;
85 unsigned int acc;
86
86
87 if (!PyArg_ParseTuple(args, "s#", &text, &len))
87 if (!PyArg_ParseTuple(args, "s#", &text, &len))
88 return NULL;
88 return NULL;
89
89
90 olen = len / 5 * 4;
90 olen = len / 5 * 4;
91 i = len % 5;
91 i = len % 5;
92 if (i)
92 if (i)
93 olen += i - 1;
93 olen += i - 1;
94 if (!(out = PyBytes_FromStringAndSize(NULL, olen)))
94 if (!(out = PyBytes_FromStringAndSize(NULL, olen)))
95 return NULL;
95 return NULL;
96
96
97 dst = PyBytes_AsString(out);
97 dst = PyBytes_AsString(out);
98
98
99 i = 0;
99 i = 0;
100 while (i < len)
100 while (i < len)
101 {
101 {
102 acc = 0;
102 acc = 0;
103 cap = len - i - 1;
103 cap = len - i - 1;
104 if (cap > 4)
104 if (cap > 4)
105 cap = 4;
105 cap = 4;
106 for (j = 0; j < cap; i++, j++)
106 for (j = 0; j < cap; i++, j++)
107 {
107 {
108 c = b85dec[(int)*text++] - 1;
108 c = b85dec[(int)*text++] - 1;
109 if (c < 0)
109 if (c < 0)
110 return PyErr_Format(
110 return PyErr_Format(
111 PyExc_ValueError,
111 PyExc_ValueError,
112 "Bad base85 character at position %d", i);
112 "Bad base85 character at position %d", i);
113 acc = acc * 85 + c;
113 acc = acc * 85 + c;
114 }
114 }
115 if (i++ < len)
115 if (i++ < len)
116 {
116 {
117 c = b85dec[(int)*text++] - 1;
117 c = b85dec[(int)*text++] - 1;
118 if (c < 0)
118 if (c < 0)
119 return PyErr_Format(
119 return PyErr_Format(
120 PyExc_ValueError,
120 PyExc_ValueError,
121 "Bad base85 character at position %d", i);
121 "Bad base85 character at position %d", i);
122 /* overflow detection: 0xffffffff == "|NsC0",
122 /* overflow detection: 0xffffffff == "|NsC0",
123 * "|NsC" == 0x03030303 */
123 * "|NsC" == 0x03030303 */
124 if (acc > 0x03030303 || (acc *= 85) > 0xffffffff - c)
124 if (acc > 0x03030303 || (acc *= 85) > 0xffffffff - c)
125 return PyErr_Format(
125 return PyErr_Format(
126 PyExc_ValueError,
126 PyExc_ValueError,
127 "Bad base85 sequence at position %d", i);
127 "Bad base85 sequence at position %d", i);
128 acc += c;
128 acc += c;
129 }
129 }
130
130
131 cap = olen < 4 ? olen : 4;
131 cap = olen < 4 ? olen : 4;
132 olen -= cap;
132 olen -= cap;
133 for (j = 0; j < 4 - cap; j++)
133 for (j = 0; j < 4 - cap; j++)
134 acc *= 85;
134 acc *= 85;
135 if (cap && cap < 4)
135 if (cap && cap < 4)
136 acc += 0xffffff >> (cap - 1) * 8;
136 acc += 0xffffff >> (cap - 1) * 8;
137 for (j = 0; j < cap; j++)
137 for (j = 0; j < cap; j++)
138 {
138 {
139 acc = (acc << 8) | (acc >> 24);
139 acc = (acc << 8) | (acc >> 24);
140 *dst++ = acc;
140 *dst++ = acc;
141 }
141 }
142 }
142 }
143
143
144 return out;
144 return out;
145 }
145 }
146
146
147 static char base85_doc[] = "Base85 Data Encoding";
147 static char base85_doc[] = "Base85 Data Encoding";
148
148
149 static PyMethodDef methods[] = {
149 static PyMethodDef methods[] = {
150 {"b85encode", b85encode, METH_VARARGS,
150 {"b85encode", b85encode, METH_VARARGS,
151 "Encode text in base85.\n\n"
151 "Encode text in base85.\n\n"
152 "If the second parameter is true, pad the result to a multiple of "
152 "If the second parameter is true, pad the result to a multiple of "
153 "five characters.\n"},
153 "five characters.\n"},
154 {"b85decode", b85decode, METH_VARARGS, "Decode base85 text.\n"},
154 {"b85decode", b85decode, METH_VARARGS, "Decode base85 text.\n"},
155 {NULL, NULL}
155 {NULL, NULL}
156 };
156 };
157
157
158 #ifdef IS_PY3K
158 #ifdef IS_PY3K
159 static struct PyModuleDef base85_module = {
159 static struct PyModuleDef base85_module = {
160 PyModuleDef_HEAD_INIT,
160 PyModuleDef_HEAD_INIT,
161 "base85",
161 "base85",
162 base85_doc,
162 base85_doc,
163 -1,
163 -1,
164 methods
164 methods
165 };
165 };
166
166
167 PyMODINIT_FUNC PyInit_base85(void)
167 PyMODINIT_FUNC PyInit_base85(void)
168 {
168 {
169 b85prep();
169 b85prep();
170
170
171 return PyModule_Create(&base85_module);
171 return PyModule_Create(&base85_module);
172 }
172 }
173 #else
173 #else
174 PyMODINIT_FUNC initbase85(void)
174 PyMODINIT_FUNC initbase85(void)
175 {
175 {
176 Py_InitModule3("base85", methods, base85_doc);
176 Py_InitModule3("base85", methods, base85_doc);
177
177
178 b85prep();
178 b85prep();
179 }
179 }
180 #endif
180 #endif
181
182 /* Local Variables: */
183 /* c-file-style: "linux" */
184 /* indent-tabs-mode: t */
185 /* End: */
@@ -1,453 +1,457 b''
1 /*
1 /*
2 bdiff.c - efficient binary diff extension for Mercurial
2 bdiff.c - efficient binary diff extension for Mercurial
3
3
4 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5
5
6 This software may be used and distributed according to the terms of
6 This software may be used and distributed according to the terms of
7 the GNU General Public License, incorporated herein by reference.
7 the GNU General Public License, incorporated herein by reference.
8
8
9 Based roughly on Python difflib
9 Based roughly on Python difflib
10 */
10 */
11
11
12 #include <Python.h>
12 #include <Python.h>
13 #include <stdlib.h>
13 #include <stdlib.h>
14 #include <string.h>
14 #include <string.h>
15 #include <limits.h>
15 #include <limits.h>
16
16
17 #if defined __hpux || defined __SUNPRO_C || defined _AIX
17 #if defined __hpux || defined __SUNPRO_C || defined _AIX
18 #define inline
18 #define inline
19 #endif
19 #endif
20
20
21 #ifdef __linux
21 #ifdef __linux
22 #define inline __inline
22 #define inline __inline
23 #endif
23 #endif
24
24
25 #ifdef _WIN32
25 #ifdef _WIN32
26 #ifdef _MSC_VER
26 #ifdef _MSC_VER
27 #define inline __inline
27 #define inline __inline
28 typedef unsigned long uint32_t;
28 typedef unsigned long uint32_t;
29 #else
29 #else
30 #include <stdint.h>
30 #include <stdint.h>
31 #endif
31 #endif
32 static uint32_t htonl(uint32_t x)
32 static uint32_t htonl(uint32_t x)
33 {
33 {
34 return ((x & 0x000000ffUL) << 24) |
34 return ((x & 0x000000ffUL) << 24) |
35 ((x & 0x0000ff00UL) << 8) |
35 ((x & 0x0000ff00UL) << 8) |
36 ((x & 0x00ff0000UL) >> 8) |
36 ((x & 0x00ff0000UL) >> 8) |
37 ((x & 0xff000000UL) >> 24);
37 ((x & 0xff000000UL) >> 24);
38 }
38 }
39 #else
39 #else
40 #include <sys/types.h>
40 #include <sys/types.h>
41 #if defined __BEOS__ && !defined __HAIKU__
41 #if defined __BEOS__ && !defined __HAIKU__
42 #include <ByteOrder.h>
42 #include <ByteOrder.h>
43 #else
43 #else
44 #include <arpa/inet.h>
44 #include <arpa/inet.h>
45 #endif
45 #endif
46 #include <inttypes.h>
46 #include <inttypes.h>
47 #endif
47 #endif
48
48
49 #include "util.h"
49 #include "util.h"
50
50
51 struct line {
51 struct line {
52 int h, len, n, e;
52 int h, len, n, e;
53 const char *l;
53 const char *l;
54 };
54 };
55
55
56 struct pos {
56 struct pos {
57 int pos, len;
57 int pos, len;
58 };
58 };
59
59
60 struct hunk;
60 struct hunk;
61 struct hunk {
61 struct hunk {
62 int a1, a2, b1, b2;
62 int a1, a2, b1, b2;
63 struct hunk *next;
63 struct hunk *next;
64 };
64 };
65
65
66 int splitlines(const char *a, int len, struct line **lr)
66 int splitlines(const char *a, int len, struct line **lr)
67 {
67 {
68 int h, i;
68 int h, i;
69 const char *p, *b = a;
69 const char *p, *b = a;
70 const char * const plast = a + len - 1;
70 const char * const plast = a + len - 1;
71 struct line *l;
71 struct line *l;
72
72
73 /* count the lines */
73 /* count the lines */
74 i = 1; /* extra line for sentinel */
74 i = 1; /* extra line for sentinel */
75 for (p = a; p < a + len; p++)
75 for (p = a; p < a + len; p++)
76 if (*p == '\n' || p == plast)
76 if (*p == '\n' || p == plast)
77 i++;
77 i++;
78
78
79 *lr = l = (struct line *)malloc(sizeof(struct line) * i);
79 *lr = l = (struct line *)malloc(sizeof(struct line) * i);
80 if (!l)
80 if (!l)
81 return -1;
81 return -1;
82
82
83 /* build the line array and calculate hashes */
83 /* build the line array and calculate hashes */
84 h = 0;
84 h = 0;
85 for (p = a; p < a + len; p++) {
85 for (p = a; p < a + len; p++) {
86 /* Leonid Yuriev's hash */
86 /* Leonid Yuriev's hash */
87 h = (h * 1664525) + *p + 1013904223;
87 h = (h * 1664525) + *p + 1013904223;
88
88
89 if (*p == '\n' || p == plast) {
89 if (*p == '\n' || p == plast) {
90 l->h = h;
90 l->h = h;
91 h = 0;
91 h = 0;
92 l->len = p - b + 1;
92 l->len = p - b + 1;
93 l->l = b;
93 l->l = b;
94 l->n = INT_MAX;
94 l->n = INT_MAX;
95 l++;
95 l++;
96 b = p + 1;
96 b = p + 1;
97 }
97 }
98 }
98 }
99
99
100 /* set up a sentinel */
100 /* set up a sentinel */
101 l->h = l->len = 0;
101 l->h = l->len = 0;
102 l->l = a + len;
102 l->l = a + len;
103 return i - 1;
103 return i - 1;
104 }
104 }
105
105
106 int inline cmp(struct line *a, struct line *b)
106 int inline cmp(struct line *a, struct line *b)
107 {
107 {
108 return a->h != b->h || a->len != b->len || memcmp(a->l, b->l, a->len);
108 return a->h != b->h || a->len != b->len || memcmp(a->l, b->l, a->len);
109 }
109 }
110
110
111 static int equatelines(struct line *a, int an, struct line *b, int bn)
111 static int equatelines(struct line *a, int an, struct line *b, int bn)
112 {
112 {
113 int i, j, buckets = 1, t, scale;
113 int i, j, buckets = 1, t, scale;
114 struct pos *h = NULL;
114 struct pos *h = NULL;
115
115
116 /* build a hash table of the next highest power of 2 */
116 /* build a hash table of the next highest power of 2 */
117 while (buckets < bn + 1)
117 while (buckets < bn + 1)
118 buckets *= 2;
118 buckets *= 2;
119
119
120 /* try to allocate a large hash table to avoid collisions */
120 /* try to allocate a large hash table to avoid collisions */
121 for (scale = 4; scale; scale /= 2) {
121 for (scale = 4; scale; scale /= 2) {
122 h = (struct pos *)malloc(scale * buckets * sizeof(struct pos));
122 h = (struct pos *)malloc(scale * buckets * sizeof(struct pos));
123 if (h)
123 if (h)
124 break;
124 break;
125 }
125 }
126
126
127 if (!h)
127 if (!h)
128 return 0;
128 return 0;
129
129
130 buckets = buckets * scale - 1;
130 buckets = buckets * scale - 1;
131
131
132 /* clear the hash table */
132 /* clear the hash table */
133 for (i = 0; i <= buckets; i++) {
133 for (i = 0; i <= buckets; i++) {
134 h[i].pos = INT_MAX;
134 h[i].pos = INT_MAX;
135 h[i].len = 0;
135 h[i].len = 0;
136 }
136 }
137
137
138 /* add lines to the hash table chains */
138 /* add lines to the hash table chains */
139 for (i = bn - 1; i >= 0; i--) {
139 for (i = bn - 1; i >= 0; i--) {
140 /* find the equivalence class */
140 /* find the equivalence class */
141 for (j = b[i].h & buckets; h[j].pos != INT_MAX;
141 for (j = b[i].h & buckets; h[j].pos != INT_MAX;
142 j = (j + 1) & buckets)
142 j = (j + 1) & buckets)
143 if (!cmp(b + i, b + h[j].pos))
143 if (!cmp(b + i, b + h[j].pos))
144 break;
144 break;
145
145
146 /* add to the head of the equivalence class */
146 /* add to the head of the equivalence class */
147 b[i].n = h[j].pos;
147 b[i].n = h[j].pos;
148 b[i].e = j;
148 b[i].e = j;
149 h[j].pos = i;
149 h[j].pos = i;
150 h[j].len++; /* keep track of popularity */
150 h[j].len++; /* keep track of popularity */
151 }
151 }
152
152
153 /* compute popularity threshold */
153 /* compute popularity threshold */
154 t = (bn >= 31000) ? bn / 1000 : 1000000 / (bn + 1);
154 t = (bn >= 31000) ? bn / 1000 : 1000000 / (bn + 1);
155
155
156 /* match items in a to their equivalence class in b */
156 /* match items in a to their equivalence class in b */
157 for (i = 0; i < an; i++) {
157 for (i = 0; i < an; i++) {
158 /* find the equivalence class */
158 /* find the equivalence class */
159 for (j = a[i].h & buckets; h[j].pos != INT_MAX;
159 for (j = a[i].h & buckets; h[j].pos != INT_MAX;
160 j = (j + 1) & buckets)
160 j = (j + 1) & buckets)
161 if (!cmp(a + i, b + h[j].pos))
161 if (!cmp(a + i, b + h[j].pos))
162 break;
162 break;
163
163
164 a[i].e = j; /* use equivalence class for quick compare */
164 a[i].e = j; /* use equivalence class for quick compare */
165 if (h[j].len <= t)
165 if (h[j].len <= t)
166 a[i].n = h[j].pos; /* point to head of match list */
166 a[i].n = h[j].pos; /* point to head of match list */
167 else
167 else
168 a[i].n = INT_MAX; /* too popular */
168 a[i].n = INT_MAX; /* too popular */
169 }
169 }
170
170
171 /* discard hash tables */
171 /* discard hash tables */
172 free(h);
172 free(h);
173 return 1;
173 return 1;
174 }
174 }
175
175
176 static int longest_match(struct line *a, struct line *b, struct pos *pos,
176 static int longest_match(struct line *a, struct line *b, struct pos *pos,
177 int a1, int a2, int b1, int b2, int *omi, int *omj)
177 int a1, int a2, int b1, int b2, int *omi, int *omj)
178 {
178 {
179 int mi = a1, mj = b1, mk = 0, mb = 0, i, j, k;
179 int mi = a1, mj = b1, mk = 0, mb = 0, i, j, k;
180
180
181 for (i = a1; i < a2; i++) {
181 for (i = a1; i < a2; i++) {
182 /* skip things before the current block */
182 /* skip things before the current block */
183 for (j = a[i].n; j < b1; j = b[j].n)
183 for (j = a[i].n; j < b1; j = b[j].n)
184 ;
184 ;
185
185
186 /* loop through all lines match a[i] in b */
186 /* loop through all lines match a[i] in b */
187 for (; j < b2; j = b[j].n) {
187 for (; j < b2; j = b[j].n) {
188 /* does this extend an earlier match? */
188 /* does this extend an earlier match? */
189 if (i > a1 && j > b1 && pos[j - 1].pos == i - 1)
189 if (i > a1 && j > b1 && pos[j - 1].pos == i - 1)
190 k = pos[j - 1].len + 1;
190 k = pos[j - 1].len + 1;
191 else
191 else
192 k = 1;
192 k = 1;
193 pos[j].pos = i;
193 pos[j].pos = i;
194 pos[j].len = k;
194 pos[j].len = k;
195
195
196 /* best match so far? */
196 /* best match so far? */
197 if (k > mk) {
197 if (k > mk) {
198 mi = i;
198 mi = i;
199 mj = j;
199 mj = j;
200 mk = k;
200 mk = k;
201 }
201 }
202 }
202 }
203 }
203 }
204
204
205 if (mk) {
205 if (mk) {
206 mi = mi - mk + 1;
206 mi = mi - mk + 1;
207 mj = mj - mk + 1;
207 mj = mj - mk + 1;
208 }
208 }
209
209
210 /* expand match to include neighboring popular lines */
210 /* expand match to include neighboring popular lines */
211 while (mi - mb > a1 && mj - mb > b1 &&
211 while (mi - mb > a1 && mj - mb > b1 &&
212 a[mi - mb - 1].e == b[mj - mb - 1].e)
212 a[mi - mb - 1].e == b[mj - mb - 1].e)
213 mb++;
213 mb++;
214 while (mi + mk < a2 && mj + mk < b2 &&
214 while (mi + mk < a2 && mj + mk < b2 &&
215 a[mi + mk].e == b[mj + mk].e)
215 a[mi + mk].e == b[mj + mk].e)
216 mk++;
216 mk++;
217
217
218 *omi = mi - mb;
218 *omi = mi - mb;
219 *omj = mj - mb;
219 *omj = mj - mb;
220
220
221 return mk + mb;
221 return mk + mb;
222 }
222 }
223
223
224 static struct hunk *recurse(struct line *a, struct line *b, struct pos *pos,
224 static struct hunk *recurse(struct line *a, struct line *b, struct pos *pos,
225 int a1, int a2, int b1, int b2, struct hunk *l)
225 int a1, int a2, int b1, int b2, struct hunk *l)
226 {
226 {
227 int i, j, k;
227 int i, j, k;
228
228
229 while (1) {
229 while (1) {
230 /* find the longest match in this chunk */
230 /* find the longest match in this chunk */
231 k = longest_match(a, b, pos, a1, a2, b1, b2, &i, &j);
231 k = longest_match(a, b, pos, a1, a2, b1, b2, &i, &j);
232 if (!k)
232 if (!k)
233 return l;
233 return l;
234
234
235 /* and recurse on the remaining chunks on either side */
235 /* and recurse on the remaining chunks on either side */
236 l = recurse(a, b, pos, a1, i, b1, j, l);
236 l = recurse(a, b, pos, a1, i, b1, j, l);
237 if (!l)
237 if (!l)
238 return NULL;
238 return NULL;
239
239
240 l->next = (struct hunk *)malloc(sizeof(struct hunk));
240 l->next = (struct hunk *)malloc(sizeof(struct hunk));
241 if (!l->next)
241 if (!l->next)
242 return NULL;
242 return NULL;
243
243
244 l = l->next;
244 l = l->next;
245 l->a1 = i;
245 l->a1 = i;
246 l->a2 = i + k;
246 l->a2 = i + k;
247 l->b1 = j;
247 l->b1 = j;
248 l->b2 = j + k;
248 l->b2 = j + k;
249 l->next = NULL;
249 l->next = NULL;
250
250
251 /* tail-recursion didn't happen, so do equivalent iteration */
251 /* tail-recursion didn't happen, so do equivalent iteration */
252 a1 = i + k;
252 a1 = i + k;
253 b1 = j + k;
253 b1 = j + k;
254 }
254 }
255 }
255 }
256
256
257 static int diff(struct line *a, int an, struct line *b, int bn,
257 static int diff(struct line *a, int an, struct line *b, int bn,
258 struct hunk *base)
258 struct hunk *base)
259 {
259 {
260 struct hunk *curr;
260 struct hunk *curr;
261 struct pos *pos;
261 struct pos *pos;
262 int t, count = 0;
262 int t, count = 0;
263
263
264 /* allocate and fill arrays */
264 /* allocate and fill arrays */
265 t = equatelines(a, an, b, bn);
265 t = equatelines(a, an, b, bn);
266 pos = (struct pos *)calloc(bn ? bn : 1, sizeof(struct pos));
266 pos = (struct pos *)calloc(bn ? bn : 1, sizeof(struct pos));
267
267
268 if (pos && t) {
268 if (pos && t) {
269 /* generate the matching block list */
269 /* generate the matching block list */
270
270
271 curr = recurse(a, b, pos, 0, an, 0, bn, base);
271 curr = recurse(a, b, pos, 0, an, 0, bn, base);
272 if (!curr)
272 if (!curr)
273 return -1;
273 return -1;
274
274
275 /* sentinel end hunk */
275 /* sentinel end hunk */
276 curr->next = (struct hunk *)malloc(sizeof(struct hunk));
276 curr->next = (struct hunk *)malloc(sizeof(struct hunk));
277 if (!curr->next)
277 if (!curr->next)
278 return -1;
278 return -1;
279 curr = curr->next;
279 curr = curr->next;
280 curr->a1 = curr->a2 = an;
280 curr->a1 = curr->a2 = an;
281 curr->b1 = curr->b2 = bn;
281 curr->b1 = curr->b2 = bn;
282 curr->next = NULL;
282 curr->next = NULL;
283 }
283 }
284
284
285 free(pos);
285 free(pos);
286
286
287 /* normalize the hunk list, try to push each hunk towards the end */
287 /* normalize the hunk list, try to push each hunk towards the end */
288 for (curr = base->next; curr; curr = curr->next) {
288 for (curr = base->next; curr; curr = curr->next) {
289 struct hunk *next = curr->next;
289 struct hunk *next = curr->next;
290 int shift = 0;
290 int shift = 0;
291
291
292 if (!next)
292 if (!next)
293 break;
293 break;
294
294
295 if (curr->a2 == next->a1)
295 if (curr->a2 == next->a1)
296 while (curr->a2 + shift < an && curr->b2 + shift < bn
296 while (curr->a2 + shift < an && curr->b2 + shift < bn
297 && !cmp(a + curr->a2 + shift,
297 && !cmp(a + curr->a2 + shift,
298 b + curr->b2 + shift))
298 b + curr->b2 + shift))
299 shift++;
299 shift++;
300 else if (curr->b2 == next->b1)
300 else if (curr->b2 == next->b1)
301 while (curr->b2 + shift < bn && curr->a2 + shift < an
301 while (curr->b2 + shift < bn && curr->a2 + shift < an
302 && !cmp(b + curr->b2 + shift,
302 && !cmp(b + curr->b2 + shift,
303 a + curr->a2 + shift))
303 a + curr->a2 + shift))
304 shift++;
304 shift++;
305 if (!shift)
305 if (!shift)
306 continue;
306 continue;
307 curr->b2 += shift;
307 curr->b2 += shift;
308 next->b1 += shift;
308 next->b1 += shift;
309 curr->a2 += shift;
309 curr->a2 += shift;
310 next->a1 += shift;
310 next->a1 += shift;
311 }
311 }
312
312
313 for (curr = base->next; curr; curr = curr->next)
313 for (curr = base->next; curr; curr = curr->next)
314 count++;
314 count++;
315 return count;
315 return count;
316 }
316 }
317
317
318 static void freehunks(struct hunk *l)
318 static void freehunks(struct hunk *l)
319 {
319 {
320 struct hunk *n;
320 struct hunk *n;
321 for (; l; l = n) {
321 for (; l; l = n) {
322 n = l->next;
322 n = l->next;
323 free(l);
323 free(l);
324 }
324 }
325 }
325 }
326
326
327 static PyObject *blocks(PyObject *self, PyObject *args)
327 static PyObject *blocks(PyObject *self, PyObject *args)
328 {
328 {
329 PyObject *sa, *sb, *rl = NULL, *m;
329 PyObject *sa, *sb, *rl = NULL, *m;
330 struct line *a, *b;
330 struct line *a, *b;
331 struct hunk l, *h;
331 struct hunk l, *h;
332 int an, bn, count, pos = 0;
332 int an, bn, count, pos = 0;
333
333
334 if (!PyArg_ParseTuple(args, "SS:bdiff", &sa, &sb))
334 if (!PyArg_ParseTuple(args, "SS:bdiff", &sa, &sb))
335 return NULL;
335 return NULL;
336
336
337 an = splitlines(PyBytes_AsString(sa), PyBytes_Size(sa), &a);
337 an = splitlines(PyBytes_AsString(sa), PyBytes_Size(sa), &a);
338 bn = splitlines(PyBytes_AsString(sb), PyBytes_Size(sb), &b);
338 bn = splitlines(PyBytes_AsString(sb), PyBytes_Size(sb), &b);
339
339
340 if (!a || !b)
340 if (!a || !b)
341 goto nomem;
341 goto nomem;
342
342
343 l.next = NULL;
343 l.next = NULL;
344 count = diff(a, an, b, bn, &l);
344 count = diff(a, an, b, bn, &l);
345 if (count < 0)
345 if (count < 0)
346 goto nomem;
346 goto nomem;
347
347
348 rl = PyList_New(count);
348 rl = PyList_New(count);
349 if (!rl)
349 if (!rl)
350 goto nomem;
350 goto nomem;
351
351
352 for (h = l.next; h; h = h->next) {
352 for (h = l.next; h; h = h->next) {
353 m = Py_BuildValue("iiii", h->a1, h->a2, h->b1, h->b2);
353 m = Py_BuildValue("iiii", h->a1, h->a2, h->b1, h->b2);
354 PyList_SetItem(rl, pos, m);
354 PyList_SetItem(rl, pos, m);
355 pos++;
355 pos++;
356 }
356 }
357
357
358 nomem:
358 nomem:
359 free(a);
359 free(a);
360 free(b);
360 free(b);
361 freehunks(l.next);
361 freehunks(l.next);
362 return rl ? rl : PyErr_NoMemory();
362 return rl ? rl : PyErr_NoMemory();
363 }
363 }
364
364
365 static PyObject *bdiff(PyObject *self, PyObject *args)
365 static PyObject *bdiff(PyObject *self, PyObject *args)
366 {
366 {
367 char *sa, *sb;
367 char *sa, *sb;
368 PyObject *result = NULL;
368 PyObject *result = NULL;
369 struct line *al, *bl;
369 struct line *al, *bl;
370 struct hunk l, *h;
370 struct hunk l, *h;
371 char encode[12], *rb;
371 char encode[12], *rb;
372 int an, bn, len = 0, la, lb, count;
372 int an, bn, len = 0, la, lb, count;
373
373
374 if (!PyArg_ParseTuple(args, "s#s#:bdiff", &sa, &la, &sb, &lb))
374 if (!PyArg_ParseTuple(args, "s#s#:bdiff", &sa, &la, &sb, &lb))
375 return NULL;
375 return NULL;
376
376
377 an = splitlines(sa, la, &al);
377 an = splitlines(sa, la, &al);
378 bn = splitlines(sb, lb, &bl);
378 bn = splitlines(sb, lb, &bl);
379 if (!al || !bl)
379 if (!al || !bl)
380 goto nomem;
380 goto nomem;
381
381
382 l.next = NULL;
382 l.next = NULL;
383 count = diff(al, an, bl, bn, &l);
383 count = diff(al, an, bl, bn, &l);
384 if (count < 0)
384 if (count < 0)
385 goto nomem;
385 goto nomem;
386
386
387 /* calculate length of output */
387 /* calculate length of output */
388 la = lb = 0;
388 la = lb = 0;
389 for (h = l.next; h; h = h->next) {
389 for (h = l.next; h; h = h->next) {
390 if (h->a1 != la || h->b1 != lb)
390 if (h->a1 != la || h->b1 != lb)
391 len += 12 + bl[h->b1].l - bl[lb].l;
391 len += 12 + bl[h->b1].l - bl[lb].l;
392 la = h->a2;
392 la = h->a2;
393 lb = h->b2;
393 lb = h->b2;
394 }
394 }
395
395
396 result = PyBytes_FromStringAndSize(NULL, len);
396 result = PyBytes_FromStringAndSize(NULL, len);
397
397
398 if (!result)
398 if (!result)
399 goto nomem;
399 goto nomem;
400
400
401 /* build binary patch */
401 /* build binary patch */
402 rb = PyBytes_AsString(result);
402 rb = PyBytes_AsString(result);
403 la = lb = 0;
403 la = lb = 0;
404
404
405 for (h = l.next; h; h = h->next) {
405 for (h = l.next; h; h = h->next) {
406 if (h->a1 != la || h->b1 != lb) {
406 if (h->a1 != la || h->b1 != lb) {
407 len = bl[h->b1].l - bl[lb].l;
407 len = bl[h->b1].l - bl[lb].l;
408 *(uint32_t *)(encode) = htonl(al[la].l - al->l);
408 *(uint32_t *)(encode) = htonl(al[la].l - al->l);
409 *(uint32_t *)(encode + 4) = htonl(al[h->a1].l - al->l);
409 *(uint32_t *)(encode + 4) = htonl(al[h->a1].l - al->l);
410 *(uint32_t *)(encode + 8) = htonl(len);
410 *(uint32_t *)(encode + 8) = htonl(len);
411 memcpy(rb, encode, 12);
411 memcpy(rb, encode, 12);
412 memcpy(rb + 12, bl[lb].l, len);
412 memcpy(rb + 12, bl[lb].l, len);
413 rb += 12 + len;
413 rb += 12 + len;
414 }
414 }
415 la = h->a2;
415 la = h->a2;
416 lb = h->b2;
416 lb = h->b2;
417 }
417 }
418
418
419 nomem:
419 nomem:
420 free(al);
420 free(al);
421 free(bl);
421 free(bl);
422 freehunks(l.next);
422 freehunks(l.next);
423 return result ? result : PyErr_NoMemory();
423 return result ? result : PyErr_NoMemory();
424 }
424 }
425
425
426 static char mdiff_doc[] = "Efficient binary diff.";
426 static char mdiff_doc[] = "Efficient binary diff.";
427
427
428 static PyMethodDef methods[] = {
428 static PyMethodDef methods[] = {
429 {"bdiff", bdiff, METH_VARARGS, "calculate a binary diff\n"},
429 {"bdiff", bdiff, METH_VARARGS, "calculate a binary diff\n"},
430 {"blocks", blocks, METH_VARARGS, "find a list of matching lines\n"},
430 {"blocks", blocks, METH_VARARGS, "find a list of matching lines\n"},
431 {NULL, NULL}
431 {NULL, NULL}
432 };
432 };
433
433
434 #ifdef IS_PY3K
434 #ifdef IS_PY3K
435 static struct PyModuleDef bdiff_module = {
435 static struct PyModuleDef bdiff_module = {
436 PyModuleDef_HEAD_INIT,
436 PyModuleDef_HEAD_INIT,
437 "bdiff",
437 "bdiff",
438 mdiff_doc,
438 mdiff_doc,
439 -1,
439 -1,
440 methods
440 methods
441 };
441 };
442
442
443 PyMODINIT_FUNC PyInit_bdiff(void)
443 PyMODINIT_FUNC PyInit_bdiff(void)
444 {
444 {
445 return PyModule_Create(&bdiff_module);
445 return PyModule_Create(&bdiff_module);
446 }
446 }
447 #else
447 #else
448 PyMODINIT_FUNC initbdiff(void)
448 PyMODINIT_FUNC initbdiff(void)
449 {
449 {
450 Py_InitModule3("bdiff", methods, mdiff_doc);
450 Py_InitModule3("bdiff", methods, mdiff_doc);
451 }
451 }
452 #endif
452 #endif
453
453
454 /* Local Variables: */
455 /* c-file-style: "linux" */
456 /* indent-tabs-mode: t */
457 /* End: */
@@ -1,190 +1,194 b''
1 /*
1 /*
2 * diffhelpers.c - helper routines for mpatch
2 * diffhelpers.c - helper routines for mpatch
3 *
3 *
4 * Copyright 2007 Chris Mason <chris.mason@oracle.com>
4 * Copyright 2007 Chris Mason <chris.mason@oracle.com>
5 *
5 *
6 * This software may be used and distributed according to the terms
6 * This software may be used and distributed according to the terms
7 * of the GNU General Public License v2, incorporated herein by reference.
7 * of the GNU General Public License v2, incorporated herein by reference.
8 */
8 */
9
9
10 #include <Python.h>
10 #include <Python.h>
11 #include <stdlib.h>
11 #include <stdlib.h>
12 #include <string.h>
12 #include <string.h>
13
13
14 #include "util.h"
14 #include "util.h"
15
15
16 static char diffhelpers_doc[] = "Efficient diff parsing";
16 static char diffhelpers_doc[] = "Efficient diff parsing";
17 static PyObject *diffhelpers_Error;
17 static PyObject *diffhelpers_Error;
18
18
19
19
20 /* fixup the last lines of a and b when the patch has no newline at eof */
20 /* fixup the last lines of a and b when the patch has no newline at eof */
21 static void _fix_newline(PyObject *hunk, PyObject *a, PyObject *b)
21 static void _fix_newline(PyObject *hunk, PyObject *a, PyObject *b)
22 {
22 {
23 int hunksz = PyList_Size(hunk);
23 int hunksz = PyList_Size(hunk);
24 PyObject *s = PyList_GET_ITEM(hunk, hunksz-1);
24 PyObject *s = PyList_GET_ITEM(hunk, hunksz-1);
25 char *l = PyBytes_AsString(s);
25 char *l = PyBytes_AsString(s);
26 int alen = PyList_Size(a);
26 int alen = PyList_Size(a);
27 int blen = PyList_Size(b);
27 int blen = PyList_Size(b);
28 char c = l[0];
28 char c = l[0];
29 PyObject *hline;
29 PyObject *hline;
30 int sz = PyBytes_GET_SIZE(s);
30 int sz = PyBytes_GET_SIZE(s);
31
31
32 if (sz > 1 && l[sz-2] == '\r')
32 if (sz > 1 && l[sz-2] == '\r')
33 /* tolerate CRLF in last line */
33 /* tolerate CRLF in last line */
34 sz -= 1;
34 sz -= 1;
35
35
36 hline = PyBytes_FromStringAndSize(l, sz-1);
36 hline = PyBytes_FromStringAndSize(l, sz-1);
37
37
38 if (c == ' ' || c == '+') {
38 if (c == ' ' || c == '+') {
39 PyObject *rline = PyBytes_FromStringAndSize(l + 1, sz - 2);
39 PyObject *rline = PyBytes_FromStringAndSize(l + 1, sz - 2);
40 PyList_SetItem(b, blen-1, rline);
40 PyList_SetItem(b, blen-1, rline);
41 }
41 }
42 if (c == ' ' || c == '-') {
42 if (c == ' ' || c == '-') {
43 Py_INCREF(hline);
43 Py_INCREF(hline);
44 PyList_SetItem(a, alen-1, hline);
44 PyList_SetItem(a, alen-1, hline);
45 }
45 }
46 PyList_SetItem(hunk, hunksz-1, hline);
46 PyList_SetItem(hunk, hunksz-1, hline);
47 }
47 }
48
48
49 /* python callable form of _fix_newline */
49 /* python callable form of _fix_newline */
50 static PyObject *
50 static PyObject *
51 fix_newline(PyObject *self, PyObject *args)
51 fix_newline(PyObject *self, PyObject *args)
52 {
52 {
53 PyObject *hunk, *a, *b;
53 PyObject *hunk, *a, *b;
54 if (!PyArg_ParseTuple(args, "OOO", &hunk, &a, &b))
54 if (!PyArg_ParseTuple(args, "OOO", &hunk, &a, &b))
55 return NULL;
55 return NULL;
56 _fix_newline(hunk, a, b);
56 _fix_newline(hunk, a, b);
57 return Py_BuildValue("l", 0);
57 return Py_BuildValue("l", 0);
58 }
58 }
59
59
60 /*
60 /*
61 * read lines from fp into the hunk. The hunk is parsed into two arrays
61 * read lines from fp into the hunk. The hunk is parsed into two arrays
62 * a and b. a gets the old state of the text, b gets the new state
62 * a and b. a gets the old state of the text, b gets the new state
63 * The control char from the hunk is saved when inserting into a, but not b
63 * The control char from the hunk is saved when inserting into a, but not b
64 * (for performance while deleting files)
64 * (for performance while deleting files)
65 */
65 */
66 static PyObject *
66 static PyObject *
67 addlines(PyObject *self, PyObject *args)
67 addlines(PyObject *self, PyObject *args)
68 {
68 {
69
69
70 PyObject *fp, *hunk, *a, *b, *x;
70 PyObject *fp, *hunk, *a, *b, *x;
71 int i;
71 int i;
72 int lena, lenb;
72 int lena, lenb;
73 int num;
73 int num;
74 int todoa, todob;
74 int todoa, todob;
75 char *s, c;
75 char *s, c;
76 PyObject *l;
76 PyObject *l;
77 if (!PyArg_ParseTuple(args, "OOiiOO", &fp, &hunk, &lena, &lenb, &a, &b))
77 if (!PyArg_ParseTuple(args, "OOiiOO", &fp, &hunk, &lena, &lenb, &a, &b))
78 return NULL;
78 return NULL;
79
79
80 while (1) {
80 while (1) {
81 todoa = lena - PyList_Size(a);
81 todoa = lena - PyList_Size(a);
82 todob = lenb - PyList_Size(b);
82 todob = lenb - PyList_Size(b);
83 num = todoa > todob ? todoa : todob;
83 num = todoa > todob ? todoa : todob;
84 if (num == 0)
84 if (num == 0)
85 break;
85 break;
86 for (i = 0; i < num; i++) {
86 for (i = 0; i < num; i++) {
87 x = PyFile_GetLine(fp, 0);
87 x = PyFile_GetLine(fp, 0);
88 s = PyBytes_AsString(x);
88 s = PyBytes_AsString(x);
89 c = *s;
89 c = *s;
90 if (strcmp(s, "\\ No newline at end of file\n") == 0) {
90 if (strcmp(s, "\\ No newline at end of file\n") == 0) {
91 _fix_newline(hunk, a, b);
91 _fix_newline(hunk, a, b);
92 continue;
92 continue;
93 }
93 }
94 if (c == '\n') {
94 if (c == '\n') {
95 /* Some patches may be missing the control char
95 /* Some patches may be missing the control char
96 * on empty lines. Supply a leading space. */
96 * on empty lines. Supply a leading space. */
97 Py_DECREF(x);
97 Py_DECREF(x);
98 x = PyBytes_FromString(" \n");
98 x = PyBytes_FromString(" \n");
99 }
99 }
100 PyList_Append(hunk, x);
100 PyList_Append(hunk, x);
101 if (c == '+') {
101 if (c == '+') {
102 l = PyBytes_FromString(s + 1);
102 l = PyBytes_FromString(s + 1);
103 PyList_Append(b, l);
103 PyList_Append(b, l);
104 Py_DECREF(l);
104 Py_DECREF(l);
105 } else if (c == '-') {
105 } else if (c == '-') {
106 PyList_Append(a, x);
106 PyList_Append(a, x);
107 } else {
107 } else {
108 l = PyBytes_FromString(s + 1);
108 l = PyBytes_FromString(s + 1);
109 PyList_Append(b, l);
109 PyList_Append(b, l);
110 Py_DECREF(l);
110 Py_DECREF(l);
111 PyList_Append(a, x);
111 PyList_Append(a, x);
112 }
112 }
113 Py_DECREF(x);
113 Py_DECREF(x);
114 }
114 }
115 }
115 }
116 return Py_BuildValue("l", 0);
116 return Py_BuildValue("l", 0);
117 }
117 }
118
118
119 /*
119 /*
120 * compare the lines in a with the lines in b. a is assumed to have
120 * compare the lines in a with the lines in b. a is assumed to have
121 * a control char at the start of each line, this char is ignored in the
121 * a control char at the start of each line, this char is ignored in the
122 * compare
122 * compare
123 */
123 */
124 static PyObject *
124 static PyObject *
125 testhunk(PyObject *self, PyObject *args)
125 testhunk(PyObject *self, PyObject *args)
126 {
126 {
127
127
128 PyObject *a, *b;
128 PyObject *a, *b;
129 long bstart;
129 long bstart;
130 int alen, blen;
130 int alen, blen;
131 int i;
131 int i;
132 char *sa, *sb;
132 char *sa, *sb;
133
133
134 if (!PyArg_ParseTuple(args, "OOl", &a, &b, &bstart))
134 if (!PyArg_ParseTuple(args, "OOl", &a, &b, &bstart))
135 return NULL;
135 return NULL;
136 alen = PyList_Size(a);
136 alen = PyList_Size(a);
137 blen = PyList_Size(b);
137 blen = PyList_Size(b);
138 if (alen > blen - bstart) {
138 if (alen > blen - bstart) {
139 return Py_BuildValue("l", -1);
139 return Py_BuildValue("l", -1);
140 }
140 }
141 for (i = 0; i < alen; i++) {
141 for (i = 0; i < alen; i++) {
142 sa = PyBytes_AsString(PyList_GET_ITEM(a, i));
142 sa = PyBytes_AsString(PyList_GET_ITEM(a, i));
143 sb = PyBytes_AsString(PyList_GET_ITEM(b, i + bstart));
143 sb = PyBytes_AsString(PyList_GET_ITEM(b, i + bstart));
144 if (strcmp(sa + 1, sb) != 0)
144 if (strcmp(sa + 1, sb) != 0)
145 return Py_BuildValue("l", -1);
145 return Py_BuildValue("l", -1);
146 }
146 }
147 return Py_BuildValue("l", 0);
147 return Py_BuildValue("l", 0);
148 }
148 }
149
149
150 static PyMethodDef methods[] = {
150 static PyMethodDef methods[] = {
151 {"addlines", addlines, METH_VARARGS, "add lines to a hunk\n"},
151 {"addlines", addlines, METH_VARARGS, "add lines to a hunk\n"},
152 {"fix_newline", fix_newline, METH_VARARGS, "fixup newline counters\n"},
152 {"fix_newline", fix_newline, METH_VARARGS, "fixup newline counters\n"},
153 {"testhunk", testhunk, METH_VARARGS, "test lines in a hunk\n"},
153 {"testhunk", testhunk, METH_VARARGS, "test lines in a hunk\n"},
154 {NULL, NULL}
154 {NULL, NULL}
155 };
155 };
156
156
157 #ifdef IS_PY3K
157 #ifdef IS_PY3K
158 static struct PyModuleDef diffhelpers_module = {
158 static struct PyModuleDef diffhelpers_module = {
159 PyModuleDef_HEAD_INIT,
159 PyModuleDef_HEAD_INIT,
160 "diffhelpers",
160 "diffhelpers",
161 diffhelpers_doc,
161 diffhelpers_doc,
162 -1,
162 -1,
163 methods
163 methods
164 };
164 };
165
165
166 PyMODINIT_FUNC PyInit_diffhelpers(void)
166 PyMODINIT_FUNC PyInit_diffhelpers(void)
167 {
167 {
168 PyObject *m;
168 PyObject *m;
169
169
170 m = PyModule_Create(&diffhelpers_module);
170 m = PyModule_Create(&diffhelpers_module);
171 if (m == NULL)
171 if (m == NULL)
172 return NULL;
172 return NULL;
173
173
174 diffhelpers_Error = PyErr_NewException("diffhelpers.diffhelpersError",
174 diffhelpers_Error = PyErr_NewException("diffhelpers.diffhelpersError",
175 NULL, NULL);
175 NULL, NULL);
176 Py_INCREF(diffhelpers_Error);
176 Py_INCREF(diffhelpers_Error);
177 PyModule_AddObject(m, "diffhelpersError", diffhelpers_Error);
177 PyModule_AddObject(m, "diffhelpersError", diffhelpers_Error);
178
178
179 return m;
179 return m;
180 }
180 }
181 #else
181 #else
182 PyMODINIT_FUNC
182 PyMODINIT_FUNC
183 initdiffhelpers(void)
183 initdiffhelpers(void)
184 {
184 {
185 Py_InitModule3("diffhelpers", methods, diffhelpers_doc);
185 Py_InitModule3("diffhelpers", methods, diffhelpers_doc);
186 diffhelpers_Error = PyErr_NewException("diffhelpers.diffhelpersError",
186 diffhelpers_Error = PyErr_NewException("diffhelpers.diffhelpersError",
187 NULL, NULL);
187 NULL, NULL);
188 }
188 }
189 #endif
189 #endif
190
190
191 /* Local Variables: */
192 /* c-file-style: "linux" */
193 /* indent-tabs-mode: t */
194 /* End: */
@@ -1,470 +1,475 b''
1 /*
1 /*
2 mpatch.c - efficient binary patching for Mercurial
2 mpatch.c - efficient binary patching for Mercurial
3
3
4 This implements a patch algorithm that's O(m + nlog n) where m is the
4 This implements a patch algorithm that's O(m + nlog n) where m is the
5 size of the output and n is the number of patches.
5 size of the output and n is the number of patches.
6
6
7 Given a list of binary patches, it unpacks each into a hunk list,
7 Given a list of binary patches, it unpacks each into a hunk list,
8 then combines the hunk lists with a treewise recursion to form a
8 then combines the hunk lists with a treewise recursion to form a
9 single hunk list. This hunk list is then applied to the original
9 single hunk list. This hunk list is then applied to the original
10 text.
10 text.
11
11
12 The text (or binary) fragments are copied directly from their source
12 The text (or binary) fragments are copied directly from their source
13 Python objects into a preallocated output string to avoid the
13 Python objects into a preallocated output string to avoid the
14 allocation of intermediate Python objects. Working memory is about 2x
14 allocation of intermediate Python objects. Working memory is about 2x
15 the total number of hunks.
15 the total number of hunks.
16
16
17 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
17 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
18
18
19 This software may be used and distributed according to the terms
19 This software may be used and distributed according to the terms
20 of the GNU General Public License, incorporated herein by reference.
20 of the GNU General Public License, incorporated herein by reference.
21 */
21 */
22
22
23 #include <Python.h>
23 #include <Python.h>
24 #include <stdlib.h>
24 #include <stdlib.h>
25 #include <string.h>
25 #include <string.h>
26
26
27 #include "util.h"
27 #include "util.h"
28
28
29 /* Definitions to get compatibility with python 2.4 and earlier which
29 /* Definitions to get compatibility with python 2.4 and earlier which
30 does not have Py_ssize_t. See also PEP 353.
30 does not have Py_ssize_t. See also PEP 353.
31 Note: msvc (8 or earlier) does not have ssize_t, so we use Py_ssize_t.
31 Note: msvc (8 or earlier) does not have ssize_t, so we use Py_ssize_t.
32 */
32 */
33 #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
33 #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
34 typedef int Py_ssize_t;
34 typedef int Py_ssize_t;
35 #define PY_SSIZE_T_MAX INT_MAX
35 #define PY_SSIZE_T_MAX INT_MAX
36 #define PY_SSIZE_T_MIN INT_MIN
36 #define PY_SSIZE_T_MIN INT_MIN
37 #endif
37 #endif
38
38
39 #ifdef _WIN32
39 #ifdef _WIN32
40 #ifdef _MSC_VER
40 #ifdef _MSC_VER
41 /* msvc 6.0 has problems */
41 /* msvc 6.0 has problems */
42 #define inline __inline
42 #define inline __inline
43 typedef unsigned long uint32_t;
43 typedef unsigned long uint32_t;
44 #else
44 #else
45 #include <stdint.h>
45 #include <stdint.h>
46 #endif
46 #endif
47 static uint32_t ntohl(uint32_t x)
47 static uint32_t ntohl(uint32_t x)
48 {
48 {
49 return ((x & 0x000000ffUL) << 24) |
49 return ((x & 0x000000ffUL) << 24) |
50 ((x & 0x0000ff00UL) << 8) |
50 ((x & 0x0000ff00UL) << 8) |
51 ((x & 0x00ff0000UL) >> 8) |
51 ((x & 0x00ff0000UL) >> 8) |
52 ((x & 0xff000000UL) >> 24);
52 ((x & 0xff000000UL) >> 24);
53 }
53 }
54 #else
54 #else
55 /* not windows */
55 /* not windows */
56 #include <sys/types.h>
56 #include <sys/types.h>
57 #if defined __BEOS__ && !defined __HAIKU__
57 #if defined __BEOS__ && !defined __HAIKU__
58 #include <ByteOrder.h>
58 #include <ByteOrder.h>
59 #else
59 #else
60 #include <arpa/inet.h>
60 #include <arpa/inet.h>
61 #endif
61 #endif
62 #include <inttypes.h>
62 #include <inttypes.h>
63 #endif
63 #endif
64
64
65 static char mpatch_doc[] = "Efficient binary patching.";
65 static char mpatch_doc[] = "Efficient binary patching.";
66 static PyObject *mpatch_Error;
66 static PyObject *mpatch_Error;
67
67
68 struct frag {
68 struct frag {
69 int start, end, len;
69 int start, end, len;
70 const char *data;
70 const char *data;
71 };
71 };
72
72
73 struct flist {
73 struct flist {
74 struct frag *base, *head, *tail;
74 struct frag *base, *head, *tail;
75 };
75 };
76
76
77 static struct flist *lalloc(int size)
77 static struct flist *lalloc(int size)
78 {
78 {
79 struct flist *a = NULL;
79 struct flist *a = NULL;
80
80
81 if (size < 1)
81 if (size < 1)
82 size = 1;
82 size = 1;
83
83
84 a = (struct flist *)malloc(sizeof(struct flist));
84 a = (struct flist *)malloc(sizeof(struct flist));
85 if (a) {
85 if (a) {
86 a->base = (struct frag *)malloc(sizeof(struct frag) * size);
86 a->base = (struct frag *)malloc(sizeof(struct frag) * size);
87 if (a->base) {
87 if (a->base) {
88 a->head = a->tail = a->base;
88 a->head = a->tail = a->base;
89 return a;
89 return a;
90 }
90 }
91 free(a);
91 free(a);
92 a = NULL;
92 a = NULL;
93 }
93 }
94 if (!PyErr_Occurred())
94 if (!PyErr_Occurred())
95 PyErr_NoMemory();
95 PyErr_NoMemory();
96 return NULL;
96 return NULL;
97 }
97 }
98
98
99 static void lfree(struct flist *a)
99 static void lfree(struct flist *a)
100 {
100 {
101 if (a) {
101 if (a) {
102 free(a->base);
102 free(a->base);
103 free(a);
103 free(a);
104 }
104 }
105 }
105 }
106
106
107 static int lsize(struct flist *a)
107 static int lsize(struct flist *a)
108 {
108 {
109 return a->tail - a->head;
109 return a->tail - a->head;
110 }
110 }
111
111
112 /* move hunks in source that are less cut to dest, compensating
112 /* move hunks in source that are less cut to dest, compensating
113 for changes in offset. the last hunk may be split if necessary.
113 for changes in offset. the last hunk may be split if necessary.
114 */
114 */
115 static int gather(struct flist *dest, struct flist *src, int cut, int offset)
115 static int gather(struct flist *dest, struct flist *src, int cut, int offset)
116 {
116 {
117 struct frag *d = dest->tail, *s = src->head;
117 struct frag *d = dest->tail, *s = src->head;
118 int postend, c, l;
118 int postend, c, l;
119
119
120 while (s != src->tail) {
120 while (s != src->tail) {
121 if (s->start + offset >= cut)
121 if (s->start + offset >= cut)
122 break; /* we've gone far enough */
122 break; /* we've gone far enough */
123
123
124 postend = offset + s->start + s->len;
124 postend = offset + s->start + s->len;
125 if (postend <= cut) {
125 if (postend <= cut) {
126 /* save this hunk */
126 /* save this hunk */
127 offset += s->start + s->len - s->end;
127 offset += s->start + s->len - s->end;
128 *d++ = *s++;
128 *d++ = *s++;
129 }
129 }
130 else {
130 else {
131 /* break up this hunk */
131 /* break up this hunk */
132 c = cut - offset;
132 c = cut - offset;
133 if (s->end < c)
133 if (s->end < c)
134 c = s->end;
134 c = s->end;
135 l = cut - offset - s->start;
135 l = cut - offset - s->start;
136 if (s->len < l)
136 if (s->len < l)
137 l = s->len;
137 l = s->len;
138
138
139 offset += s->start + l - c;
139 offset += s->start + l - c;
140
140
141 d->start = s->start;
141 d->start = s->start;
142 d->end = c;
142 d->end = c;
143 d->len = l;
143 d->len = l;
144 d->data = s->data;
144 d->data = s->data;
145 d++;
145 d++;
146 s->start = c;
146 s->start = c;
147 s->len = s->len - l;
147 s->len = s->len - l;
148 s->data = s->data + l;
148 s->data = s->data + l;
149
149
150 break;
150 break;
151 }
151 }
152 }
152 }
153
153
154 dest->tail = d;
154 dest->tail = d;
155 src->head = s;
155 src->head = s;
156 return offset;
156 return offset;
157 }
157 }
158
158
159 /* like gather, but with no output list */
159 /* like gather, but with no output list */
160 static int discard(struct flist *src, int cut, int offset)
160 static int discard(struct flist *src, int cut, int offset)
161 {
161 {
162 struct frag *s = src->head;
162 struct frag *s = src->head;
163 int postend, c, l;
163 int postend, c, l;
164
164
165 while (s != src->tail) {
165 while (s != src->tail) {
166 if (s->start + offset >= cut)
166 if (s->start + offset >= cut)
167 break;
167 break;
168
168
169 postend = offset + s->start + s->len;
169 postend = offset + s->start + s->len;
170 if (postend <= cut) {
170 if (postend <= cut) {
171 offset += s->start + s->len - s->end;
171 offset += s->start + s->len - s->end;
172 s++;
172 s++;
173 }
173 }
174 else {
174 else {
175 c = cut - offset;
175 c = cut - offset;
176 if (s->end < c)
176 if (s->end < c)
177 c = s->end;
177 c = s->end;
178 l = cut - offset - s->start;
178 l = cut - offset - s->start;
179 if (s->len < l)
179 if (s->len < l)
180 l = s->len;
180 l = s->len;
181
181
182 offset += s->start + l - c;
182 offset += s->start + l - c;
183 s->start = c;
183 s->start = c;
184 s->len = s->len - l;
184 s->len = s->len - l;
185 s->data = s->data + l;
185 s->data = s->data + l;
186
186
187 break;
187 break;
188 }
188 }
189 }
189 }
190
190
191 src->head = s;
191 src->head = s;
192 return offset;
192 return offset;
193 }
193 }
194
194
195 /* combine hunk lists a and b, while adjusting b for offset changes in a/
195 /* combine hunk lists a and b, while adjusting b for offset changes in a/
196 this deletes a and b and returns the resultant list. */
196 this deletes a and b and returns the resultant list. */
197 static struct flist *combine(struct flist *a, struct flist *b)
197 static struct flist *combine(struct flist *a, struct flist *b)
198 {
198 {
199 struct flist *c = NULL;
199 struct flist *c = NULL;
200 struct frag *bh, *ct;
200 struct frag *bh, *ct;
201 int offset = 0, post;
201 int offset = 0, post;
202
202
203 if (a && b)
203 if (a && b)
204 c = lalloc((lsize(a) + lsize(b)) * 2);
204 c = lalloc((lsize(a) + lsize(b)) * 2);
205
205
206 if (c) {
206 if (c) {
207
207
208 for (bh = b->head; bh != b->tail; bh++) {
208 for (bh = b->head; bh != b->tail; bh++) {
209 /* save old hunks */
209 /* save old hunks */
210 offset = gather(c, a, bh->start, offset);
210 offset = gather(c, a, bh->start, offset);
211
211
212 /* discard replaced hunks */
212 /* discard replaced hunks */
213 post = discard(a, bh->end, offset);
213 post = discard(a, bh->end, offset);
214
214
215 /* insert new hunk */
215 /* insert new hunk */
216 ct = c->tail;
216 ct = c->tail;
217 ct->start = bh->start - offset;
217 ct->start = bh->start - offset;
218 ct->end = bh->end - post;
218 ct->end = bh->end - post;
219 ct->len = bh->len;
219 ct->len = bh->len;
220 ct->data = bh->data;
220 ct->data = bh->data;
221 c->tail++;
221 c->tail++;
222 offset = post;
222 offset = post;
223 }
223 }
224
224
225 /* hold on to tail from a */
225 /* hold on to tail from a */
226 memcpy(c->tail, a->head, sizeof(struct frag) * lsize(a));
226 memcpy(c->tail, a->head, sizeof(struct frag) * lsize(a));
227 c->tail += lsize(a);
227 c->tail += lsize(a);
228 }
228 }
229
229
230 lfree(a);
230 lfree(a);
231 lfree(b);
231 lfree(b);
232 return c;
232 return c;
233 }
233 }
234
234
235 /* decode a binary patch into a hunk list */
235 /* decode a binary patch into a hunk list */
236 static struct flist *decode(const char *bin, int len)
236 static struct flist *decode(const char *bin, int len)
237 {
237 {
238 struct flist *l;
238 struct flist *l;
239 struct frag *lt;
239 struct frag *lt;
240 const char *data = bin + 12, *end = bin + len;
240 const char *data = bin + 12, *end = bin + len;
241 char decode[12]; /* for dealing with alignment issues */
241 char decode[12]; /* for dealing with alignment issues */
242
242
243 /* assume worst case size, we won't have many of these lists */
243 /* assume worst case size, we won't have many of these lists */
244 l = lalloc(len / 12);
244 l = lalloc(len / 12);
245 if (!l)
245 if (!l)
246 return NULL;
246 return NULL;
247
247
248 lt = l->tail;
248 lt = l->tail;
249
249
250 while (data <= end) {
250 while (data <= end) {
251 memcpy(decode, bin, 12);
251 memcpy(decode, bin, 12);
252 lt->start = ntohl(*(uint32_t *)decode);
252 lt->start = ntohl(*(uint32_t *)decode);
253 lt->end = ntohl(*(uint32_t *)(decode + 4));
253 lt->end = ntohl(*(uint32_t *)(decode + 4));
254 lt->len = ntohl(*(uint32_t *)(decode + 8));
254 lt->len = ntohl(*(uint32_t *)(decode + 8));
255 if (lt->start > lt->end)
255 if (lt->start > lt->end)
256 break; /* sanity check */
256 break; /* sanity check */
257 bin = data + lt->len;
257 bin = data + lt->len;
258 if (bin < data)
258 if (bin < data)
259 break; /* big data + big (bogus) len can wrap around */
259 break; /* big data + big (bogus) len can wrap around */
260 lt->data = data;
260 lt->data = data;
261 data = bin + 12;
261 data = bin + 12;
262 lt++;
262 lt++;
263 }
263 }
264
264
265 if (bin != end) {
265 if (bin != end) {
266 if (!PyErr_Occurred())
266 if (!PyErr_Occurred())
267 PyErr_SetString(mpatch_Error, "patch cannot be decoded");
267 PyErr_SetString(mpatch_Error, "patch cannot be decoded");
268 lfree(l);
268 lfree(l);
269 return NULL;
269 return NULL;
270 }
270 }
271
271
272 l->tail = lt;
272 l->tail = lt;
273 return l;
273 return l;
274 }
274 }
275
275
276 /* calculate the size of resultant text */
276 /* calculate the size of resultant text */
277 static int calcsize(int len, struct flist *l)
277 static int calcsize(int len, struct flist *l)
278 {
278 {
279 int outlen = 0, last = 0;
279 int outlen = 0, last = 0;
280 struct frag *f = l->head;
280 struct frag *f = l->head;
281
281
282 while (f != l->tail) {
282 while (f != l->tail) {
283 if (f->start < last || f->end > len) {
283 if (f->start < last || f->end > len) {
284 if (!PyErr_Occurred())
284 if (!PyErr_Occurred())
285 PyErr_SetString(mpatch_Error,
285 PyErr_SetString(mpatch_Error,
286 "invalid patch");
286 "invalid patch");
287 return -1;
287 return -1;
288 }
288 }
289 outlen += f->start - last;
289 outlen += f->start - last;
290 last = f->end;
290 last = f->end;
291 outlen += f->len;
291 outlen += f->len;
292 f++;
292 f++;
293 }
293 }
294
294
295 outlen += len - last;
295 outlen += len - last;
296 return outlen;
296 return outlen;
297 }
297 }
298
298
299 static int apply(char *buf, const char *orig, int len, struct flist *l)
299 static int apply(char *buf, const char *orig, int len, struct flist *l)
300 {
300 {
301 struct frag *f = l->head;
301 struct frag *f = l->head;
302 int last = 0;
302 int last = 0;
303 char *p = buf;
303 char *p = buf;
304
304
305 while (f != l->tail) {
305 while (f != l->tail) {
306 if (f->start < last || f->end > len) {
306 if (f->start < last || f->end > len) {
307 if (!PyErr_Occurred())
307 if (!PyErr_Occurred())
308 PyErr_SetString(mpatch_Error,
308 PyErr_SetString(mpatch_Error,
309 "invalid patch");
309 "invalid patch");
310 return 0;
310 return 0;
311 }
311 }
312 memcpy(p, orig + last, f->start - last);
312 memcpy(p, orig + last, f->start - last);
313 p += f->start - last;
313 p += f->start - last;
314 memcpy(p, f->data, f->len);
314 memcpy(p, f->data, f->len);
315 last = f->end;
315 last = f->end;
316 p += f->len;
316 p += f->len;
317 f++;
317 f++;
318 }
318 }
319 memcpy(p, orig + last, len - last);
319 memcpy(p, orig + last, len - last);
320 return 1;
320 return 1;
321 }
321 }
322
322
323 /* recursively generate a patch of all bins between start and end */
323 /* recursively generate a patch of all bins between start and end */
324 static struct flist *fold(PyObject *bins, int start, int end)
324 static struct flist *fold(PyObject *bins, int start, int end)
325 {
325 {
326 int len;
326 int len;
327 Py_ssize_t blen;
327 Py_ssize_t blen;
328 const char *buffer;
328 const char *buffer;
329
329
330 if (start + 1 == end) {
330 if (start + 1 == end) {
331 /* trivial case, output a decoded list */
331 /* trivial case, output a decoded list */
332 PyObject *tmp = PyList_GetItem(bins, start);
332 PyObject *tmp = PyList_GetItem(bins, start);
333 if (!tmp)
333 if (!tmp)
334 return NULL;
334 return NULL;
335 if (PyObject_AsCharBuffer(tmp, &buffer, &blen))
335 if (PyObject_AsCharBuffer(tmp, &buffer, &blen))
336 return NULL;
336 return NULL;
337 return decode(buffer, blen);
337 return decode(buffer, blen);
338 }
338 }
339
339
340 /* divide and conquer, memory management is elsewhere */
340 /* divide and conquer, memory management is elsewhere */
341 len = (end - start) / 2;
341 len = (end - start) / 2;
342 return combine(fold(bins, start, start + len),
342 return combine(fold(bins, start, start + len),
343 fold(bins, start + len, end));
343 fold(bins, start + len, end));
344 }
344 }
345
345
346 static PyObject *
346 static PyObject *
347 patches(PyObject *self, PyObject *args)
347 patches(PyObject *self, PyObject *args)
348 {
348 {
349 PyObject *text, *bins, *result;
349 PyObject *text, *bins, *result;
350 struct flist *patch;
350 struct flist *patch;
351 const char *in;
351 const char *in;
352 char *out;
352 char *out;
353 int len, outlen;
353 int len, outlen;
354 Py_ssize_t inlen;
354 Py_ssize_t inlen;
355
355
356 if (!PyArg_ParseTuple(args, "OO:mpatch", &text, &bins))
356 if (!PyArg_ParseTuple(args, "OO:mpatch", &text, &bins))
357 return NULL;
357 return NULL;
358
358
359 len = PyList_Size(bins);
359 len = PyList_Size(bins);
360 if (!len) {
360 if (!len) {
361 /* nothing to do */
361 /* nothing to do */
362 Py_INCREF(text);
362 Py_INCREF(text);
363 return text;
363 return text;
364 }
364 }
365
365
366 if (PyObject_AsCharBuffer(text, &in, &inlen))
366 if (PyObject_AsCharBuffer(text, &in, &inlen))
367 return NULL;
367 return NULL;
368
368
369 patch = fold(bins, 0, len);
369 patch = fold(bins, 0, len);
370 if (!patch)
370 if (!patch)
371 return NULL;
371 return NULL;
372
372
373 outlen = calcsize(inlen, patch);
373 outlen = calcsize(inlen, patch);
374 if (outlen < 0) {
374 if (outlen < 0) {
375 result = NULL;
375 result = NULL;
376 goto cleanup;
376 goto cleanup;
377 }
377 }
378 result = PyBytes_FromStringAndSize(NULL, outlen);
378 result = PyBytes_FromStringAndSize(NULL, outlen);
379 if (!result) {
379 if (!result) {
380 result = NULL;
380 result = NULL;
381 goto cleanup;
381 goto cleanup;
382 }
382 }
383 out = PyBytes_AsString(result);
383 out = PyBytes_AsString(result);
384 if (!apply(out, in, inlen, patch)) {
384 if (!apply(out, in, inlen, patch)) {
385 Py_DECREF(result);
385 Py_DECREF(result);
386 result = NULL;
386 result = NULL;
387 }
387 }
388 cleanup:
388 cleanup:
389 lfree(patch);
389 lfree(patch);
390 return result;
390 return result;
391 }
391 }
392
392
393 /* calculate size of a patched file directly */
393 /* calculate size of a patched file directly */
394 static PyObject *
394 static PyObject *
395 patchedsize(PyObject *self, PyObject *args)
395 patchedsize(PyObject *self, PyObject *args)
396 {
396 {
397 long orig, start, end, len, outlen = 0, last = 0;
397 long orig, start, end, len, outlen = 0, last = 0;
398 int patchlen;
398 int patchlen;
399 char *bin, *binend, *data;
399 char *bin, *binend, *data;
400 char decode[12]; /* for dealing with alignment issues */
400 char decode[12]; /* for dealing with alignment issues */
401
401
402 if (!PyArg_ParseTuple(args, "ls#", &orig, &bin, &patchlen))
402 if (!PyArg_ParseTuple(args, "ls#", &orig, &bin, &patchlen))
403 return NULL;
403 return NULL;
404
404
405 binend = bin + patchlen;
405 binend = bin + patchlen;
406 data = bin + 12;
406 data = bin + 12;
407
407
408 while (data <= binend) {
408 while (data <= binend) {
409 memcpy(decode, bin, 12);
409 memcpy(decode, bin, 12);
410 start = ntohl(*(uint32_t *)decode);
410 start = ntohl(*(uint32_t *)decode);
411 end = ntohl(*(uint32_t *)(decode + 4));
411 end = ntohl(*(uint32_t *)(decode + 4));
412 len = ntohl(*(uint32_t *)(decode + 8));
412 len = ntohl(*(uint32_t *)(decode + 8));
413 if (start > end)
413 if (start > end)
414 break; /* sanity check */
414 break; /* sanity check */
415 bin = data + len;
415 bin = data + len;
416 if (bin < data)
416 if (bin < data)
417 break; /* big data + big (bogus) len can wrap around */
417 break; /* big data + big (bogus) len can wrap around */
418 data = bin + 12;
418 data = bin + 12;
419 outlen += start - last;
419 outlen += start - last;
420 last = end;
420 last = end;
421 outlen += len;
421 outlen += len;
422 }
422 }
423
423
424 if (bin != binend) {
424 if (bin != binend) {
425 if (!PyErr_Occurred())
425 if (!PyErr_Occurred())
426 PyErr_SetString(mpatch_Error, "patch cannot be decoded");
426 PyErr_SetString(mpatch_Error, "patch cannot be decoded");
427 return NULL;
427 return NULL;
428 }
428 }
429
429
430 outlen += orig - last;
430 outlen += orig - last;
431 return Py_BuildValue("l", outlen);
431 return Py_BuildValue("l", outlen);
432 }
432 }
433
433
434 static PyMethodDef methods[] = {
434 static PyMethodDef methods[] = {
435 {"patches", patches, METH_VARARGS, "apply a series of patches\n"},
435 {"patches", patches, METH_VARARGS, "apply a series of patches\n"},
436 {"patchedsize", patchedsize, METH_VARARGS, "calculed patched size\n"},
436 {"patchedsize", patchedsize, METH_VARARGS, "calculed patched size\n"},
437 {NULL, NULL}
437 {NULL, NULL}
438 };
438 };
439
439
440 #ifdef IS_PY3K
440 #ifdef IS_PY3K
441 static struct PyModuleDef mpatch_module = {
441 static struct PyModuleDef mpatch_module = {
442 PyModuleDef_HEAD_INIT,
442 PyModuleDef_HEAD_INIT,
443 "mpatch",
443 "mpatch",
444 mpatch_doc,
444 mpatch_doc,
445 -1,
445 -1,
446 methods
446 methods
447 };
447 };
448
448
449 PyMODINIT_FUNC PyInit_mpatch(void)
449 PyMODINIT_FUNC PyInit_mpatch(void)
450 {
450 {
451 PyObject *m;
451 PyObject *m;
452
452
453 m = PyModule_Create(&mpatch_module);
453 m = PyModule_Create(&mpatch_module);
454 if (m == NULL)
454 if (m == NULL)
455 return NULL;
455 return NULL;
456
456
457 mpatch_Error = PyErr_NewException("mpatch.mpatchError", NULL, NULL);
457 mpatch_Error = PyErr_NewException("mpatch.mpatchError", NULL, NULL);
458 Py_INCREF(mpatch_Error);
458 Py_INCREF(mpatch_Error);
459 PyModule_AddObject(m, "mpatchError", mpatch_Error);
459 PyModule_AddObject(m, "mpatchError", mpatch_Error);
460
460
461 return m;
461 return m;
462 }
462 }
463 #else
463 #else
464 PyMODINIT_FUNC
464 PyMODINIT_FUNC
465 initmpatch(void)
465 initmpatch(void)
466 {
466 {
467 Py_InitModule3("mpatch", methods, mpatch_doc);
467 Py_InitModule3("mpatch", methods, mpatch_doc);
468 mpatch_Error = PyErr_NewException("mpatch.mpatchError", NULL, NULL);
468 mpatch_Error = PyErr_NewException("mpatch.mpatchError", NULL, NULL);
469 }
469 }
470 #endif
470 #endif
471
472 /* Local Variables: */
473 /* c-file-style: "linux" */
474 /* indent-tabs-mode: t */
475 /* End: */
@@ -1,554 +1,559 b''
1 /*
1 /*
2 osutil.c - native operating system services
2 osutil.c - native operating system services
3
3
4 Copyright 2007 Matt Mackall and others
4 Copyright 2007 Matt Mackall and others
5
5
6 This software may be used and distributed according to the terms of
6 This software may be used and distributed according to the terms of
7 the GNU General Public License, incorporated herein by reference.
7 the GNU General Public License, incorporated herein by reference.
8 */
8 */
9
9
10 #define _ATFILE_SOURCE
10 #define _ATFILE_SOURCE
11 #include <Python.h>
11 #include <Python.h>
12 #include <fcntl.h>
12 #include <fcntl.h>
13 #include <stdio.h>
13 #include <stdio.h>
14 #include <string.h>
14 #include <string.h>
15
15
16 #ifdef _WIN32
16 #ifdef _WIN32
17 #include <windows.h>
17 #include <windows.h>
18 #include <io.h>
18 #include <io.h>
19 #else
19 #else
20 #include <dirent.h>
20 #include <dirent.h>
21 #include <sys/stat.h>
21 #include <sys/stat.h>
22 #include <sys/types.h>
22 #include <sys/types.h>
23 #include <unistd.h>
23 #include <unistd.h>
24 #endif
24 #endif
25
25
26 #include "util.h"
26 #include "util.h"
27
27
28 /* some platforms lack the PATH_MAX definition (eg. GNU/Hurd) */
28 /* some platforms lack the PATH_MAX definition (eg. GNU/Hurd) */
29 #ifndef PATH_MAX
29 #ifndef PATH_MAX
30 #define PATH_MAX 4096
30 #define PATH_MAX 4096
31 #endif
31 #endif
32
32
33 #ifdef _WIN32
33 #ifdef _WIN32
34 /*
34 /*
35 stat struct compatible with hg expectations
35 stat struct compatible with hg expectations
36 Mercurial only uses st_mode, st_size and st_mtime
36 Mercurial only uses st_mode, st_size and st_mtime
37 the rest is kept to minimize changes between implementations
37 the rest is kept to minimize changes between implementations
38 */
38 */
39 struct hg_stat {
39 struct hg_stat {
40 int st_dev;
40 int st_dev;
41 int st_mode;
41 int st_mode;
42 int st_nlink;
42 int st_nlink;
43 __int64 st_size;
43 __int64 st_size;
44 int st_mtime;
44 int st_mtime;
45 int st_ctime;
45 int st_ctime;
46 };
46 };
47 struct listdir_stat {
47 struct listdir_stat {
48 PyObject_HEAD
48 PyObject_HEAD
49 struct hg_stat st;
49 struct hg_stat st;
50 };
50 };
51 #else
51 #else
52 struct listdir_stat {
52 struct listdir_stat {
53 PyObject_HEAD
53 PyObject_HEAD
54 struct stat st;
54 struct stat st;
55 };
55 };
56 #endif
56 #endif
57
57
58 #define listdir_slot(name) \
58 #define listdir_slot(name) \
59 static PyObject *listdir_stat_##name(PyObject *self, void *x) \
59 static PyObject *listdir_stat_##name(PyObject *self, void *x) \
60 { \
60 { \
61 return PyInt_FromLong(((struct listdir_stat *)self)->st.name); \
61 return PyInt_FromLong(((struct listdir_stat *)self)->st.name); \
62 }
62 }
63
63
64 listdir_slot(st_dev)
64 listdir_slot(st_dev)
65 listdir_slot(st_mode)
65 listdir_slot(st_mode)
66 listdir_slot(st_nlink)
66 listdir_slot(st_nlink)
67 #ifdef _WIN32
67 #ifdef _WIN32
68 static PyObject *listdir_stat_st_size(PyObject *self, void *x)
68 static PyObject *listdir_stat_st_size(PyObject *self, void *x)
69 {
69 {
70 return PyLong_FromLongLong(
70 return PyLong_FromLongLong(
71 (PY_LONG_LONG)((struct listdir_stat *)self)->st.st_size);
71 (PY_LONG_LONG)((struct listdir_stat *)self)->st.st_size);
72 }
72 }
73 #else
73 #else
74 listdir_slot(st_size)
74 listdir_slot(st_size)
75 #endif
75 #endif
76 listdir_slot(st_mtime)
76 listdir_slot(st_mtime)
77 listdir_slot(st_ctime)
77 listdir_slot(st_ctime)
78
78
79 static struct PyGetSetDef listdir_stat_getsets[] = {
79 static struct PyGetSetDef listdir_stat_getsets[] = {
80 {"st_dev", listdir_stat_st_dev, 0, 0, 0},
80 {"st_dev", listdir_stat_st_dev, 0, 0, 0},
81 {"st_mode", listdir_stat_st_mode, 0, 0, 0},
81 {"st_mode", listdir_stat_st_mode, 0, 0, 0},
82 {"st_nlink", listdir_stat_st_nlink, 0, 0, 0},
82 {"st_nlink", listdir_stat_st_nlink, 0, 0, 0},
83 {"st_size", listdir_stat_st_size, 0, 0, 0},
83 {"st_size", listdir_stat_st_size, 0, 0, 0},
84 {"st_mtime", listdir_stat_st_mtime, 0, 0, 0},
84 {"st_mtime", listdir_stat_st_mtime, 0, 0, 0},
85 {"st_ctime", listdir_stat_st_ctime, 0, 0, 0},
85 {"st_ctime", listdir_stat_st_ctime, 0, 0, 0},
86 {0, 0, 0, 0, 0}
86 {0, 0, 0, 0, 0}
87 };
87 };
88
88
89 static PyObject *listdir_stat_new(PyTypeObject *t, PyObject *a, PyObject *k)
89 static PyObject *listdir_stat_new(PyTypeObject *t, PyObject *a, PyObject *k)
90 {
90 {
91 return t->tp_alloc(t, 0);
91 return t->tp_alloc(t, 0);
92 }
92 }
93
93
94 static void listdir_stat_dealloc(PyObject *o)
94 static void listdir_stat_dealloc(PyObject *o)
95 {
95 {
96 o->ob_type->tp_free(o);
96 o->ob_type->tp_free(o);
97 }
97 }
98
98
99 static PyTypeObject listdir_stat_type = {
99 static PyTypeObject listdir_stat_type = {
100 PyVarObject_HEAD_INIT(NULL, 0)
100 PyVarObject_HEAD_INIT(NULL, 0)
101 "osutil.stat", /*tp_name*/
101 "osutil.stat", /*tp_name*/
102 sizeof(struct listdir_stat), /*tp_basicsize*/
102 sizeof(struct listdir_stat), /*tp_basicsize*/
103 0, /*tp_itemsize*/
103 0, /*tp_itemsize*/
104 (destructor)listdir_stat_dealloc, /*tp_dealloc*/
104 (destructor)listdir_stat_dealloc, /*tp_dealloc*/
105 0, /*tp_print*/
105 0, /*tp_print*/
106 0, /*tp_getattr*/
106 0, /*tp_getattr*/
107 0, /*tp_setattr*/
107 0, /*tp_setattr*/
108 0, /*tp_compare*/
108 0, /*tp_compare*/
109 0, /*tp_repr*/
109 0, /*tp_repr*/
110 0, /*tp_as_number*/
110 0, /*tp_as_number*/
111 0, /*tp_as_sequence*/
111 0, /*tp_as_sequence*/
112 0, /*tp_as_mapping*/
112 0, /*tp_as_mapping*/
113 0, /*tp_hash */
113 0, /*tp_hash */
114 0, /*tp_call*/
114 0, /*tp_call*/
115 0, /*tp_str*/
115 0, /*tp_str*/
116 0, /*tp_getattro*/
116 0, /*tp_getattro*/
117 0, /*tp_setattro*/
117 0, /*tp_setattro*/
118 0, /*tp_as_buffer*/
118 0, /*tp_as_buffer*/
119 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
119 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
120 "stat objects", /* tp_doc */
120 "stat objects", /* tp_doc */
121 0, /* tp_traverse */
121 0, /* tp_traverse */
122 0, /* tp_clear */
122 0, /* tp_clear */
123 0, /* tp_richcompare */
123 0, /* tp_richcompare */
124 0, /* tp_weaklistoffset */
124 0, /* tp_weaklistoffset */
125 0, /* tp_iter */
125 0, /* tp_iter */
126 0, /* tp_iternext */
126 0, /* tp_iternext */
127 0, /* tp_methods */
127 0, /* tp_methods */
128 0, /* tp_members */
128 0, /* tp_members */
129 listdir_stat_getsets, /* tp_getset */
129 listdir_stat_getsets, /* tp_getset */
130 0, /* tp_base */
130 0, /* tp_base */
131 0, /* tp_dict */
131 0, /* tp_dict */
132 0, /* tp_descr_get */
132 0, /* tp_descr_get */
133 0, /* tp_descr_set */
133 0, /* tp_descr_set */
134 0, /* tp_dictoffset */
134 0, /* tp_dictoffset */
135 0, /* tp_init */
135 0, /* tp_init */
136 0, /* tp_alloc */
136 0, /* tp_alloc */
137 listdir_stat_new, /* tp_new */
137 listdir_stat_new, /* tp_new */
138 };
138 };
139
139
140 #ifdef _WIN32
140 #ifdef _WIN32
141
141
142 static int to_python_time(const FILETIME *tm)
142 static int to_python_time(const FILETIME *tm)
143 {
143 {
144 /* number of seconds between epoch and January 1 1601 */
144 /* number of seconds between epoch and January 1 1601 */
145 const __int64 a0 = (__int64)134774L * (__int64)24L * (__int64)3600L;
145 const __int64 a0 = (__int64)134774L * (__int64)24L * (__int64)3600L;
146 /* conversion factor from 100ns to 1s */
146 /* conversion factor from 100ns to 1s */
147 const __int64 a1 = 10000000;
147 const __int64 a1 = 10000000;
148 /* explicit (int) cast to suspend compiler warnings */
148 /* explicit (int) cast to suspend compiler warnings */
149 return (int)((((__int64)tm->dwHighDateTime << 32)
149 return (int)((((__int64)tm->dwHighDateTime << 32)
150 + tm->dwLowDateTime) / a1 - a0);
150 + tm->dwLowDateTime) / a1 - a0);
151 }
151 }
152
152
153 static PyObject *make_item(const WIN32_FIND_DATAA *fd, int wantstat)
153 static PyObject *make_item(const WIN32_FIND_DATAA *fd, int wantstat)
154 {
154 {
155 PyObject *py_st;
155 PyObject *py_st;
156 struct hg_stat *stp;
156 struct hg_stat *stp;
157
157
158 int kind = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
158 int kind = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
159 ? _S_IFDIR : _S_IFREG;
159 ? _S_IFDIR : _S_IFREG;
160
160
161 if (!wantstat)
161 if (!wantstat)
162 return Py_BuildValue("si", fd->cFileName, kind);
162 return Py_BuildValue("si", fd->cFileName, kind);
163
163
164 py_st = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
164 py_st = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
165 if (!py_st)
165 if (!py_st)
166 return NULL;
166 return NULL;
167
167
168 stp = &((struct listdir_stat *)py_st)->st;
168 stp = &((struct listdir_stat *)py_st)->st;
169 /*
169 /*
170 use kind as st_mode
170 use kind as st_mode
171 rwx bits on Win32 are meaningless
171 rwx bits on Win32 are meaningless
172 and Hg does not use them anyway
172 and Hg does not use them anyway
173 */
173 */
174 stp->st_mode = kind;
174 stp->st_mode = kind;
175 stp->st_mtime = to_python_time(&fd->ftLastWriteTime);
175 stp->st_mtime = to_python_time(&fd->ftLastWriteTime);
176 stp->st_ctime = to_python_time(&fd->ftCreationTime);
176 stp->st_ctime = to_python_time(&fd->ftCreationTime);
177 if (kind == _S_IFREG)
177 if (kind == _S_IFREG)
178 stp->st_size = ((__int64)fd->nFileSizeHigh << 32)
178 stp->st_size = ((__int64)fd->nFileSizeHigh << 32)
179 + fd->nFileSizeLow;
179 + fd->nFileSizeLow;
180 return Py_BuildValue("siN", fd->cFileName,
180 return Py_BuildValue("siN", fd->cFileName,
181 kind, py_st);
181 kind, py_st);
182 }
182 }
183
183
184 static PyObject *_listdir(char *path, int plen, int wantstat, char *skip)
184 static PyObject *_listdir(char *path, int plen, int wantstat, char *skip)
185 {
185 {
186 PyObject *rval = NULL; /* initialize - return value */
186 PyObject *rval = NULL; /* initialize - return value */
187 PyObject *list;
187 PyObject *list;
188 HANDLE fh;
188 HANDLE fh;
189 WIN32_FIND_DATAA fd;
189 WIN32_FIND_DATAA fd;
190 char *pattern;
190 char *pattern;
191
191
192 /* build the path + \* pattern string */
192 /* build the path + \* pattern string */
193 pattern = malloc(plen + 3); /* path + \* + \0 */
193 pattern = malloc(plen + 3); /* path + \* + \0 */
194 if (!pattern) {
194 if (!pattern) {
195 PyErr_NoMemory();
195 PyErr_NoMemory();
196 goto error_nomem;
196 goto error_nomem;
197 }
197 }
198 strcpy(pattern, path);
198 strcpy(pattern, path);
199
199
200 if (plen > 0) {
200 if (plen > 0) {
201 char c = path[plen-1];
201 char c = path[plen-1];
202 if (c != ':' && c != '/' && c != '\\')
202 if (c != ':' && c != '/' && c != '\\')
203 pattern[plen++] = '\\';
203 pattern[plen++] = '\\';
204 }
204 }
205 strcpy(pattern + plen, "*");
205 strcpy(pattern + plen, "*");
206
206
207 fh = FindFirstFileA(pattern, &fd);
207 fh = FindFirstFileA(pattern, &fd);
208 if (fh == INVALID_HANDLE_VALUE) {
208 if (fh == INVALID_HANDLE_VALUE) {
209 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
209 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
210 goto error_file;
210 goto error_file;
211 }
211 }
212
212
213 list = PyList_New(0);
213 list = PyList_New(0);
214 if (!list)
214 if (!list)
215 goto error_list;
215 goto error_list;
216
216
217 do {
217 do {
218 PyObject *item;
218 PyObject *item;
219
219
220 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
220 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
221 if (!strcmp(fd.cFileName, ".")
221 if (!strcmp(fd.cFileName, ".")
222 || !strcmp(fd.cFileName, ".."))
222 || !strcmp(fd.cFileName, ".."))
223 continue;
223 continue;
224
224
225 if (skip && !strcmp(fd.cFileName, skip)) {
225 if (skip && !strcmp(fd.cFileName, skip)) {
226 rval = PyList_New(0);
226 rval = PyList_New(0);
227 goto error;
227 goto error;
228 }
228 }
229 }
229 }
230
230
231 item = make_item(&fd, wantstat);
231 item = make_item(&fd, wantstat);
232 if (!item)
232 if (!item)
233 goto error;
233 goto error;
234
234
235 if (PyList_Append(list, item)) {
235 if (PyList_Append(list, item)) {
236 Py_XDECREF(item);
236 Py_XDECREF(item);
237 goto error;
237 goto error;
238 }
238 }
239
239
240 Py_XDECREF(item);
240 Py_XDECREF(item);
241 } while (FindNextFileA(fh, &fd));
241 } while (FindNextFileA(fh, &fd));
242
242
243 if (GetLastError() != ERROR_NO_MORE_FILES) {
243 if (GetLastError() != ERROR_NO_MORE_FILES) {
244 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
244 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
245 goto error;
245 goto error;
246 }
246 }
247
247
248 rval = list;
248 rval = list;
249 Py_XINCREF(rval);
249 Py_XINCREF(rval);
250 error:
250 error:
251 Py_XDECREF(list);
251 Py_XDECREF(list);
252 error_list:
252 error_list:
253 FindClose(fh);
253 FindClose(fh);
254 error_file:
254 error_file:
255 free(pattern);
255 free(pattern);
256 error_nomem:
256 error_nomem:
257 return rval;
257 return rval;
258 }
258 }
259
259
260 #else
260 #else
261
261
262 int entkind(struct dirent *ent)
262 int entkind(struct dirent *ent)
263 {
263 {
264 #ifdef DT_REG
264 #ifdef DT_REG
265 switch (ent->d_type) {
265 switch (ent->d_type) {
266 case DT_REG: return S_IFREG;
266 case DT_REG: return S_IFREG;
267 case DT_DIR: return S_IFDIR;
267 case DT_DIR: return S_IFDIR;
268 case DT_LNK: return S_IFLNK;
268 case DT_LNK: return S_IFLNK;
269 case DT_BLK: return S_IFBLK;
269 case DT_BLK: return S_IFBLK;
270 case DT_CHR: return S_IFCHR;
270 case DT_CHR: return S_IFCHR;
271 case DT_FIFO: return S_IFIFO;
271 case DT_FIFO: return S_IFIFO;
272 case DT_SOCK: return S_IFSOCK;
272 case DT_SOCK: return S_IFSOCK;
273 }
273 }
274 #endif
274 #endif
275 return -1;
275 return -1;
276 }
276 }
277
277
278 static PyObject *_listdir(char *path, int pathlen, int keepstat, char *skip)
278 static PyObject *_listdir(char *path, int pathlen, int keepstat, char *skip)
279 {
279 {
280 PyObject *list, *elem, *stat, *ret = NULL;
280 PyObject *list, *elem, *stat, *ret = NULL;
281 char fullpath[PATH_MAX + 10];
281 char fullpath[PATH_MAX + 10];
282 int kind, err;
282 int kind, err;
283 struct stat st;
283 struct stat st;
284 struct dirent *ent;
284 struct dirent *ent;
285 DIR *dir;
285 DIR *dir;
286 #ifdef AT_SYMLINK_NOFOLLOW
286 #ifdef AT_SYMLINK_NOFOLLOW
287 int dfd = -1;
287 int dfd = -1;
288 #endif
288 #endif
289
289
290 if (pathlen >= PATH_MAX) {
290 if (pathlen >= PATH_MAX) {
291 PyErr_SetString(PyExc_ValueError, "path too long");
291 PyErr_SetString(PyExc_ValueError, "path too long");
292 goto error_value;
292 goto error_value;
293 }
293 }
294 strncpy(fullpath, path, PATH_MAX);
294 strncpy(fullpath, path, PATH_MAX);
295 fullpath[pathlen] = '/';
295 fullpath[pathlen] = '/';
296
296
297 #ifdef AT_SYMLINK_NOFOLLOW
297 #ifdef AT_SYMLINK_NOFOLLOW
298 dfd = open(path, O_RDONLY);
298 dfd = open(path, O_RDONLY);
299 if (dfd == -1) {
299 if (dfd == -1) {
300 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
300 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
301 goto error_value;
301 goto error_value;
302 }
302 }
303 dir = fdopendir(dfd);
303 dir = fdopendir(dfd);
304 #else
304 #else
305 dir = opendir(path);
305 dir = opendir(path);
306 #endif
306 #endif
307 if (!dir) {
307 if (!dir) {
308 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
308 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
309 goto error_dir;
309 goto error_dir;
310 }
310 }
311
311
312 list = PyList_New(0);
312 list = PyList_New(0);
313 if (!list)
313 if (!list)
314 goto error_list;
314 goto error_list;
315
315
316 while ((ent = readdir(dir))) {
316 while ((ent = readdir(dir))) {
317 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
317 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
318 continue;
318 continue;
319
319
320 kind = entkind(ent);
320 kind = entkind(ent);
321 if (kind == -1 || keepstat) {
321 if (kind == -1 || keepstat) {
322 #ifdef AT_SYMLINK_NOFOLLOW
322 #ifdef AT_SYMLINK_NOFOLLOW
323 err = fstatat(dfd, ent->d_name, &st,
323 err = fstatat(dfd, ent->d_name, &st,
324 AT_SYMLINK_NOFOLLOW);
324 AT_SYMLINK_NOFOLLOW);
325 #else
325 #else
326 strncpy(fullpath + pathlen + 1, ent->d_name,
326 strncpy(fullpath + pathlen + 1, ent->d_name,
327 PATH_MAX - pathlen);
327 PATH_MAX - pathlen);
328 fullpath[PATH_MAX] = 0;
328 fullpath[PATH_MAX] = 0;
329 err = lstat(fullpath, &st);
329 err = lstat(fullpath, &st);
330 #endif
330 #endif
331 if (err == -1) {
331 if (err == -1) {
332 strncpy(fullpath + pathlen + 1, ent->d_name,
332 strncpy(fullpath + pathlen + 1, ent->d_name,
333 PATH_MAX - pathlen);
333 PATH_MAX - pathlen);
334 fullpath[PATH_MAX] = 0;
334 fullpath[PATH_MAX] = 0;
335 PyErr_SetFromErrnoWithFilename(PyExc_OSError,
335 PyErr_SetFromErrnoWithFilename(PyExc_OSError,
336 fullpath);
336 fullpath);
337 goto error;
337 goto error;
338 }
338 }
339 kind = st.st_mode & S_IFMT;
339 kind = st.st_mode & S_IFMT;
340 }
340 }
341
341
342 /* quit early? */
342 /* quit early? */
343 if (skip && kind == S_IFDIR && !strcmp(ent->d_name, skip)) {
343 if (skip && kind == S_IFDIR && !strcmp(ent->d_name, skip)) {
344 ret = PyList_New(0);
344 ret = PyList_New(0);
345 goto error;
345 goto error;
346 }
346 }
347
347
348 if (keepstat) {
348 if (keepstat) {
349 stat = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
349 stat = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
350 if (!stat)
350 if (!stat)
351 goto error;
351 goto error;
352 memcpy(&((struct listdir_stat *)stat)->st, &st, sizeof(st));
352 memcpy(&((struct listdir_stat *)stat)->st, &st, sizeof(st));
353 elem = Py_BuildValue("siN", ent->d_name, kind, stat);
353 elem = Py_BuildValue("siN", ent->d_name, kind, stat);
354 } else
354 } else
355 elem = Py_BuildValue("si", ent->d_name, kind);
355 elem = Py_BuildValue("si", ent->d_name, kind);
356 if (!elem)
356 if (!elem)
357 goto error;
357 goto error;
358
358
359 PyList_Append(list, elem);
359 PyList_Append(list, elem);
360 Py_DECREF(elem);
360 Py_DECREF(elem);
361 }
361 }
362
362
363 ret = list;
363 ret = list;
364 Py_INCREF(ret);
364 Py_INCREF(ret);
365
365
366 error:
366 error:
367 Py_DECREF(list);
367 Py_DECREF(list);
368 error_list:
368 error_list:
369 closedir(dir);
369 closedir(dir);
370 error_dir:
370 error_dir:
371 #ifdef AT_SYMLINK_NOFOLLOW
371 #ifdef AT_SYMLINK_NOFOLLOW
372 close(dfd);
372 close(dfd);
373 #endif
373 #endif
374 error_value:
374 error_value:
375 return ret;
375 return ret;
376 }
376 }
377
377
378 #endif /* ndef _WIN32 */
378 #endif /* ndef _WIN32 */
379
379
380 static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs)
380 static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs)
381 {
381 {
382 PyObject *statobj = NULL; /* initialize - optional arg */
382 PyObject *statobj = NULL; /* initialize - optional arg */
383 PyObject *skipobj = NULL; /* initialize - optional arg */
383 PyObject *skipobj = NULL; /* initialize - optional arg */
384 char *path, *skip = NULL;
384 char *path, *skip = NULL;
385 int wantstat, plen;
385 int wantstat, plen;
386
386
387 static char *kwlist[] = {"path", "stat", "skip", NULL};
387 static char *kwlist[] = {"path", "stat", "skip", NULL};
388
388
389 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|OO:listdir",
389 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|OO:listdir",
390 kwlist, &path, &plen, &statobj, &skipobj))
390 kwlist, &path, &plen, &statobj, &skipobj))
391 return NULL;
391 return NULL;
392
392
393 wantstat = statobj && PyObject_IsTrue(statobj);
393 wantstat = statobj && PyObject_IsTrue(statobj);
394
394
395 if (skipobj && skipobj != Py_None) {
395 if (skipobj && skipobj != Py_None) {
396 skip = PyBytes_AsString(skipobj);
396 skip = PyBytes_AsString(skipobj);
397 if (!skip)
397 if (!skip)
398 return NULL;
398 return NULL;
399 }
399 }
400
400
401 return _listdir(path, plen, wantstat, skip);
401 return _listdir(path, plen, wantstat, skip);
402 }
402 }
403
403
404 #ifdef _WIN32
404 #ifdef _WIN32
405 static PyObject *posixfile(PyObject *self, PyObject *args, PyObject *kwds)
405 static PyObject *posixfile(PyObject *self, PyObject *args, PyObject *kwds)
406 {
406 {
407 static char *kwlist[] = {"name", "mode", "buffering", NULL};
407 static char *kwlist[] = {"name", "mode", "buffering", NULL};
408 PyObject *file_obj = NULL;
408 PyObject *file_obj = NULL;
409 char *name = NULL;
409 char *name = NULL;
410 char *mode = "rb";
410 char *mode = "rb";
411 DWORD access = 0;
411 DWORD access = 0;
412 DWORD creation;
412 DWORD creation;
413 HANDLE handle;
413 HANDLE handle;
414 int fd, flags = 0;
414 int fd, flags = 0;
415 int bufsize = -1;
415 int bufsize = -1;
416 char m0, m1, m2;
416 char m0, m1, m2;
417 char fpmode[4];
417 char fpmode[4];
418 int fppos = 0;
418 int fppos = 0;
419 int plus;
419 int plus;
420 FILE *fp;
420 FILE *fp;
421
421
422 if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|si:posixfile", kwlist,
422 if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|si:posixfile", kwlist,
423 Py_FileSystemDefaultEncoding,
423 Py_FileSystemDefaultEncoding,
424 &name, &mode, &bufsize))
424 &name, &mode, &bufsize))
425 return NULL;
425 return NULL;
426
426
427 m0 = mode[0];
427 m0 = mode[0];
428 m1 = m0 ? mode[1] : '\0';
428 m1 = m0 ? mode[1] : '\0';
429 m2 = m1 ? mode[2] : '\0';
429 m2 = m1 ? mode[2] : '\0';
430 plus = m1 == '+' || m2 == '+';
430 plus = m1 == '+' || m2 == '+';
431
431
432 fpmode[fppos++] = m0;
432 fpmode[fppos++] = m0;
433 if (m1 == 'b' || m2 == 'b') {
433 if (m1 == 'b' || m2 == 'b') {
434 flags = _O_BINARY;
434 flags = _O_BINARY;
435 fpmode[fppos++] = 'b';
435 fpmode[fppos++] = 'b';
436 }
436 }
437 else
437 else
438 flags = _O_TEXT;
438 flags = _O_TEXT;
439 if (m0 == 'r' && !plus) {
439 if (m0 == 'r' && !plus) {
440 flags |= _O_RDONLY;
440 flags |= _O_RDONLY;
441 access = GENERIC_READ;
441 access = GENERIC_READ;
442 } else {
442 } else {
443 /*
443 /*
444 work around http://support.microsoft.com/kb/899149 and
444 work around http://support.microsoft.com/kb/899149 and
445 set _O_RDWR for 'w' and 'a', even if mode has no '+'
445 set _O_RDWR for 'w' and 'a', even if mode has no '+'
446 */
446 */
447 flags |= _O_RDWR;
447 flags |= _O_RDWR;
448 access = GENERIC_READ | GENERIC_WRITE;
448 access = GENERIC_READ | GENERIC_WRITE;
449 fpmode[fppos++] = '+';
449 fpmode[fppos++] = '+';
450 }
450 }
451 fpmode[fppos++] = '\0';
451 fpmode[fppos++] = '\0';
452
452
453 switch (m0) {
453 switch (m0) {
454 case 'r':
454 case 'r':
455 creation = OPEN_EXISTING;
455 creation = OPEN_EXISTING;
456 break;
456 break;
457 case 'w':
457 case 'w':
458 creation = CREATE_ALWAYS;
458 creation = CREATE_ALWAYS;
459 break;
459 break;
460 case 'a':
460 case 'a':
461 creation = OPEN_ALWAYS;
461 creation = OPEN_ALWAYS;
462 flags |= _O_APPEND;
462 flags |= _O_APPEND;
463 break;
463 break;
464 default:
464 default:
465 PyErr_Format(PyExc_ValueError,
465 PyErr_Format(PyExc_ValueError,
466 "mode string must begin with one of 'r', 'w', "
466 "mode string must begin with one of 'r', 'w', "
467 "or 'a', not '%c'", m0);
467 "or 'a', not '%c'", m0);
468 goto bail;
468 goto bail;
469 }
469 }
470
470
471 handle = CreateFile(name, access,
471 handle = CreateFile(name, access,
472 FILE_SHARE_READ | FILE_SHARE_WRITE |
472 FILE_SHARE_READ | FILE_SHARE_WRITE |
473 FILE_SHARE_DELETE,
473 FILE_SHARE_DELETE,
474 NULL,
474 NULL,
475 creation,
475 creation,
476 FILE_ATTRIBUTE_NORMAL,
476 FILE_ATTRIBUTE_NORMAL,
477 0);
477 0);
478
478
479 if (handle == INVALID_HANDLE_VALUE) {
479 if (handle == INVALID_HANDLE_VALUE) {
480 PyErr_SetFromWindowsErrWithFilename(GetLastError(), name);
480 PyErr_SetFromWindowsErrWithFilename(GetLastError(), name);
481 goto bail;
481 goto bail;
482 }
482 }
483
483
484 fd = _open_osfhandle((intptr_t)handle, flags);
484 fd = _open_osfhandle((intptr_t)handle, flags);
485
485
486 if (fd == -1) {
486 if (fd == -1) {
487 CloseHandle(handle);
487 CloseHandle(handle);
488 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
488 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
489 goto bail;
489 goto bail;
490 }
490 }
491 #ifndef IS_PY3K
491 #ifndef IS_PY3K
492 fp = _fdopen(fd, fpmode);
492 fp = _fdopen(fd, fpmode);
493 if (fp == NULL) {
493 if (fp == NULL) {
494 _close(fd);
494 _close(fd);
495 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
495 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
496 goto bail;
496 goto bail;
497 }
497 }
498
498
499 file_obj = PyFile_FromFile(fp, name, mode, fclose);
499 file_obj = PyFile_FromFile(fp, name, mode, fclose);
500 if (file_obj == NULL) {
500 if (file_obj == NULL) {
501 fclose(fp);
501 fclose(fp);
502 goto bail;
502 goto bail;
503 }
503 }
504
504
505 PyFile_SetBufSize(file_obj, bufsize);
505 PyFile_SetBufSize(file_obj, bufsize);
506 #else
506 #else
507 file_obj = PyFile_FromFd(fd, name, mode, bufsize, NULL, NULL, NULL, 1);
507 file_obj = PyFile_FromFd(fd, name, mode, bufsize, NULL, NULL, NULL, 1);
508 if (file_obj == NULL)
508 if (file_obj == NULL)
509 goto bail;
509 goto bail;
510 #endif
510 #endif
511 bail:
511 bail:
512 PyMem_Free(name);
512 PyMem_Free(name);
513 return file_obj;
513 return file_obj;
514 }
514 }
515 #endif
515 #endif
516
516
517 static char osutil_doc[] = "Native operating system services.";
517 static char osutil_doc[] = "Native operating system services.";
518
518
519 static PyMethodDef methods[] = {
519 static PyMethodDef methods[] = {
520 {"listdir", (PyCFunction)listdir, METH_VARARGS | METH_KEYWORDS,
520 {"listdir", (PyCFunction)listdir, METH_VARARGS | METH_KEYWORDS,
521 "list a directory\n"},
521 "list a directory\n"},
522 #ifdef _WIN32
522 #ifdef _WIN32
523 {"posixfile", (PyCFunction)posixfile, METH_VARARGS | METH_KEYWORDS,
523 {"posixfile", (PyCFunction)posixfile, METH_VARARGS | METH_KEYWORDS,
524 "Open a file with POSIX-like semantics.\n"
524 "Open a file with POSIX-like semantics.\n"
525 "On error, this function may raise either a WindowsError or an IOError."},
525 "On error, this function may raise either a WindowsError or an IOError."},
526 #endif
526 #endif
527 {NULL, NULL}
527 {NULL, NULL}
528 };
528 };
529
529
530 #ifdef IS_PY3K
530 #ifdef IS_PY3K
531 static struct PyModuleDef osutil_module = {
531 static struct PyModuleDef osutil_module = {
532 PyModuleDef_HEAD_INIT,
532 PyModuleDef_HEAD_INIT,
533 "osutil",
533 "osutil",
534 osutil_doc,
534 osutil_doc,
535 -1,
535 -1,
536 methods
536 methods
537 };
537 };
538
538
539 PyMODINIT_FUNC PyInit_osutil(void)
539 PyMODINIT_FUNC PyInit_osutil(void)
540 {
540 {
541 if (PyType_Ready(&listdir_stat_type) < 0)
541 if (PyType_Ready(&listdir_stat_type) < 0)
542 return NULL;
542 return NULL;
543
543
544 return PyModule_Create(&osutil_module);
544 return PyModule_Create(&osutil_module);
545 }
545 }
546 #else
546 #else
547 PyMODINIT_FUNC initosutil(void)
547 PyMODINIT_FUNC initosutil(void)
548 {
548 {
549 if (PyType_Ready(&listdir_stat_type) == -1)
549 if (PyType_Ready(&listdir_stat_type) == -1)
550 return;
550 return;
551
551
552 Py_InitModule3("osutil", methods, osutil_doc);
552 Py_InitModule3("osutil", methods, osutil_doc);
553 }
553 }
554 #endif
554 #endif
555
556 /* Local Variables: */
557 /* c-file-style: "linux" */
558 /* indent-tabs-mode: t */
559 /* End: */
@@ -1,418 +1,422 b''
1 /*
1 /*
2 parsers.c - efficient content parsing
2 parsers.c - efficient content parsing
3
3
4 Copyright 2008 Matt Mackall <mpm@selenic.com> and others
4 Copyright 2008 Matt Mackall <mpm@selenic.com> and others
5
5
6 This software may be used and distributed according to the terms of
6 This software may be used and distributed according to the terms of
7 the GNU General Public License, incorporated herein by reference.
7 the GNU General Public License, incorporated herein by reference.
8 */
8 */
9
9
10 #include <Python.h>
10 #include <Python.h>
11 #include <ctype.h>
11 #include <ctype.h>
12 #include <string.h>
12 #include <string.h>
13
13
14 #include "util.h"
14 #include "util.h"
15
15
16 static int hexdigit(char c)
16 static int hexdigit(char c)
17 {
17 {
18 if (c >= '0' && c <= '9')
18 if (c >= '0' && c <= '9')
19 return c - '0';
19 return c - '0';
20 if (c >= 'a' && c <= 'f')
20 if (c >= 'a' && c <= 'f')
21 return c - 'a' + 10;
21 return c - 'a' + 10;
22 if (c >= 'A' && c <= 'F')
22 if (c >= 'A' && c <= 'F')
23 return c - 'A' + 10;
23 return c - 'A' + 10;
24
24
25 PyErr_SetString(PyExc_ValueError, "input contains non-hex character");
25 PyErr_SetString(PyExc_ValueError, "input contains non-hex character");
26 return 0;
26 return 0;
27 }
27 }
28
28
29 /*
29 /*
30 * Turn a hex-encoded string into binary.
30 * Turn a hex-encoded string into binary.
31 */
31 */
32 static PyObject *unhexlify(const char *str, int len)
32 static PyObject *unhexlify(const char *str, int len)
33 {
33 {
34 PyObject *ret;
34 PyObject *ret;
35 const char *c;
35 const char *c;
36 char *d;
36 char *d;
37
37
38 ret = PyBytes_FromStringAndSize(NULL, len / 2);
38 ret = PyBytes_FromStringAndSize(NULL, len / 2);
39
39
40 if (!ret)
40 if (!ret)
41 return NULL;
41 return NULL;
42
42
43 d = PyBytes_AsString(ret);
43 d = PyBytes_AsString(ret);
44
44
45 for (c = str; c < str + len;) {
45 for (c = str; c < str + len;) {
46 int hi = hexdigit(*c++);
46 int hi = hexdigit(*c++);
47 int lo = hexdigit(*c++);
47 int lo = hexdigit(*c++);
48 *d++ = (hi << 4) | lo;
48 *d++ = (hi << 4) | lo;
49 }
49 }
50
50
51 return ret;
51 return ret;
52 }
52 }
53
53
54 /*
54 /*
55 * This code assumes that a manifest is stitched together with newline
55 * This code assumes that a manifest is stitched together with newline
56 * ('\n') characters.
56 * ('\n') characters.
57 */
57 */
58 static PyObject *parse_manifest(PyObject *self, PyObject *args)
58 static PyObject *parse_manifest(PyObject *self, PyObject *args)
59 {
59 {
60 PyObject *mfdict, *fdict;
60 PyObject *mfdict, *fdict;
61 char *str, *cur, *start, *zero;
61 char *str, *cur, *start, *zero;
62 int len;
62 int len;
63
63
64 if (!PyArg_ParseTuple(args, "O!O!s#:parse_manifest",
64 if (!PyArg_ParseTuple(args, "O!O!s#:parse_manifest",
65 &PyDict_Type, &mfdict,
65 &PyDict_Type, &mfdict,
66 &PyDict_Type, &fdict,
66 &PyDict_Type, &fdict,
67 &str, &len))
67 &str, &len))
68 goto quit;
68 goto quit;
69
69
70 for (start = cur = str, zero = NULL; cur < str + len; cur++) {
70 for (start = cur = str, zero = NULL; cur < str + len; cur++) {
71 PyObject *file = NULL, *node = NULL;
71 PyObject *file = NULL, *node = NULL;
72 PyObject *flags = NULL;
72 PyObject *flags = NULL;
73 int nlen;
73 int nlen;
74
74
75 if (!*cur) {
75 if (!*cur) {
76 zero = cur;
76 zero = cur;
77 continue;
77 continue;
78 }
78 }
79 else if (*cur != '\n')
79 else if (*cur != '\n')
80 continue;
80 continue;
81
81
82 if (!zero) {
82 if (!zero) {
83 PyErr_SetString(PyExc_ValueError,
83 PyErr_SetString(PyExc_ValueError,
84 "manifest entry has no separator");
84 "manifest entry has no separator");
85 goto quit;
85 goto quit;
86 }
86 }
87
87
88 file = PyBytes_FromStringAndSize(start, zero - start);
88 file = PyBytes_FromStringAndSize(start, zero - start);
89
89
90 if (!file)
90 if (!file)
91 goto bail;
91 goto bail;
92
92
93 nlen = cur - zero - 1;
93 nlen = cur - zero - 1;
94
94
95 node = unhexlify(zero + 1, nlen > 40 ? 40 : nlen);
95 node = unhexlify(zero + 1, nlen > 40 ? 40 : nlen);
96 if (!node)
96 if (!node)
97 goto bail;
97 goto bail;
98
98
99 if (nlen > 40) {
99 if (nlen > 40) {
100 flags = PyBytes_FromStringAndSize(zero + 41,
100 flags = PyBytes_FromStringAndSize(zero + 41,
101 nlen - 40);
101 nlen - 40);
102 if (!flags)
102 if (!flags)
103 goto bail;
103 goto bail;
104
104
105 if (PyDict_SetItem(fdict, file, flags) == -1)
105 if (PyDict_SetItem(fdict, file, flags) == -1)
106 goto bail;
106 goto bail;
107 }
107 }
108
108
109 if (PyDict_SetItem(mfdict, file, node) == -1)
109 if (PyDict_SetItem(mfdict, file, node) == -1)
110 goto bail;
110 goto bail;
111
111
112 start = cur + 1;
112 start = cur + 1;
113 zero = NULL;
113 zero = NULL;
114
114
115 Py_XDECREF(flags);
115 Py_XDECREF(flags);
116 Py_XDECREF(node);
116 Py_XDECREF(node);
117 Py_XDECREF(file);
117 Py_XDECREF(file);
118 continue;
118 continue;
119 bail:
119 bail:
120 Py_XDECREF(flags);
120 Py_XDECREF(flags);
121 Py_XDECREF(node);
121 Py_XDECREF(node);
122 Py_XDECREF(file);
122 Py_XDECREF(file);
123 goto quit;
123 goto quit;
124 }
124 }
125
125
126 if (len > 0 && *(cur - 1) != '\n') {
126 if (len > 0 && *(cur - 1) != '\n') {
127 PyErr_SetString(PyExc_ValueError,
127 PyErr_SetString(PyExc_ValueError,
128 "manifest contains trailing garbage");
128 "manifest contains trailing garbage");
129 goto quit;
129 goto quit;
130 }
130 }
131
131
132 Py_INCREF(Py_None);
132 Py_INCREF(Py_None);
133 return Py_None;
133 return Py_None;
134 quit:
134 quit:
135 return NULL;
135 return NULL;
136 }
136 }
137
137
138 #ifdef _WIN32
138 #ifdef _WIN32
139 #ifdef _MSC_VER
139 #ifdef _MSC_VER
140 /* msvc 6.0 has problems */
140 /* msvc 6.0 has problems */
141 #define inline __inline
141 #define inline __inline
142 typedef unsigned long uint32_t;
142 typedef unsigned long uint32_t;
143 typedef unsigned __int64 uint64_t;
143 typedef unsigned __int64 uint64_t;
144 #else
144 #else
145 #include <stdint.h>
145 #include <stdint.h>
146 #endif
146 #endif
147 static uint32_t ntohl(uint32_t x)
147 static uint32_t ntohl(uint32_t x)
148 {
148 {
149 return ((x & 0x000000ffUL) << 24) |
149 return ((x & 0x000000ffUL) << 24) |
150 ((x & 0x0000ff00UL) << 8) |
150 ((x & 0x0000ff00UL) << 8) |
151 ((x & 0x00ff0000UL) >> 8) |
151 ((x & 0x00ff0000UL) >> 8) |
152 ((x & 0xff000000UL) >> 24);
152 ((x & 0xff000000UL) >> 24);
153 }
153 }
154 #else
154 #else
155 /* not windows */
155 /* not windows */
156 #include <sys/types.h>
156 #include <sys/types.h>
157 #if defined __BEOS__ && !defined __HAIKU__
157 #if defined __BEOS__ && !defined __HAIKU__
158 #include <ByteOrder.h>
158 #include <ByteOrder.h>
159 #else
159 #else
160 #include <arpa/inet.h>
160 #include <arpa/inet.h>
161 #endif
161 #endif
162 #include <inttypes.h>
162 #include <inttypes.h>
163 #endif
163 #endif
164
164
165 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
165 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
166 {
166 {
167 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
167 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
168 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
168 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
169 char *str, *cur, *end, *cpos;
169 char *str, *cur, *end, *cpos;
170 int state, mode, size, mtime;
170 int state, mode, size, mtime;
171 unsigned int flen;
171 unsigned int flen;
172 int len;
172 int len;
173 char decode[16]; /* for alignment */
173 char decode[16]; /* for alignment */
174
174
175 if (!PyArg_ParseTuple(args, "O!O!s#:parse_dirstate",
175 if (!PyArg_ParseTuple(args, "O!O!s#:parse_dirstate",
176 &PyDict_Type, &dmap,
176 &PyDict_Type, &dmap,
177 &PyDict_Type, &cmap,
177 &PyDict_Type, &cmap,
178 &str, &len))
178 &str, &len))
179 goto quit;
179 goto quit;
180
180
181 /* read parents */
181 /* read parents */
182 if (len < 40)
182 if (len < 40)
183 goto quit;
183 goto quit;
184
184
185 parents = Py_BuildValue("s#s#", str, 20, str + 20, 20);
185 parents = Py_BuildValue("s#s#", str, 20, str + 20, 20);
186 if (!parents)
186 if (!parents)
187 goto quit;
187 goto quit;
188
188
189 /* read filenames */
189 /* read filenames */
190 cur = str + 40;
190 cur = str + 40;
191 end = str + len;
191 end = str + len;
192
192
193 while (cur < end - 17) {
193 while (cur < end - 17) {
194 /* unpack header */
194 /* unpack header */
195 state = *cur;
195 state = *cur;
196 memcpy(decode, cur + 1, 16);
196 memcpy(decode, cur + 1, 16);
197 mode = ntohl(*(uint32_t *)(decode));
197 mode = ntohl(*(uint32_t *)(decode));
198 size = ntohl(*(uint32_t *)(decode + 4));
198 size = ntohl(*(uint32_t *)(decode + 4));
199 mtime = ntohl(*(uint32_t *)(decode + 8));
199 mtime = ntohl(*(uint32_t *)(decode + 8));
200 flen = ntohl(*(uint32_t *)(decode + 12));
200 flen = ntohl(*(uint32_t *)(decode + 12));
201 cur += 17;
201 cur += 17;
202 if (cur + flen > end || cur + flen < cur) {
202 if (cur + flen > end || cur + flen < cur) {
203 PyErr_SetString(PyExc_ValueError, "overflow in dirstate");
203 PyErr_SetString(PyExc_ValueError, "overflow in dirstate");
204 goto quit;
204 goto quit;
205 }
205 }
206
206
207 entry = Py_BuildValue("ciii", state, mode, size, mtime);
207 entry = Py_BuildValue("ciii", state, mode, size, mtime);
208 if (!entry)
208 if (!entry)
209 goto quit;
209 goto quit;
210 PyObject_GC_UnTrack(entry); /* don't waste time with this */
210 PyObject_GC_UnTrack(entry); /* don't waste time with this */
211
211
212 cpos = memchr(cur, 0, flen);
212 cpos = memchr(cur, 0, flen);
213 if (cpos) {
213 if (cpos) {
214 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
214 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
215 cname = PyBytes_FromStringAndSize(cpos + 1,
215 cname = PyBytes_FromStringAndSize(cpos + 1,
216 flen - (cpos - cur) - 1);
216 flen - (cpos - cur) - 1);
217 if (!fname || !cname ||
217 if (!fname || !cname ||
218 PyDict_SetItem(cmap, fname, cname) == -1 ||
218 PyDict_SetItem(cmap, fname, cname) == -1 ||
219 PyDict_SetItem(dmap, fname, entry) == -1)
219 PyDict_SetItem(dmap, fname, entry) == -1)
220 goto quit;
220 goto quit;
221 Py_DECREF(cname);
221 Py_DECREF(cname);
222 } else {
222 } else {
223 fname = PyBytes_FromStringAndSize(cur, flen);
223 fname = PyBytes_FromStringAndSize(cur, flen);
224 if (!fname ||
224 if (!fname ||
225 PyDict_SetItem(dmap, fname, entry) == -1)
225 PyDict_SetItem(dmap, fname, entry) == -1)
226 goto quit;
226 goto quit;
227 }
227 }
228 cur += flen;
228 cur += flen;
229 Py_DECREF(fname);
229 Py_DECREF(fname);
230 Py_DECREF(entry);
230 Py_DECREF(entry);
231 fname = cname = entry = NULL;
231 fname = cname = entry = NULL;
232 }
232 }
233
233
234 ret = parents;
234 ret = parents;
235 Py_INCREF(ret);
235 Py_INCREF(ret);
236 quit:
236 quit:
237 Py_XDECREF(fname);
237 Py_XDECREF(fname);
238 Py_XDECREF(cname);
238 Py_XDECREF(cname);
239 Py_XDECREF(entry);
239 Py_XDECREF(entry);
240 Py_XDECREF(parents);
240 Py_XDECREF(parents);
241 return ret;
241 return ret;
242 }
242 }
243
243
244 const char nullid[20];
244 const char nullid[20];
245 const int nullrev = -1;
245 const int nullrev = -1;
246
246
247 /* RevlogNG format (all in big endian, data may be inlined):
247 /* RevlogNG format (all in big endian, data may be inlined):
248 * 6 bytes: offset
248 * 6 bytes: offset
249 * 2 bytes: flags
249 * 2 bytes: flags
250 * 4 bytes: compressed length
250 * 4 bytes: compressed length
251 * 4 bytes: uncompressed length
251 * 4 bytes: uncompressed length
252 * 4 bytes: base revision
252 * 4 bytes: base revision
253 * 4 bytes: link revision
253 * 4 bytes: link revision
254 * 4 bytes: parent 1 revision
254 * 4 bytes: parent 1 revision
255 * 4 bytes: parent 2 revision
255 * 4 bytes: parent 2 revision
256 * 32 bytes: nodeid (only 20 bytes used)
256 * 32 bytes: nodeid (only 20 bytes used)
257 */
257 */
258 static int _parse_index_ng(const char *data, int size, int inlined,
258 static int _parse_index_ng(const char *data, int size, int inlined,
259 PyObject *index)
259 PyObject *index)
260 {
260 {
261 PyObject *entry;
261 PyObject *entry;
262 int n = 0, err;
262 int n = 0, err;
263 uint64_t offset_flags;
263 uint64_t offset_flags;
264 int comp_len, uncomp_len, base_rev, link_rev, parent_1, parent_2;
264 int comp_len, uncomp_len, base_rev, link_rev, parent_1, parent_2;
265 const char *c_node_id;
265 const char *c_node_id;
266 const char *end = data + size;
266 const char *end = data + size;
267 char decode[64]; /* to enforce alignment with inline data */
267 char decode[64]; /* to enforce alignment with inline data */
268
268
269 while (data < end) {
269 while (data < end) {
270 unsigned int step;
270 unsigned int step;
271
271
272 memcpy(decode, data, 64);
272 memcpy(decode, data, 64);
273 offset_flags = ntohl(*((uint32_t *) (decode + 4)));
273 offset_flags = ntohl(*((uint32_t *) (decode + 4)));
274 if (n == 0) /* mask out version number for the first entry */
274 if (n == 0) /* mask out version number for the first entry */
275 offset_flags &= 0xFFFF;
275 offset_flags &= 0xFFFF;
276 else {
276 else {
277 uint32_t offset_high = ntohl(*((uint32_t *)decode));
277 uint32_t offset_high = ntohl(*((uint32_t *)decode));
278 offset_flags |= ((uint64_t)offset_high) << 32;
278 offset_flags |= ((uint64_t)offset_high) << 32;
279 }
279 }
280
280
281 comp_len = ntohl(*((uint32_t *)(decode + 8)));
281 comp_len = ntohl(*((uint32_t *)(decode + 8)));
282 uncomp_len = ntohl(*((uint32_t *)(decode + 12)));
282 uncomp_len = ntohl(*((uint32_t *)(decode + 12)));
283 base_rev = ntohl(*((uint32_t *)(decode + 16)));
283 base_rev = ntohl(*((uint32_t *)(decode + 16)));
284 link_rev = ntohl(*((uint32_t *)(decode + 20)));
284 link_rev = ntohl(*((uint32_t *)(decode + 20)));
285 parent_1 = ntohl(*((uint32_t *)(decode + 24)));
285 parent_1 = ntohl(*((uint32_t *)(decode + 24)));
286 parent_2 = ntohl(*((uint32_t *)(decode + 28)));
286 parent_2 = ntohl(*((uint32_t *)(decode + 28)));
287 c_node_id = decode + 32;
287 c_node_id = decode + 32;
288
288
289 entry = Py_BuildValue("Liiiiiis#", offset_flags, comp_len,
289 entry = Py_BuildValue("Liiiiiis#", offset_flags, comp_len,
290 uncomp_len, base_rev, link_rev,
290 uncomp_len, base_rev, link_rev,
291 parent_1, parent_2, c_node_id, 20);
291 parent_1, parent_2, c_node_id, 20);
292
292
293 if (!entry)
293 if (!entry)
294 return 0;
294 return 0;
295
295
296 PyObject_GC_UnTrack(entry); /* don't waste time with this */
296 PyObject_GC_UnTrack(entry); /* don't waste time with this */
297
297
298 if (inlined) {
298 if (inlined) {
299 err = PyList_Append(index, entry);
299 err = PyList_Append(index, entry);
300 Py_DECREF(entry);
300 Py_DECREF(entry);
301 if (err)
301 if (err)
302 return 0;
302 return 0;
303 } else
303 } else
304 PyList_SET_ITEM(index, n, entry); /* steals reference */
304 PyList_SET_ITEM(index, n, entry); /* steals reference */
305
305
306 n++;
306 n++;
307 step = 64 + (inlined ? comp_len : 0);
307 step = 64 + (inlined ? comp_len : 0);
308 if (data + step > end || data + step < data)
308 if (data + step > end || data + step < data)
309 break;
309 break;
310 data += step;
310 data += step;
311 }
311 }
312 if (data != end) {
312 if (data != end) {
313 if (!PyErr_Occurred())
313 if (!PyErr_Occurred())
314 PyErr_SetString(PyExc_ValueError, "corrupt index file");
314 PyErr_SetString(PyExc_ValueError, "corrupt index file");
315 return 0;
315 return 0;
316 }
316 }
317
317
318 /* create the magic nullid entry in the index at [-1] */
318 /* create the magic nullid entry in the index at [-1] */
319 entry = Py_BuildValue("Liiiiiis#", (uint64_t)0, 0, 0, -1, -1, -1, -1, nullid, 20);
319 entry = Py_BuildValue("Liiiiiis#", (uint64_t)0, 0, 0, -1, -1, -1, -1, nullid, 20);
320
320
321 if (!entry)
321 if (!entry)
322 return 0;
322 return 0;
323
323
324 PyObject_GC_UnTrack(entry); /* don't waste time with this */
324 PyObject_GC_UnTrack(entry); /* don't waste time with this */
325
325
326 if (inlined) {
326 if (inlined) {
327 err = PyList_Append(index, entry);
327 err = PyList_Append(index, entry);
328 Py_DECREF(entry);
328 Py_DECREF(entry);
329 if (err)
329 if (err)
330 return 0;
330 return 0;
331 } else
331 } else
332 PyList_SET_ITEM(index, n, entry); /* steals reference */
332 PyList_SET_ITEM(index, n, entry); /* steals reference */
333
333
334 return 1;
334 return 1;
335 }
335 }
336
336
337 /* This function parses a index file and returns a Python tuple of the
337 /* This function parses a index file and returns a Python tuple of the
338 * following format: (index, cache)
338 * following format: (index, cache)
339 *
339 *
340 * index: a list of tuples containing the RevlogNG records
340 * index: a list of tuples containing the RevlogNG records
341 * cache: if data is inlined, a tuple (index_file_content, 0) else None
341 * cache: if data is inlined, a tuple (index_file_content, 0) else None
342 */
342 */
343 static PyObject *parse_index2(PyObject *self, PyObject *args)
343 static PyObject *parse_index2(PyObject *self, PyObject *args)
344 {
344 {
345 const char *data;
345 const char *data;
346 int size, inlined;
346 int size, inlined;
347 PyObject *rval = NULL, *index = NULL, *cache = NULL;
347 PyObject *rval = NULL, *index = NULL, *cache = NULL;
348 PyObject *data_obj = NULL, *inlined_obj;
348 PyObject *data_obj = NULL, *inlined_obj;
349
349
350 if (!PyArg_ParseTuple(args, "s#O", &data, &size, &inlined_obj))
350 if (!PyArg_ParseTuple(args, "s#O", &data, &size, &inlined_obj))
351 return NULL;
351 return NULL;
352 inlined = inlined_obj && PyObject_IsTrue(inlined_obj);
352 inlined = inlined_obj && PyObject_IsTrue(inlined_obj);
353
353
354 /* If no data is inlined, we know the size of the index list in
354 /* If no data is inlined, we know the size of the index list in
355 * advance: size divided by the size of one revlog record (64 bytes)
355 * advance: size divided by the size of one revlog record (64 bytes)
356 * plus one for nullid */
356 * plus one for nullid */
357 index = inlined ? PyList_New(0) : PyList_New(size / 64 + 1);
357 index = inlined ? PyList_New(0) : PyList_New(size / 64 + 1);
358 if (!index)
358 if (!index)
359 goto quit;
359 goto quit;
360
360
361 /* set up the cache return value */
361 /* set up the cache return value */
362 if (inlined) {
362 if (inlined) {
363 /* Note that the reference to data_obj is only borrowed */
363 /* Note that the reference to data_obj is only borrowed */
364 data_obj = PyTuple_GET_ITEM(args, 0);
364 data_obj = PyTuple_GET_ITEM(args, 0);
365 cache = Py_BuildValue("iO", 0, data_obj);
365 cache = Py_BuildValue("iO", 0, data_obj);
366 if (!cache)
366 if (!cache)
367 goto quit;
367 goto quit;
368 } else {
368 } else {
369 cache = Py_None;
369 cache = Py_None;
370 Py_INCREF(Py_None);
370 Py_INCREF(Py_None);
371 }
371 }
372
372
373 /* actually populate the index with data */
373 /* actually populate the index with data */
374 if (!_parse_index_ng(data, size, inlined, index))
374 if (!_parse_index_ng(data, size, inlined, index))
375 goto quit;
375 goto quit;
376
376
377 rval = Py_BuildValue("NN", index, cache);
377 rval = Py_BuildValue("NN", index, cache);
378 if (!rval)
378 if (!rval)
379 goto quit;
379 goto quit;
380 return rval;
380 return rval;
381
381
382 quit:
382 quit:
383 Py_XDECREF(index);
383 Py_XDECREF(index);
384 Py_XDECREF(cache);
384 Py_XDECREF(cache);
385 Py_XDECREF(rval);
385 Py_XDECREF(rval);
386 return NULL;
386 return NULL;
387 }
387 }
388
388
389
389
390 static char parsers_doc[] = "Efficient content parsing.";
390 static char parsers_doc[] = "Efficient content parsing.";
391
391
392 static PyMethodDef methods[] = {
392 static PyMethodDef methods[] = {
393 {"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"},
393 {"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"},
394 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
394 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
395 {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
395 {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
396 {NULL, NULL}
396 {NULL, NULL}
397 };
397 };
398
398
399 #ifdef IS_PY3K
399 #ifdef IS_PY3K
400 static struct PyModuleDef parsers_module = {
400 static struct PyModuleDef parsers_module = {
401 PyModuleDef_HEAD_INIT,
401 PyModuleDef_HEAD_INIT,
402 "parsers",
402 "parsers",
403 parsers_doc,
403 parsers_doc,
404 -1,
404 -1,
405 methods
405 methods
406 };
406 };
407
407
408 PyMODINIT_FUNC PyInit_parsers(void)
408 PyMODINIT_FUNC PyInit_parsers(void)
409 {
409 {
410 return PyModule_Create(&parsers_module);
410 return PyModule_Create(&parsers_module);
411 }
411 }
412 #else
412 #else
413 PyMODINIT_FUNC initparsers(void)
413 PyMODINIT_FUNC initparsers(void)
414 {
414 {
415 Py_InitModule3("parsers", methods, parsers_doc);
415 Py_InitModule3("parsers", methods, parsers_doc);
416 }
416 }
417 #endif
417 #endif
418
418
419 /* Local Variables: */
420 /* c-file-style: "linux" */
421 /* indent-tabs-mode: t */
422 /* End: */
@@ -1,105 +1,109 b''
1 /*
1 /*
2 util.h - utility functions for interfacing with the various python APIs.
2 util.h - utility functions for interfacing with the various python APIs.
3
3
4 This software may be used and distributed according to the terms of
4 This software may be used and distributed according to the terms of
5 the GNU General Public License, incorporated herein by reference.
5 the GNU General Public License, incorporated herein by reference.
6 */
6 */
7
7
8 #ifndef _HG_UTIL_H_
8 #ifndef _HG_UTIL_H_
9 #define _HG_UTIL_H_
9 #define _HG_UTIL_H_
10
10
11 #if PY_MAJOR_VERSION >= 3
11 #if PY_MAJOR_VERSION >= 3
12
12
13 #define IS_PY3K
13 #define IS_PY3K
14 #define PyInt_FromLong PyLong_FromLong
14 #define PyInt_FromLong PyLong_FromLong
15 #define PyInt_AsLong PyLong_AsLong
15 #define PyInt_AsLong PyLong_AsLong
16
16
17 /*
17 /*
18 Mapping of some of the python < 2.x PyString* functions to py3k's PyUnicode.
18 Mapping of some of the python < 2.x PyString* functions to py3k's PyUnicode.
19
19
20 The commented names below represent those that are present in the PyBytes
20 The commented names below represent those that are present in the PyBytes
21 definitions for python < 2.6 (below in this file) that don't have a direct
21 definitions for python < 2.6 (below in this file) that don't have a direct
22 implementation.
22 implementation.
23 */
23 */
24
24
25 #define PyStringObject PyUnicodeObject
25 #define PyStringObject PyUnicodeObject
26 #define PyString_Type PyUnicode_Type
26 #define PyString_Type PyUnicode_Type
27
27
28 #define PyString_Check PyUnicode_Check
28 #define PyString_Check PyUnicode_Check
29 #define PyString_CheckExact PyUnicode_CheckExact
29 #define PyString_CheckExact PyUnicode_CheckExact
30 #define PyString_CHECK_INTERNED PyUnicode_CHECK_INTERNED
30 #define PyString_CHECK_INTERNED PyUnicode_CHECK_INTERNED
31 #define PyString_AS_STRING PyUnicode_AsLatin1String
31 #define PyString_AS_STRING PyUnicode_AsLatin1String
32 #define PyString_GET_SIZE PyUnicode_GET_SIZE
32 #define PyString_GET_SIZE PyUnicode_GET_SIZE
33
33
34 #define PyString_FromStringAndSize PyUnicode_FromStringAndSize
34 #define PyString_FromStringAndSize PyUnicode_FromStringAndSize
35 #define PyString_FromString PyUnicode_FromString
35 #define PyString_FromString PyUnicode_FromString
36 #define PyString_FromFormatV PyUnicode_FromFormatV
36 #define PyString_FromFormatV PyUnicode_FromFormatV
37 #define PyString_FromFormat PyUnicode_FromFormat
37 #define PyString_FromFormat PyUnicode_FromFormat
38 /* #define PyString_Size PyUnicode_GET_SIZE */
38 /* #define PyString_Size PyUnicode_GET_SIZE */
39 /* #define PyString_AsString */
39 /* #define PyString_AsString */
40 /* #define PyString_Repr */
40 /* #define PyString_Repr */
41 #define PyString_Concat PyUnicode_Concat
41 #define PyString_Concat PyUnicode_Concat
42 #define PyString_ConcatAndDel PyUnicode_AppendAndDel
42 #define PyString_ConcatAndDel PyUnicode_AppendAndDel
43 #define _PyString_Resize PyUnicode_Resize
43 #define _PyString_Resize PyUnicode_Resize
44 /* #define _PyString_Eq */
44 /* #define _PyString_Eq */
45 #define PyString_Format PyUnicode_Format
45 #define PyString_Format PyUnicode_Format
46 /* #define _PyString_FormatLong */
46 /* #define _PyString_FormatLong */
47 /* #define PyString_DecodeEscape */
47 /* #define PyString_DecodeEscape */
48 #define _PyString_Join PyUnicode_Join
48 #define _PyString_Join PyUnicode_Join
49 #define PyString_Decode PyUnicode_Decode
49 #define PyString_Decode PyUnicode_Decode
50 #define PyString_Encode PyUnicode_Encode
50 #define PyString_Encode PyUnicode_Encode
51 #define PyString_AsEncodedObject PyUnicode_AsEncodedObject
51 #define PyString_AsEncodedObject PyUnicode_AsEncodedObject
52 #define PyString_AsEncodedString PyUnicode_AsEncodedString
52 #define PyString_AsEncodedString PyUnicode_AsEncodedString
53 #define PyString_AsDecodedObject PyUnicode_AsDecodedObject
53 #define PyString_AsDecodedObject PyUnicode_AsDecodedObject
54 #define PyString_AsDecodedString PyUnicode_AsDecodedUnicode
54 #define PyString_AsDecodedString PyUnicode_AsDecodedUnicode
55 /* #define PyString_AsStringAndSize */
55 /* #define PyString_AsStringAndSize */
56 #define _PyString_InsertThousandsGrouping _PyUnicode_InsertThousandsGrouping
56 #define _PyString_InsertThousandsGrouping _PyUnicode_InsertThousandsGrouping
57
57
58 #endif /* PY_MAJOR_VERSION */
58 #endif /* PY_MAJOR_VERSION */
59
59
60 /* Backports from 2.6 */
60 /* Backports from 2.6 */
61 #if PY_VERSION_HEX < 0x02060000
61 #if PY_VERSION_HEX < 0x02060000
62
62
63 #define Py_TYPE(ob) (ob)->ob_type
63 #define Py_TYPE(ob) (ob)->ob_type
64 #define Py_SIZE(ob) (ob)->ob_size
64 #define Py_SIZE(ob) (ob)->ob_size
65 #define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size,
65 #define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size,
66
66
67 /* Shamelessly stolen from bytesobject.h */
67 /* Shamelessly stolen from bytesobject.h */
68 #define PyBytesObject PyStringObject
68 #define PyBytesObject PyStringObject
69 #define PyBytes_Type PyString_Type
69 #define PyBytes_Type PyString_Type
70
70
71 #define PyBytes_Check PyString_Check
71 #define PyBytes_Check PyString_Check
72 #define PyBytes_CheckExact PyString_CheckExact
72 #define PyBytes_CheckExact PyString_CheckExact
73 #define PyBytes_CHECK_INTERNED PyString_CHECK_INTERNED
73 #define PyBytes_CHECK_INTERNED PyString_CHECK_INTERNED
74 #define PyBytes_AS_STRING PyString_AS_STRING
74 #define PyBytes_AS_STRING PyString_AS_STRING
75 #define PyBytes_GET_SIZE PyString_GET_SIZE
75 #define PyBytes_GET_SIZE PyString_GET_SIZE
76 #define Py_TPFLAGS_BYTES_SUBCLASS Py_TPFLAGS_STRING_SUBCLASS
76 #define Py_TPFLAGS_BYTES_SUBCLASS Py_TPFLAGS_STRING_SUBCLASS
77
77
78 #define PyBytes_FromStringAndSize PyString_FromStringAndSize
78 #define PyBytes_FromStringAndSize PyString_FromStringAndSize
79 #define PyBytes_FromString PyString_FromString
79 #define PyBytes_FromString PyString_FromString
80 #define PyBytes_FromFormatV PyString_FromFormatV
80 #define PyBytes_FromFormatV PyString_FromFormatV
81 #define PyBytes_FromFormat PyString_FromFormat
81 #define PyBytes_FromFormat PyString_FromFormat
82 #define PyBytes_Size PyString_Size
82 #define PyBytes_Size PyString_Size
83 #define PyBytes_AsString PyString_AsString
83 #define PyBytes_AsString PyString_AsString
84 #define PyBytes_Repr PyString_Repr
84 #define PyBytes_Repr PyString_Repr
85 #define PyBytes_Concat PyString_Concat
85 #define PyBytes_Concat PyString_Concat
86 #define PyBytes_ConcatAndDel PyString_ConcatAndDel
86 #define PyBytes_ConcatAndDel PyString_ConcatAndDel
87 #define _PyBytes_Resize _PyString_Resize
87 #define _PyBytes_Resize _PyString_Resize
88 #define _PyBytes_Eq _PyString_Eq
88 #define _PyBytes_Eq _PyString_Eq
89 #define PyBytes_Format PyString_Format
89 #define PyBytes_Format PyString_Format
90 #define _PyBytes_FormatLong _PyString_FormatLong
90 #define _PyBytes_FormatLong _PyString_FormatLong
91 #define PyBytes_DecodeEscape PyString_DecodeEscape
91 #define PyBytes_DecodeEscape PyString_DecodeEscape
92 #define _PyBytes_Join _PyString_Join
92 #define _PyBytes_Join _PyString_Join
93 #define PyBytes_Decode PyString_Decode
93 #define PyBytes_Decode PyString_Decode
94 #define PyBytes_Encode PyString_Encode
94 #define PyBytes_Encode PyString_Encode
95 #define PyBytes_AsEncodedObject PyString_AsEncodedObject
95 #define PyBytes_AsEncodedObject PyString_AsEncodedObject
96 #define PyBytes_AsEncodedString PyString_AsEncodedString
96 #define PyBytes_AsEncodedString PyString_AsEncodedString
97 #define PyBytes_AsDecodedObject PyString_AsDecodedObject
97 #define PyBytes_AsDecodedObject PyString_AsDecodedObject
98 #define PyBytes_AsDecodedString PyString_AsDecodedString
98 #define PyBytes_AsDecodedString PyString_AsDecodedString
99 #define PyBytes_AsStringAndSize PyString_AsStringAndSize
99 #define PyBytes_AsStringAndSize PyString_AsStringAndSize
100 #define _PyBytes_InsertThousandsGrouping _PyString_InsertThousandsGrouping
100 #define _PyBytes_InsertThousandsGrouping _PyString_InsertThousandsGrouping
101
101
102 #endif /* PY_VERSION_HEX */
102 #endif /* PY_VERSION_HEX */
103
103
104 #endif /* _HG_UTIL_H_ */
104 #endif /* _HG_UTIL_H_ */
105
105
106 /* Local Variables: */
107 /* c-file-style: "linux" */
108 /* indent-tabs-mode: t */
109 /* End: */
General Comments 0
You need to be logged in to leave comments. Login now