1 /*
2 ** Copyright 2003 Double Precision, Inc.
3 ** See COPYING for distribution information.
4 */
5 
6 #if	HAVE_CONFIG_H
7 #include	"config.h"
8 #endif
9 #include	<stdio.h>
10 #include	<stdlib.h>
11 #include	<string.h>
12 #include	<errno.h>
13 #include	<ctype.h>
14 #include	<fcntl.h>
15 #if	HAVE_UNISTD_H
16 #include	<unistd.h>
17 #endif
18 #if	HAVE_UTIME_H
19 #include	<utime.h>
20 #endif
21 #if TIME_WITH_SYS_TIME
22 #include	<sys/time.h>
23 #include	<time.h>
24 #else
25 #if HAVE_SYS_TIME_H
26 #include	<sys/time.h>
27 #else
28 #include	<time.h>
29 #endif
30 #endif
31 
32 #include	<sys/types.h>
33 #include	<sys/stat.h>
34 
35 #include	"config.h"
36 #include	"maildirinfo.h"
37 
38 #include	"maildirmisc.h"
39 #include	"maildirnewshared.h"
40 #include	<courier-unicode.h>
41 
maildir_info_destroy(struct maildir_info * info)42 void maildir_info_destroy(struct maildir_info *info)
43 {
44 	if (info->homedir)
45 		free(info->homedir);
46 	if (info->maildir)
47 		free(info->maildir);
48 	if (info->owner)
49 		free(info->owner);
50 	info->homedir=NULL;
51 	info->maildir=NULL;
52 	info->owner=NULL;
53 }
54 
55 struct imap_find_shared {
56 	struct maildir_info *info;
57 	const char *path;
58 	size_t path_l;
59 	char *homedir;
60 	char *maildir;
61 };
62 
imap_find_cb(struct maildir_newshared_enum_cb * cb)63 static int imap_find_cb(struct maildir_newshared_enum_cb *cb)
64 {
65 	struct imap_find_shared *ifs=(struct imap_find_shared *)cb->cb_arg;
66 
67 	if (cb->homedir)
68 	{
69 		ifs->homedir=strdup(cb->homedir);
70 		if (!ifs->homedir)
71 			return -1;
72 	}
73 
74 	if (cb->maildir)
75 	{
76 		ifs->maildir=strdup(cb->maildir);
77 
78 		if (!ifs->maildir)
79 		{
80 			if (cb->homedir)
81 			{
82 				free(ifs->homedir);
83 				ifs->homedir=NULL;
84 			}
85 			return -1;
86 		}
87 	}
88 
89 	return 0;
90 }
91 
maildir_info_imap_find(struct maildir_info * info,const char * path,const char * myId)92 int maildir_info_imap_find(struct maildir_info *info, const char *path,
93 			   const char *myId)
94 {
95 	const char *p;
96 	struct imap_find_shared ifs;
97 	const char *indexfile;
98 	char *indexfile_cpy;
99 	struct maildir_shindex_cache *curcache;
100 	const char *subhierarchy;
101 
102 	info->homedir=NULL;
103 	info->maildir=NULL;
104 	info->owner=NULL;
105 
106 	if (strchr(path, '/'))
107 	{
108 		errno=EINVAL;
109 		return -1;
110 	}
111 
112 	for (p=path; *p; p++)
113 		if (*p == '.' && p[1] == '.')
114 		{
115 			errno=EINVAL;
116 			return -1;
117 		}
118 
119 	if (strncmp(path, SHARED, sizeof(SHARED)-1) == 0)
120 	{
121 		path += sizeof(SHARED)-1;
122 
123 		info->homedir=strdup(".");
124 		if (!info->homedir)
125 			return -1;
126 
127 		info->mailbox_type=MAILBOXTYPE_OLDSHARED;
128 		info->owner=strdup("anonymous");
129 
130 		if (!info->owner)
131 		{
132 			maildir_info_destroy(info);
133 			return -1;
134 		}
135 
136 		/* We need to specialcase "shared" and "shared.name".
137 		** maildir_shareddir will return NULL for these cases, because
138 		** it will insist on "name.folder", but we need to return a
139 		** non NULL value to indicate that this is a valid hierarchy
140 		** name.  We return a special value of an empty string, which
141 		** is checked for in situations where a valid folder is
142 		** required.
143 		*/
144 
145 		if (*path && *path != '.')
146 		{
147 			maildir_info_destroy(info);
148 			errno=EINVAL;
149 			return -1;
150 		}
151 
152 		return 0;
153 	}
154 
155 	if (strncasecmp(path, INBOX, sizeof(INBOX)-1) == 0)
156 	{
157 		switch (path[sizeof(INBOX)-1]) {
158 		case 0:
159 		case '.':
160 			break;
161 		default:
162 			errno=EINVAL;
163 			return -1;
164 		}
165 
166 		info->homedir=strdup(".");
167 
168 		if (!info->homedir)
169 			return -1;
170 
171 		info->maildir=strdup(path);
172 		if (!info->maildir)
173 		{
174 			maildir_info_destroy(info);
175 			return -1;
176 		}
177 
178 		info->owner=malloc(strlen(myId)+sizeof("user="));
179 
180 		if (!info->owner)
181 		{
182 			maildir_info_destroy(info);
183 			return -1;
184 		}
185 
186 		info->mailbox_type=MAILBOXTYPE_INBOX;
187 		strcat(strcpy(info->owner, "user="), myId);
188 		return 0;
189 	}
190 
191 	if (strncmp(path, NEWSHARED,
192 		    sizeof(NEWSHARED)-1) != 0)
193 	{
194 		errno=EINVAL;
195 		return -1;
196 	}
197 
198 	ifs.info=info;
199 	ifs.path=path+sizeof(NEWSHARED)-1;
200 
201 	info->mailbox_type=MAILBOXTYPE_NEWSHARED;
202 	info->homedir=NULL;
203 	info->maildir=NULL;
204 	info->owner=strdup("vendor=courier.internal");
205 
206 	if (!info->owner)
207 		return -1;
208 
209 	ifs.homedir=NULL;
210 	ifs.maildir=NULL;
211 
212 	indexfile=NULL;
213 	indexfile_cpy=NULL;
214 	curcache=NULL;
215 	subhierarchy=NULL;
216 
217 	while (*ifs.path)
218 	{
219 		int rc, eof;
220 		size_t i;
221 
222 		curcache=maildir_shared_cache_read(curcache, indexfile,
223 						   subhierarchy);
224 
225 		if (indexfile_cpy)
226 		{
227 			free(indexfile_cpy);
228 			indexfile_cpy=NULL;
229 		}
230 
231 		if (!curcache)
232 			break;
233 
234 		p=strchr(ifs.path, '.');
235 
236 		if (p)
237 			ifs.path_l=p-ifs.path;
238 		else
239 			ifs.path_l=strlen(ifs.path);
240 
241 
242 		if (ifs.homedir)
243 			free(ifs.homedir);
244 		if (ifs.maildir)
245 			free(ifs.maildir);
246 
247 		ifs.homedir=NULL;
248 		ifs.maildir=NULL;
249 
250 		for (i=0; i < curcache->nrecords; i++)
251 		{
252 			char *n=maildir_info_imapmunge(curcache->
253 						       records[i].name);
254 
255 			if (n == NULL)
256 			{
257 				i=curcache->nrecords;
258 				break;
259 			}
260 
261 			if (strlen(n) == ifs.path_l &&
262 			    strncmp(n, ifs.path, ifs.path_l) == 0)
263 			{
264 				free(n);
265 				break;
266 			}
267 			free(n);
268 		}
269 
270 		if (i >= curcache->nrecords)
271 			break;
272 
273 		curcache->indexfile.startingpos=
274 			curcache->records[i].offset;
275 		rc=maildir_newshared_nextAt(&curcache->indexfile,
276 					    &eof,
277 					    imap_find_cb, &ifs);
278 
279 		if (rc || eof)
280 		{
281 			fprintf(stderr, "ERR: Internal error -"
282 				" maildir_newshared_nextAt: %s\n",
283 				strerror(errno));
284 			fflush(stderr);
285 			break;
286 		}
287 
288 		if (!ifs.homedir && !ifs.maildir)
289 			break;
290 
291 		if (!ifs.homedir)
292 		{
293 			indexfile=indexfile_cpy=ifs.maildir;
294 			ifs.maildir=NULL;
295 			subhierarchy=curcache->records[i].name;
296 
297 			ifs.path += ifs.path_l;
298 			if (*ifs.path)
299 				++ifs.path;
300 			continue;
301 		}
302 
303 		info->homedir=maildir_location(ifs.homedir,
304 					     ifs.maildir);
305 
306 		free(ifs.homedir);
307 		free(ifs.maildir);
308 
309 		free(info->owner);
310 
311 		if (!info->homedir)
312 		{
313 			info->maildir=NULL;
314 			info->owner=NULL;
315 			return -1;
316 		}
317 
318 		if (!subhierarchy || !*subhierarchy)
319 		{
320 			info->owner=strdup("vendor=courier.internal");
321 			if (!info->owner)
322 			{
323 				free(info->homedir);
324 				info->homedir=NULL;
325 				info->maildir=NULL;
326 				return -1;
327 			}
328 		}
329 		else
330 		{
331 			char *owner_utf8;
332 
333 			info->owner=malloc(strlen(subhierarchy)
334 					   +sizeof("user="));
335 
336 			if (!info->owner)
337 			{
338 				free(info->homedir);
339 				info->homedir=NULL;
340 				info->maildir=NULL;
341 				info->owner=NULL;
342 				return -1;
343 			}
344 			strcpy(info->owner, "user=");
345 			strcat(info->owner, subhierarchy);
346 
347 			/*
348 			** The folder path is in modified-UTF7.  The owner is
349 			** obtained from shared hierarchy, but in ACL2 the
350 			** identifiers are in UTF8.
351 			*/
352 
353 			owner_utf8=
354 				unicode_convert_tobuf(info->owner,
355 							unicode_x_imap_modutf7,
356 							"utf-8", NULL);
357 
358 			if (!owner_utf8)
359 			{
360 				free(info->homedir);
361 				info->homedir=NULL;
362 				return (0);
363 			}
364 
365 			free(info->owner);
366 			info->owner=owner_utf8;
367 		}
368 
369 		ifs.path += ifs.path_l;
370 
371 		info->maildir=malloc(strlen(INBOX)+1+strlen(ifs.path));
372 		if (!info->maildir)
373 		{
374 			free(info->owner);
375 			free(info->homedir);
376 			info->owner=NULL;
377 			info->homedir=NULL;
378 			return -1;
379 		}
380 		strcat(strcpy(info->maildir, INBOX), ifs.path);
381 
382 		if (maildir_info_suppress(info->homedir))
383 		{
384 
385 			free(info->homedir);
386 			free(info->maildir);
387 			info->homedir=NULL;
388 			info->maildir=NULL;
389 			info->mailbox_type=MAILBOXTYPE_IGNORE;
390 			free(info->owner);
391 			info->owner=NULL;
392 			info->owner=strdup("vendor=courier.internal");
393 			if (!info->owner)
394 			{
395 				return -1;
396 			}
397 		}
398 
399 		return 0;
400 	}
401 
402 	if (indexfile_cpy)
403 		free(indexfile_cpy);
404 	if (ifs.homedir)
405 	{
406 		free(ifs.homedir);
407 		ifs.homedir=NULL;
408 	}
409 
410 	if (ifs.maildir)
411 	{
412 		free(ifs.maildir);
413 		ifs.maildir=NULL;
414 	}
415 	return 0;
416 }
417 
418 /***************************************************************************/
419 
420 /*
421 ** Maildir folders are named in IMAP-compatible modified-UTF8 encoding,
422 ** with periods as hierarchy delimiters.  One exception: ".", "/", "~", and
423 ** ":" are also encoded using modified-UTF8, making folder names that contain
424 ** those characters incompatible with IMAP.
425 **
426 ** smap_to_utf8 crates a modified-UTF8-encoded folder name from a vector
427 ** of UTF-8 words.
428 **
429 ** input:  "INBOX" "a" "b"
430 ** output: "INBOX.a.b"
431 **
432 */
433 
smap_to_utf8(char ** ptr)434 static char *smap_to_utf8(char **ptr)
435 {
436 	char *f=NULL;
437 	char *n;
438 
439 	while ((n=*ptr++) != NULL && *n)
440 	{
441 		char *p=unicode_convert_tobuf(n, "utf-8",
442 					      unicode_x_smap_modutf8,
443 					      NULL);
444 
445 		if (!p)
446 		{
447 			if (f)
448 				free(f);
449 			return NULL;
450 		}
451 
452 		n= f ? realloc(f, strlen(f)+strlen(p)+2):malloc(strlen(p)+1);
453 
454 		if (!n)
455 		{
456 			free(p);
457 			if (f)
458 				free(f);
459 			return NULL;
460 		}
461 		if (f)
462 			f=strcat(strcat(n, "."), p);
463 		else
464 			f=strcpy(n, p);
465 		free(p);
466 	}
467 
468 	if (!f)
469 		errno=EINVAL;
470 	return f;
471 }
472 
473 /*
474 ** Convert modified-UTF8 folder name into an array of UTF-8 words, that
475 ** represent a folder name.
476 */
477 
maildir_smapfn_fromutf8(const char * modutf8)478 char **maildir_smapfn_fromutf8(const char *modutf8)
479 {
480 	char *p=strdup(modutf8), *q;
481 	size_t n, i;
482 	char **fn;
483 
484 	if (!p)
485 		return NULL;
486 
487 	n=1;
488 	for (i=0; p[i]; i++)
489 		if (p[i] == '.' && p[i+1] && p[i+1] != '.')
490 		{
491 			++n;
492 		}
493 
494 	fn=malloc((n+1)*sizeof(char *));
495 
496 	if (!fn)
497 	{
498 		free(p);
499 		return NULL;
500 	}
501 
502 	n=0;
503 	q=p;
504 	do
505 	{
506 		for (i=0; q[i]; i++)
507 			if (q[i] == '.' && q[i+1] && q[i+1] != '.')
508 			{
509 				q[i++]=0;
510 				break;
511 			}
512 
513 		fn[n]=unicode_convert_tobuf(q,
514 					    unicode_x_smap_modutf8,
515 					    "utf-8", NULL);
516 		q += i;
517 
518 		if (!fn[n])
519 		{
520 			while (n)
521 				free(fn[--n]);
522 			free(fn);
523 			free(p);
524 			return NULL;
525 		}
526 		n++;
527 	} while (*q);
528 	fn[n]=0;
529 	free(p);
530 	return fn;
531 }
532 
maildir_smapfn_free(char ** fn)533 void maildir_smapfn_free(char **fn)
534 {
535 	size_t i;
536 
537 	for (i=0; fn[i]; i++)
538 		free(fn[i]);
539 	free(fn);
540 }
541 
542 struct get_existing_folder_info {
543 	char **fn;
544 	char *pathname;
545 };
546 
get_existing_callback(const char * f,void * vp)547 static void get_existing_callback(const char *f, void *vp)
548 {
549 	char **fn;
550 
551 	struct get_existing_folder_info *gefi=
552 		(struct get_existing_folder_info *)vp;
553 	size_t i;
554 	size_t j;
555 
556 	if (gefi->pathname)
557 		return;
558 
559 	fn=maildir_smapfn_fromutf8(f);
560 	if (!fn)
561 	{
562 		perror(f);
563 		return;
564 	}
565 
566 	for (i=0; gefi->fn[i]; i++)
567 		if (fn[i] == NULL || strcmp(fn[i], gefi->fn[i]))
568 		{
569 			maildir_smapfn_free(fn);
570 			return;
571 		}
572 
573 	maildir_smapfn_free(fn);
574 
575 	for (j=0; i && f[j]; j++)
576 		if (f[j] == '.' && f[j+1] && f[j+1] != '.')
577 		{
578 			--i;
579 			if (i == 0)
580 				break;
581 		}
582 
583 	gefi->pathname=malloc(j+1);
584 
585 	if (!gefi->pathname)
586 	{
587 		perror("malloc");
588 		return;
589 	}
590 
591 	memcpy(gefi->pathname, f, j);
592 	gefi->pathname[j]=0;
593 }
594 
smap_path(const char * homedir,char ** words)595 static char *smap_path(const char *homedir,
596 		       char **words)  /* words[0] better be INBOX! */
597 {
598 	struct get_existing_folder_info gefi;
599 	char *n, *p;
600 	struct stat stat_buf;
601 
602 	if ((n=smap_to_utf8(words)) == NULL)
603 		return NULL;
604 
605 	p=maildir_name2dir(homedir, n);
606 
607 	if (!p)
608 	{
609 		free(n);
610 		return NULL;
611 	}
612 
613 	if (stat(p, &stat_buf) == 0)
614 	{
615 		free(p);
616 		return n;
617 	}
618 
619 	gefi.fn=words;
620 	gefi.pathname=NULL;
621 
622 	maildir_list(homedir ? homedir:".",
623 		     &get_existing_callback, &gefi);
624 
625 	if (gefi.pathname)
626 	{
627 		free(n);
628 		free(p);
629 
630 		return gefi.pathname;
631 	}
632 
633 	free(p);
634 	return n;
635 }
636 
maildir_info_smap_find(struct maildir_info * info,char ** folder,const char * myId)637 int maildir_info_smap_find(struct maildir_info *info, char **folder,
638 			   const char *myId)
639 {
640 	char *p;
641 	size_t n;
642 	const char *indexfile;
643 	struct maildir_shindex_cache *curcache;
644 	const char *subhierarchy;
645 	struct imap_find_shared ifs;
646 	int rc, eof;
647 	char *indexfile_cpy=NULL;
648 
649 	info->homedir=NULL;
650 	info->maildir=NULL;
651 	info->owner=NULL;
652 	info->mailbox_type=MAILBOXTYPE_IGNORE;
653 
654 	if (folder[0] == NULL)
655 	{
656 		errno=EINVAL;
657 		return -1;
658 	}
659 
660 	if (strcmp(folder[0], PUBLIC))
661 	{
662 		if (strcmp(folder[0], INBOX))
663 		{
664 			errno=EINVAL;
665 			return -1;
666 		}
667 
668 		info->maildir=smap_path(NULL, folder);
669 
670 		if (info->maildir == NULL)
671 			return -1;
672 		info->homedir=strdup(".");
673 		if (!info->homedir)
674 		{
675 			maildir_info_destroy(info);
676 			return -1;
677 		}
678 
679 		info->mailbox_type=MAILBOXTYPE_INBOX;
680 
681 		info->owner=malloc(strlen(myId)+sizeof("user="));
682 
683 		if (!info->owner)
684 		{
685 			maildir_info_destroy(info);
686 			return -1;
687 		}
688 
689 		strcat(strcpy(info->owner, "user="), myId);
690 
691 		return 0;
692 	}
693 
694 	indexfile=NULL;
695 	curcache=NULL;
696 	subhierarchy=NULL;
697 	n=1;
698 	ifs.homedir=NULL;
699 	ifs.maildir=NULL;
700 
701 	while (folder[n])
702 	{
703 		size_t i;
704 
705 		curcache=maildir_shared_cache_read(curcache, indexfile,
706 						   subhierarchy);
707 
708 		if (!curcache)
709 			break;
710 
711 		for (i=0; i<curcache->nrecords; i++)
712 			if (strcmp(curcache->records[i].name,
713 				   folder[n]) == 0)
714 				break;
715 
716 		if (i >= curcache->nrecords)
717 			break;
718 		curcache->indexfile.startingpos=
719 			curcache->records[i].offset;
720 
721 		if (ifs.homedir)
722 			free(ifs.homedir);
723 		if (ifs.maildir)
724 			free(ifs.maildir);
725 		ifs.homedir=NULL;
726 		ifs.maildir=NULL;
727 
728 		rc=maildir_newshared_nextAt(&curcache->indexfile,
729 					    &eof,
730 					    imap_find_cb, &ifs);
731 
732 		if (rc || eof)
733 		{
734 			fprintf(stderr, "ERR: Internal error -"
735 				" maildir_newshared_nextAt: %s\n",
736 				strerror(errno));
737 			fflush(stderr);
738 			break;
739 		}
740 
741 		if (!ifs.homedir && !ifs.maildir)
742 			break;
743 
744 		if (!ifs.homedir)
745 		{
746 			if (indexfile_cpy)
747 				free(indexfile_cpy);
748 			indexfile=indexfile_cpy=ifs.maildir;
749 			ifs.maildir=NULL;
750 			subhierarchy=curcache->records[i].name;
751 			++n;
752 			continue;
753 		}
754 
755 		if (indexfile_cpy)
756 			free(indexfile_cpy);
757 		info->homedir=maildir_location(ifs.homedir,
758 					       ifs.maildir);
759 		free(ifs.homedir);
760 		free(ifs.maildir);
761 
762 		info->maildir=NULL;
763 
764 		if (maildir_info_suppress(info->homedir))
765 		{
766 
767 			free(info->homedir);
768 			info->homedir=NULL;
769 			info->maildir=NULL;
770 			info->mailbox_type=MAILBOXTYPE_IGNORE;
771 			info->owner=NULL;
772 			info->owner=strdup("vendor=courier.internal");
773 			if (!info->owner)
774 			{
775 				maildir_info_destroy(info);
776 				return -1;
777 			}
778 
779 			return 0;
780 		}
781 
782 
783 		if (!subhierarchy || !*subhierarchy)
784 		{
785 			info->owner=strdup("vendor=courier.internal");
786 			if (!info->owner)
787 			{
788 				maildir_info_destroy(info);
789 				return -1;
790 			}
791 		}
792 		else
793 		{
794 			info->owner=malloc(strlen(subhierarchy)
795 					   +sizeof("user="));
796 
797 			if (!info->owner)
798 			{
799 				free(info->homedir);
800 				info->homedir=NULL;
801 				info->maildir=NULL;
802 				return -1;
803 			}
804 			strcpy(info->owner, "user=");
805 			strcat(info->owner, subhierarchy);
806 		}
807 
808 		p=folder[n];
809 		folder[n]=INBOX;
810 		info->maildir=smap_path(info->homedir, folder+n);
811 		folder[n]=p;
812 
813 		if (!info->maildir)
814 		{
815 			free(info->homedir);
816 			free(info->owner);
817 			info->homedir=NULL;
818 			info->maildir=NULL;
819 			info->owner=NULL;
820 			return -1;
821 		}
822 
823 		info->mailbox_type=MAILBOXTYPE_NEWSHARED;
824 		return 0;
825 	}
826 
827 	if (ifs.homedir)
828 		free(ifs.homedir);
829 	if (ifs.maildir)
830 		free(ifs.maildir);
831 	if (indexfile_cpy)
832 		free(indexfile_cpy);
833 
834 	if (folder[n] == 0)
835 	{
836 		info->mailbox_type=MAILBOXTYPE_NEWSHARED;
837 		info->owner=strdup("vendor=courier.internal");
838 		if (!info->owner)
839 		{
840 			maildir_info_destroy(info);
841 			return -1;
842 		}
843 
844 		/* Intermediate shared namespce */
845 		return 0;
846 	}
847 
848 	return -1;
849 }
850 
851 static int complex_flag;
852 
maildir_info_munge_complex(int f)853 void maildir_info_munge_complex(int f)
854 {
855 	complex_flag=f;
856 }
857 
858 static size_t munge_complex(const char *, char *);
859 
maildir_info_imapmunge(const char * name)860 char *maildir_info_imapmunge(const char *name)
861 {
862 	char *n=unicode_convert_tobuf(name, "utf-8",
863 					unicode_x_imap_modutf7, NULL);
864 	char *p;
865 	size_t cnt;
866 
867 	if (!n)
868 		return NULL;
869 
870 	if (!complex_flag)
871 	{
872 		for (p=n; *p; p++)
873 		{
874 			if (*p == '.' || *p == '/')
875 				*p=' ';
876 		}
877 
878 		return n;
879 	}
880 
881 	cnt=munge_complex(n, NULL);
882 	p=malloc(cnt);
883 	if (!p)
884 	{
885 		free(n);
886 		return NULL;
887 	}
888 
889 	munge_complex(n, p);
890 
891 	free(n);
892 	return p;
893 }
894 
munge_complex(const char * orig,char * n)895 static size_t munge_complex(const char *orig, char *n)
896 {
897 	size_t cnt=0;
898 
899 	while (*orig)
900 	{
901 		switch (*orig) {
902 		case '.':
903 			if (n)
904 			{
905 				*n++='\\';
906 				*n++=':';
907 			}
908 			cnt += 2;
909 			break;
910 		case '/':
911 			if (n)
912 			{
913 				*n++='\\';
914 				*n++=';';
915 			}
916 			cnt += 2;
917 			break;
918 		case '\\':
919 			if (n)
920 			{
921 				*n++='\\';
922 				*n++='\\';
923 			}
924 			cnt += 2;
925 			break;
926 		default:
927 			if (n) *n++ = *orig;
928 			++cnt;
929 		}
930 		++orig;
931 	}
932 
933 	if (n) *n=0;
934 	return cnt+1;
935 }
936