ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/gclib/tophat_cpp/GBase.cpp
Revision: 154
Committed: Tue Jan 24 02:29:21 2012 UTC (7 years, 9 months ago) by gpertea
File size: 18719 byte(s)
Log Message:
massive update with Daehwan's work

Line File contents
1 #include "GBase.h"
2 #include <stdarg.h>
3 #include <ctype.h>
4 #include <sys/stat.h>
5
6 #ifndef S_ISDIR
7 #define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
8 #endif
9
10 #ifndef S_ISREG
11 #define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
12 #endif
13
14 static char msg[4069];
15 /*
16 #ifdef _DEFINE_WIN32_FSEEKO
17 int fseeko(FILE *stream, off_t offset, int whence) {
18
19 }
20 #endif
21
22 #ifdef _DEFINE_WIN32_FTELLO
23 off_t ftello(FILE *stream) {
24
25 }
26 #endif
27 */
28
29
30 int saprintf(char **retp, const char *fmt, ...) {
31 va_list argp;
32 int len;
33 char *buf;
34
35 va_start(argp, fmt);
36 len = vsnprintf(NULL, 0, fmt, argp);
37 va_end(argp);
38 GMALLOC(buf, (len + 1));
39 if(buf == NULL)
40 {
41 *retp = NULL;
42 return -1;
43 }
44
45 va_start(argp, fmt);
46 vsnprintf(buf, len+1, fmt, argp);
47 va_end(argp);
48
49 *retp = buf;
50 return len;
51 }
52
53 //************************* Debug helpers **************************
54 // Assert failed routine
55 void GAssert(const char* expression, const char* filename, unsigned int lineno){
56 sprintf(msg,"%s(%d): ASSERT(%s) failed.\n",filename,lineno,expression);
57 fprintf(stderr,"%s",msg);
58 //abort();
59 }
60 // Error routine (prints error message and exits!)
61 void GError(const char* format,...){
62 #ifdef __WIN32__
63 va_list arguments;
64 va_start(arguments,format);
65 vsprintf(msg,format,arguments);
66 va_end(arguments);
67 OutputDebugString(msg);
68 fprintf(stderr,"%s",msg); // if a console is available
69 MessageBox(NULL,msg,NULL,MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL);
70 #else
71 va_list arguments;
72 va_start(arguments,format);
73 vfprintf(stderr,format,arguments);
74 va_end(arguments);
75 #ifdef DEBUG
76 // modify here if you want a core dump
77 abort();
78 #endif
79 #endif
80 exit(1);
81 }
82
83 // Warning routine (just print message without exiting)
84 void GMessage(const char* format,...){
85 va_list arguments;
86 va_start(arguments,format);
87 vsprintf(msg,format,arguments);
88 va_end(arguments);
89 #ifdef __WIN32__
90 OutputDebugString(msg);
91 #endif
92 fprintf(stderr,"%s",msg);fflush(stderr);
93 }
94
95 /*************** Memory management routines *****************/
96 // Allocate memory
97 bool GMalloc(pointer* ptr,unsigned long size){
98 //GASSERT(ptr);
99 if (size!=0) *ptr=malloc(size);
100 return *ptr!=NULL;
101 }
102
103 // Allocate cleaned memory (0 filled)
104 bool GCalloc(pointer* ptr,unsigned long size){
105 GASSERT(ptr);
106 *ptr=calloc(size,1);
107 return *ptr!=NULL;
108 }
109
110 // Resize memory
111 bool GRealloc(pointer* ptr,unsigned long size){
112 //GASSERT(ptr);
113 if (size==0) {
114 GFree(ptr);
115 return true;
116 }
117 if (*ptr==NULL) {//simple malloc
118 void *p=malloc(size);
119 if (p != NULL) {
120 *ptr=p;
121 return true;
122 }
123 else return false;
124 }//malloc
125 else {//realloc
126 void *p=realloc(*ptr,size);
127 if (p) {
128 *ptr=p;
129 return true;
130 }
131 return false;
132 }
133 }
134 // Free memory, resets ptr to NULL afterward
135 void GFree(pointer* ptr){
136 GASSERT(ptr);
137 if (*ptr) free(*ptr);
138 *ptr=NULL;
139 }
140
141 char* Gstrdup(const char* str) {
142 if (str==NULL) return NULL;
143 char *copy=NULL;
144 GMALLOC(copy, strlen(str)+1);
145 strcpy(copy,str);
146 return copy;
147 }
148
149 char* newEmptyStr() {
150 char* zs=NULL;
151 GMALLOC(zs,1);
152 zs[0]=0;
153 return zs;
154 }
155
156 char* Gstrdup(const char* sfrom, const char* sto) {
157 if (sfrom==NULL || sto==NULL) return NULL;
158 char *copy=NULL;
159 if (sfrom[0]==0) return newEmptyStr();
160 GMALLOC(copy, sto-sfrom+2);
161 strncpy(copy, sfrom, sto-sfrom+1);
162 copy[sto-sfrom+1]=0;
163 return copy;
164 }
165
166 int Gstrcmp(const char* a, const char* b, int n) {
167 if (a==NULL || b==NULL) {
168 return a==NULL ? -1 : 1;
169 }
170 else {
171 if (n<0) return strcmp(a,b);
172 else return strncmp(a,b,n);
173 }
174
175 }
176
177 int Gstricmp(const char* a, const char* b, int n) {
178 if (a==NULL || b==NULL) return a==NULL ? -1 : 1;
179 register int ua, ub;
180 if (n<0) {
181 while ((*a!=0) && (*b!=0)) {
182 ua=tolower((unsigned char)*a);
183 ub=tolower((unsigned char)*b);
184 a++;b++;
185 if (ua!=ub) return ua < ub ? -1 : 1;
186 }
187 return (*a == 0) ? ( (*b == 0) ? 0 : -1 ) : 1 ;
188 }
189 else {
190 while (n && (*a!=0) && (*b!=0)) {
191 ua=tolower((unsigned char)*a);
192 ub=tolower((unsigned char)*b);
193 a++;b++;n--;
194 if (ua!=ub) return ua < ub ? -1 : 1;
195 }
196 //return (*a == 0) ? ( (*b == 0) ? 0 : -1 ) : 1 ;
197 if (n==0) return 0;
198 else { return (*a == 0) ? ( (*b == 0) ? 0 : -1 ) : 1 ; }
199 }
200 }
201
202 int strsplit(char* str, char** fields, int maxfields, const char* delim) {
203 //splits by placing 0 where delim chars are found, setting fields[] to the beginning
204 //of each field (stopping after maxfields); returns number of fields parsed
205 int tidx=0;
206 bool afterdelim=true;
207 int i=0;
208 while (str[i]!=0 && tidx<maxfields) {
209 if (afterdelim) {
210 fields[tidx]=str+i;
211 tidx++;
212 }
213 afterdelim=false;
214 if (chrInStr(str[i],(char*)delim)) {
215 str[i]=0;
216 i++;
217 while (str[i]!=0 && chrInStr(str[i], (char*)delim)) i++;
218 afterdelim=true;
219 continue;
220 }
221 i++;
222 }
223 return tidx;
224 }
225
226 int strsplit(char* str, char** fields, int maxfields, const char delim) {
227 //splits by placing 0 where delim is found, setting fields[] to the beginning
228 //of each field (stopping after maxfields); returns number of fields parsed
229 int tidx=0;
230 bool afterdelim=true;
231 int i=0;
232 while (str[i]!=0 && tidx<maxfields) {
233 if (afterdelim) {
234 fields[tidx]=str+i;
235 tidx++;
236 }
237 afterdelim=false;
238 if (str[i]==delim) {
239 str[i]=0;
240 i++;
241 while (str[i]!=0 && str[i]==delim) i++;
242 afterdelim=true;
243 continue;
244 }
245 i++;
246 }
247 return tidx;
248 }
249
250 int strsplit(char* str, char** fields, int maxfields) {
251 //splits by placing 0 where delim is found, setting fields[] to the beginning
252 //of each field (stopping after maxfields); returns number of fields parsed
253 int tidx=0;
254 bool afterdelim=true;
255 int i=0;
256 while (str[i]!=0 && tidx<maxfields) {
257 if (afterdelim) {
258 fields[tidx]=str+i;
259 tidx++;
260 }
261 afterdelim=false;
262 if (str[i]==' ' || str[i]=='\t') {
263 str[i]=0;
264 i++;
265 while (str[i]!=0 && (str[i]=='\t' || str[i]==' ')) i++;
266 afterdelim=true;
267 continue;
268 }
269 i++;
270 }
271 return tidx;
272 }
273
274
275 char* Gsubstr(const char* str, char* from, char* to) {
276 //extract (and allocate) a substring, including boundaries (from/to)
277 if (str==NULL || from==NULL) return NULL;
278 if (from[0]==0 || str[0]==0) return newEmptyStr();
279 if (from<str) return NULL;
280 if (to==NULL) {
281 to=from;
282 while (to[1]) to++;
283 }
284 if (to<from) return newEmptyStr();
285 int newlen=to-from+1;
286 char* subs;
287 GMALLOC(subs, newlen);
288 memcpy(subs, str, newlen-1);
289 subs[newlen]='\0';
290 return subs;
291 }
292
293 char* replaceStr(char* &str, char* newvalue) {
294 if (str!=NULL) GFREE(str);
295 if (newvalue==NULL) { return NULL; }
296 GMALLOC(str, strlen(newvalue)+1);
297 strcpy(str,newvalue);
298 return str;
299 }
300
301 void* Gmemscan(void *mem, unsigned int len,
302 void *part, unsigned int partlen) {
303 char* p;
304 unsigned int restlen=len-partlen+1;
305 void* oldp=mem;
306 while ( (p=(char*)memchr(oldp, ((char*)part)[0], restlen))!=NULL) {
307 //located first char, try to match the rest:
308 p++;
309 if (memcmp(p, &((char*)part)[1], partlen-1)==0) return p-1;
310 //no string match, prepare next iteration
311 restlen-=(p-(char*)oldp);
312 oldp=p;
313 }//while
314 return NULL;
315 }
316
317 //rindex function is missing on some platforms ?
318 char* rstrchr(char* str, char ch) { /* returns a pointer to the rightmost
319 occurence of ch in str */
320 char *p;
321 if (str==NULL) return NULL;
322 p=str+strlen(str)-1;
323 while (p>=str) {
324 if (*p==ch) return p;
325 p--;
326 }
327 return NULL;
328 }
329
330
331 /* DOS/UNIX safer fgets : reads a text line from a (binary) file and
332 update the file position accordingly and the buffer capacity accordingly.
333 The given buf is resized to read the entire line in memory
334 -- even when it's abnormally long
335 */
336 char* fgetline(char* & buf, int& buf_cap, FILE *stream, off_t* f_pos, int* linelen) {
337 //reads a char at a time until \n and/or \r are encountered
338 int i=0;
339 int c=0;
340 off_t fpos=(f_pos!=NULL) ? *f_pos : 0;
341 while ((c=getc(stream))!=EOF) {
342 if (i>=buf_cap-1) {
343 buf_cap+=1024;
344 GREALLOC(buf, buf_cap);
345 }
346 if (c=='\n' || c=='\r') {
347 if (c=='\r') {
348 if ((c=getc(stream))!='\n') ungetc(c,stream);
349 else fpos++;
350 }
351 fpos++;
352 break;
353 }
354 fpos++;
355 buf[i]=(char)c;
356 i++;
357 } //while i<buf_cap-1
358 if (linelen!=NULL) *linelen=i;
359 if (f_pos!=NULL) *f_pos=fpos;
360 if (c==EOF && i==0) return NULL;
361 buf[i]='\0';
362 return buf;
363 }
364
365 char* GLineReader::getLine(FILE* stream, off_t& f_pos) {
366 if (pushed) { pushed=false; return buf; }
367 //reads a char at a time until \n and/or \r are encountered
368 len=0;
369 int c=0;
370 while ((c=getc(stream))!=EOF) {
371 if (len>=allocated-1) {
372 allocated+=1024;
373 GREALLOC(buf, allocated);
374 }
375 if (c=='\n' || c=='\r') {
376 buf[len]='\0';
377 if (c=='\r') { //DOS file -- special case
378 if ((c=getc(stream))!='\n') ungetc(c,stream);
379 else f_pos++;
380 }
381 f_pos++;
382 lcount++;
383 return buf;
384 }
385 f_pos++;
386 buf[len]=(char)c;
387 len++;
388 } //while i<buf_cap-1
389 if (c==EOF) {
390 isEOF=true;
391 if (len==0) return NULL;
392 }
393 buf[len]='\0';
394 lcount++;
395 return buf;
396 }
397
398
399 //strchr but with a set of chars instead of only one
400 char* strchrs(const char* s, const char* chrs) {
401 if (s==NULL || chrs==NULL || *chrs=='\0' || *s=='\0')
402 return NULL;
403 unsigned int l=strlen(s);
404 unsigned int r=strcspn(s, chrs);
405 if (r==l) return NULL;
406 return ((char*)s+r);
407 }
408
409 char* upCase(const char* str) {
410 if (str==NULL) return NULL;
411 int len=strlen(str);
412 char* upstr;
413 GMALLOC(upstr, len+1);
414 upstr[len]='\0';
415 for (int i=0;i<len;i++) upstr[i]=toupper(str[i]);
416 return upstr;
417 }
418
419 char* loCase(const char* str) {
420 if (str==NULL) return NULL;
421 int len=strlen(str);
422 char* lostr;
423 GMALLOC(lostr, len+1);
424 lostr[len]='\0';
425 for (int i=0;i<len;i++) lostr[i]=tolower(str[i]);
426 return lostr;
427 }
428
429 char* strlower(char * str) {//changes string in place
430 if (str==NULL) return NULL;
431 int i=0;
432 while (str[i]!=0) { str[i]=tolower(str[i]); i++; }
433 return str;
434 }
435
436 char* strupper(char * str) {//changes string in place
437 if (str==NULL) return NULL;
438 int i=0;
439 while (str[i]!=0) { str[i]=toupper(str[i]); i++; }
440 return str;
441 }
442
443
444
445 //test if a char is in a given string (set)
446 bool chrInStr(char c, const char* str) {
447 if (str==NULL || *str=='\0') return false;
448 for (const char* p=str; (*p)!='\0'; p++) {
449 if ((*p)==c) return true;
450 }
451 return false;
452 }
453
454
455
456 char* rstrfind(const char* str, const char* substr) {
457 /* like rindex() for a string */
458 int l,i;
459 if (str==NULL || *str=='\0') return NULL;
460 if (substr==NULL || *substr=='\0') return NULL;
461 l=strlen(substr);
462 char* p=(char*)str+strlen(str)-l;
463 //rightmost position that could match
464
465 while (p>=str) {
466 for (i=0; i<l && *(p+i) == *(substr+i); i++) ;
467 if (i==l) return p; //found!
468 p--;
469 }
470 return NULL;
471 }
472
473
474 char* strifind(const char* str, const char* substr) {
475 // the case insensitive version of strstr -- finding a string within a strin
476 int l,i;
477 if (str==NULL || *str==0) return NULL;
478 if (substr==NULL || *substr==0) return NULL;
479 l=strlen(substr);
480 char* smax=(char*)str+strlen(str)-l;
481 //rightmost position that could match
482 char* p=(char*)str;
483 while (p<=smax) {
484 for (i=0; i<l && tolower(*(p+i))==tolower(*(substr+i)); i++) ;
485 if (i==l) return p; //found!
486 p++;
487 }
488 return NULL;
489 }
490
491
492
493 // tests if string s has the given prefix
494 bool startsWith(const char* s, const char* prefix) {
495 if (prefix==NULL || s==NULL) return false;
496 int i=0;
497 while (prefix[i]!='\0' && prefix[i]==s[i]) i++;
498 return (prefix[i]=='\0');
499 }
500
501 // tests if string s ends with given suffix
502 bool endsWith(const char* s, const char* suffix) {
503 if (suffix==NULL || s==NULL) return false;
504 if (suffix[0]==0) return true; //special case: empty suffix
505 int j=strlen(suffix)-1;
506 int i=strlen(s)-1;
507 if (i<j) return false;
508 while (j>=0 && s[i]==suffix[j]) { i--; j--; }
509 return (j==-1);
510 }
511
512
513 char* reverseChars(char* str, int slen) {
514 if (slen==0) slen=strlen(str);
515 int l=0;
516 int r=slen-1;
517 char c;
518 while (l<r) {
519 c=str[l];str[l]=str[r];
520 str[r]=c;
521 l++;r--;
522 }
523 return str;
524 }
525
526
527 char* rstrstr(const char* rstart, const char *lend, const char* substr) { /*like strstr, but starts searching
528 from right end, going up to lend and returns a pointer to the last (right)
529 matching character in str */
530 char *p;
531 int l,i;
532 l=strlen(substr);
533 p=(char*)rstart-l+1;
534 while (p>=lend) {
535 for (i=0;i<l;i++) if (*(p+i) != *(substr+i)) break;
536 if (i==l) return p+l-1;
537 p--;
538 }
539 return NULL;
540 }
541
542
543 //hash function used for strings in GHash
544 int strhash(const char* str){
545 register int h=0;
546 register int g;
547 while (*str) {
548 h=(h<<4)+*str++;
549 g=h&0xF0000000;
550 if(g) h^=g>>24;
551 h&=0x0fffffff;
552 }
553 GASSERT(h<=0x0fffffff);
554 return h;
555 }
556
557 // removes the last part (file or directory name) of a full path
558 // this is a destructive operation for the given string!!!
559 // the trailing '/' is guaranteed to be there
560 void delFileName(char* filepath) {
561 char *p, *sep;
562 if (filepath==NULL) return;
563 for (p=filepath, sep=filepath;*p!='\0';p++)
564 if (*p=='/' || *p=='\\') sep=p+1;
565 *sep='\0'; // truncate filepath
566 }
567
568 // returns a pointer to the last file or directory name in a full path
569 const char* getFileName(const char* filepath) {
570 const char *p, *sep;
571 if (filepath==NULL) return NULL;
572 for (p=filepath, sep=filepath;*p!='\0';p++)
573 if (*p=='/' || *p=='\\') sep=p+1;
574 return sep;
575 }
576
577 // returns a pointer to the file "extension" part in a filename
578 const char* getFileExt(const char* filepath) {
579 const char *p, *dp, *sep;
580 if (filepath==NULL) return NULL;
581 for (p=filepath, dp=filepath, sep=filepath;*p!='\0';p++) {
582 if (*p=='.') dp=p+1;
583 else if (*p=='/' || *p=='\\')
584 sep=p+1;
585 }
586 return (dp>sep) ? dp : NULL ;
587 }
588
589 int fileExists(const char* fname) {
590 struct stat stFileInfo;
591 int r=0;
592 // Attempt to get the file attributes
593 int fs = stat(fname,&stFileInfo);
594 if (fs == 0) {
595 r=3;
596 // We were able to get the file attributes
597 // so the file obviously exists.
598 if (S_ISREG (stFileInfo.st_mode)) {
599 r=2;
600 }
601 if (S_ISDIR (stFileInfo.st_mode)) {
602 r=1;
603 }
604 }
605 return r;
606 }
607
608 /*bool fileExists(const char* filepath) {
609 if (filepath==NULL) return false;
610 FILE* ft=fopen(filepath, "rb");
611 if (ft==NULL) return false;
612 fclose(ft);
613 return true;
614 }
615 */
616 int64 fileSize(const char* fpath) {
617 struct stat results;
618 if (stat(fpath, &results) == 0)
619 // The size of the file in bytes is in
620 return (int64)results.st_size;
621 else
622 // An error occurred
623 //GMessage("Error at stat(%s)!\n", fpath);
624 return 0;
625 }
626
627 bool parseNumber(char* &p, double& v) {
628 //skip any spaces..
629 while (*p==' ' || *p=='\t') p++;
630 char* start=p;
631 /*if (*p=='-') p++;
632 else if (*p=='+') { p++;start++; }*/
633
634 /* while ((*p>='1' && *p<='9') || *p=='0' ||
635 *p=='.' || *p=='-' || tolower(*p)=='e') p++; */
636 int numlen=strspn(start, "0123456789eE.-+");
637 p=start+numlen;
638 //now p is on a non-digit;
639 if (*start=='-' && p==start+1) return false;
640 char saved=*p;
641 *p='\0';
642 char* endptr=p;
643 v=strtod(start,&endptr);
644 *p=saved;
645 if (endptr!=p) return false;
646 return true;
647 }
648
649
650 bool parseDouble(char* &p, double& v) {
651 return parseNumber(p,v);
652 }
653
654 bool parseInt(char* &p, int& i) {
655 while (*p==' ' || *p=='\t') p++;
656 char* start=p;
657 if (*p=='-') p++;
658 else if (*p=='+') { p++;start++; }
659 while ((*p>='1' && *p<='9') || *p=='0') p++;
660 //now p is on a non-digit;
661 if (*start=='-' && p==start+1) return false;
662 char saved=*p;
663 *p='\0';
664 char* endptr=p;
665 long l=strtol(start,&endptr,10);
666 i=(int)l;
667 *p=saved;
668 if (endptr!=p || i!=l) return false;
669 return true;
670 }
671
672 bool parseUInt(char* &p, uint& i) {
673 while (*p==' ' || *p=='\t') p++;
674 char* start=p;
675 if (*p=='-') return false;
676 else if (*p=='+') { p++;start++; }
677 while ((*p>='1' && *p<='9') || *p=='0') p++;
678 //now p is on a non-digit;
679 if (*start=='-' && p==start+1) return false;
680 char saved=*p;
681 *p='\0';
682 char* endptr=p;
683 unsigned long l=strtoul(start,&endptr,10);
684 i=(uint) l;
685 *p=saved;
686 if (endptr!=p || i!=l) return false;
687 return true;
688 }
689
690 bool parseHex(char* &p, uint& i) {
691 //skip initial spaces/prefix
692 while (*p==' ' || *p=='\t' || *p=='0' || *p=='x') p++;
693 char* start=p;
694 if (*p=='-') return false;
695 else if (*p=='+') { p++;start++; }
696 while (isxdigit(*p)) p++;
697 //now p is on a non-hexdigit;
698 if (p==start+1) return false;
699 char saved=*p;
700 *p='\0';
701 char* endptr=p;
702 unsigned long l=strtoul(start,&endptr,16);
703 i=(uint) l;
704 *p=saved;
705 if (endptr!=p || i!=l) return false;
706 return true;
707 }
708
709 //write a formatted fasta record, fasta formatted
710 void writeFasta(FILE *fw, const char* seqid, const char* descr,
711 const char* seq, int linelen, int seqlen) {
712 fflush(fw);
713 // write header line only if given!
714 if (seqid!=NULL) {
715 if (descr==NULL || descr[0]==0)
716 fprintf(fw,">%s\n",seqid);
717 else fprintf(fw,">%s %s\n",seqid, descr);
718 }
719 fflush(fw);
720 if (seq==NULL || *seq==0) return; //nothing to print
721 if (linelen==0) { //unlimited line length: write the whole sequence on a line
722 if (seqlen>0)
723 fwrite((const void*)seq, 1, seqlen,fw);
724 else fprintf(fw,"%s",seq);
725 fprintf(fw,"\n");
726 fflush(fw);
727 return;
728 }
729 int ilen=0;
730 if (seqlen>0) { //seq length given, so we know when to stop
731 for (int i=0; i < seqlen; i++, ilen++) {
732 if (ilen == linelen) {
733 fputc('\n', fw);
734 ilen = 0;
735 }
736 fputc(seq[i], fw);
737 }
738 fputc('\n', fw);
739 }
740 else { //seq length not given, stop when 0 encountered
741 for (int i=0; seq[i]!=0; i++, ilen++) {
742 if (ilen == linelen) {
743 fputc('\n', fw);
744 ilen = 0;
745 }
746 fputc(seq[i], fw);
747 } //for
748 fputc('\n', fw);
749 }
750 fflush(fw);
751 }
752
753 char* commaprint(uint64 n) {
754 static int comma = '\0';
755 static char retbuf[48];
756 char *p = &retbuf[sizeof(retbuf)-1];
757 int i = 0;
758 if(comma == '\0') {
759 /* struct lconv *lcp = localeconv();
760 if(lcp != NULL) {
761 if(lcp->thousands_sep != NULL &&
762 *lcp->thousands_sep != '\0')
763 comma = *lcp->thousands_sep;
764 else */
765 comma = ',';
766 // }
767 }
768 *p = '\0';
769 do {
770 if(i%3 == 0 && i != 0)
771 *--p = comma;
772 *--p = '0' + n % 10;
773 n /= 10;
774 i++;
775 } while(n != 0);
776 return p;
777 }