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