1 /* $Header: util.c,v 3.0.1.6 90/08/09 05:44:55 lwall Locked $
3 * Copyright (c) 1989, Larry Wall
5 * You may distribute under the terms of the GNU General Public License
6 * as specified in the README file that comes with the perl 3.0 kit.
9 * Revision 3.0.1.6 90/08/09 05:44:55 lwall
10 * patch19: fixed double include of <signal.h>
11 * patch19: various MSDOS and OS/2 patches folded in
12 * patch19: open(STDOUT,"|command") left wrong descriptor attached to STDOUT
14 * Revision 3.0.1.5 90/03/27 16:35:13 lwall
15 * patch16: MSDOS support
16 * patch16: support for machines that can't cast negative floats to unsigned ints
17 * patch16: tail anchored pattern could dump if string to search was shorter
19 * Revision 3.0.1.4 90/03/01 10:26:48 lwall
20 * patch9: fbminstr() called instr() rather than ninstr()
21 * patch9: nested evals clobbered their longjmp environment
22 * patch9: piped opens returned undefined rather than 0 in child
23 * patch9: the x operator is now up to 10 times faster
25 * Revision 3.0.1.3 89/12/21 20:27:41 lwall
26 * patch7: errno may now be a macro with an lvalue
28 * Revision 3.0.1.2 89/11/17 15:46:35 lwall
29 * patch5: BZERO separate from BCOPY now
30 * patch5: byteorder now is a hex value
32 * Revision 3.0.1.1 89/11/11 05:06:13 lwall
33 * patch2: made dup2 a little better
35 * Revision 3.0 89/10/18 15:32:43 lwall
57 static char nomem[] = "Out of memory!\n";
59 /* paranoid version of malloc */
65 /* NOTE: Do not call the next three routines directly. Use the macros
66 * in handy.h, so that we can easily redefine everything to do tracking of
67 * allocated hunks back to the original New to track down any memory leaks.
83 fprintf(stderr, "Allocation too large: %lx\n", size) FLUSH;
87 ptr = malloc(size?size:1); /* malloc(0) is NASTY on our system */
91 fprintf(stderr,"0x%x: (%05d) malloc %d bytes\n",ptr,an++,size);
94 fprintf(stderr,"0x%lx: (%05d) malloc %d bytes\n",ptr,an++,size);
100 fputs(nomem,stdout) FLUSH;
109 /* paranoid version of realloc */
112 saferealloc(where,size)
125 fprintf(stderr, "Reallocation too large: %lx\n", size) FLUSH;
130 fatal("Null realloc");
131 ptr = realloc(where,size?size:1); /* realloc(0) is NASTY on our system */
135 fprintf(stderr,"0x%x: (%05d) rfree\n",where,an++);
136 fprintf(stderr,"0x%x: (%05d) realloc %d bytes\n",ptr,an++,size);
140 fprintf(stderr,"0x%lx: (%05d) rfree\n",where,an++);
141 fprintf(stderr,"0x%lx: (%05d) realloc %d bytes\n",ptr,an++,size);
148 fputs(nomem,stdout) FLUSH;
157 /* safe version of free */
166 fprintf(stderr,"0x%x: (%05d) free\n",where,an++);
169 fprintf(stderr,"0x%lx: (%05d) free\n",where,an++);
179 #define ALIGN sizeof(long)
186 register char *where;
188 where = safemalloc(size + ALIGN);
192 return where + ALIGN;
196 safexrealloc(where,size)
200 return saferealloc(where - ALIGN, size + ALIGN) + ALIGN;
212 x = where[0] + 100 * where[1];
221 for (i = 0; i < MAXXCOUNT; i++) {
222 if (xcount[i] != lastxcount[i]) {
223 fprintf(stderr,"%2d %2d\t%ld\n", i / 100, i % 100, xcount[i]);
224 lastxcount[i] = xcount[i];
229 #endif /* LEAKTEST */
231 /* copy a string up to some (non-backslashed) delimiter, if any */
234 cpytill(to,from,fromend,delim,retlen)
237 register char *fromend;
243 for (; from < fromend; from++,to++) {
245 if (from[1] == delim)
247 else if (from[1] == '\\')
250 else if (*from == delim)
255 *retlen = to - origto;
259 /* return ptr to little string in big string, NULL if not found */
260 /* This routine was donated by Corey Satten. */
265 register char *little;
267 register char *s, *x;
278 for (x=big,s=little; *s; /**/ ) {
292 /* same as instr but allow embedded nulls */
295 ninstr(big, bigend, little, lend)
297 register char *bigend;
301 register char *s, *x;
302 register int first = *little;
303 register char *littleend = lend;
305 if (!first && little > littleend)
307 bigend -= littleend - little++;
308 while (big <= bigend) {
311 for (x=big,s=little; s < littleend; /**/ ) {
323 /* reverse of the above--find last substring */
326 rninstr(big, bigend, little, lend)
332 register char *bigbeg;
333 register char *s, *x;
334 register int first = *little;
335 register char *littleend = lend;
337 if (!first && little > littleend)
340 big = bigend - (littleend - little++);
341 while (big >= bigbeg) {
344 for (x=big+2,s=little; s < littleend; /**/ ) {
356 unsigned char fold[] = {
357 0, 1, 2, 3, 4, 5, 6, 7,
358 8, 9, 10, 11, 12, 13, 14, 15,
359 16, 17, 18, 19, 20, 21, 22, 23,
360 24, 25, 26, 27, 28, 29, 30, 31,
361 32, 33, 34, 35, 36, 37, 38, 39,
362 40, 41, 42, 43, 44, 45, 46, 47,
363 48, 49, 50, 51, 52, 53, 54, 55,
364 56, 57, 58, 59, 60, 61, 62, 63,
365 64, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
366 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
367 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
368 'x', 'y', 'z', 91, 92, 93, 94, 95,
369 96, 'A', 'B', 'C', 'D', 'E', 'F', 'G',
370 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
371 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
372 'X', 'Y', 'Z', 123, 124, 125, 126, 127,
373 128, 129, 130, 131, 132, 133, 134, 135,
374 136, 137, 138, 139, 140, 141, 142, 143,
375 144, 145, 146, 147, 148, 149, 150, 151,
376 152, 153, 154, 155, 156, 157, 158, 159,
377 160, 161, 162, 163, 164, 165, 166, 167,
378 168, 169, 170, 171, 172, 173, 174, 175,
379 176, 177, 178, 179, 180, 181, 182, 183,
380 184, 185, 186, 187, 188, 189, 190, 191,
381 192, 193, 194, 195, 196, 197, 198, 199,
382 200, 201, 202, 203, 204, 205, 206, 207,
383 208, 209, 210, 211, 212, 213, 214, 215,
384 216, 217, 218, 219, 220, 221, 222, 223,
385 224, 225, 226, 227, 228, 229, 230, 231,
386 232, 233, 234, 235, 236, 237, 238, 239,
387 240, 241, 242, 243, 244, 245, 246, 247,
388 248, 249, 250, 251, 252, 253, 254, 255
391 static unsigned char freq[] = {
392 1, 2, 84, 151, 154, 155, 156, 157,
393 165, 246, 250, 3, 158, 7, 18, 29,
394 40, 51, 62, 73, 85, 96, 107, 118,
395 129, 140, 147, 148, 149, 150, 152, 153,
396 255, 182, 224, 205, 174, 176, 180, 217,
397 233, 232, 236, 187, 235, 228, 234, 226,
398 222, 219, 211, 195, 188, 193, 185, 184,
399 191, 183, 201, 229, 181, 220, 194, 162,
400 163, 208, 186, 202, 200, 218, 198, 179,
401 178, 214, 166, 170, 207, 199, 209, 206,
402 204, 160, 212, 216, 215, 192, 175, 173,
403 243, 172, 161, 190, 203, 189, 164, 230,
404 167, 248, 227, 244, 242, 255, 241, 231,
405 240, 253, 169, 210, 245, 237, 249, 247,
406 239, 168, 252, 251, 254, 238, 223, 221,
407 213, 225, 177, 197, 171, 196, 159, 4,
408 5, 6, 8, 9, 10, 11, 12, 13,
409 14, 15, 16, 17, 19, 20, 21, 22,
410 23, 24, 25, 26, 27, 28, 30, 31,
411 32, 33, 34, 35, 36, 37, 38, 39,
412 41, 42, 43, 44, 45, 46, 47, 48,
413 49, 50, 52, 53, 54, 55, 56, 57,
414 58, 59, 60, 61, 63, 64, 65, 66,
415 67, 68, 69, 70, 71, 72, 74, 75,
416 76, 77, 78, 79, 80, 81, 82, 83,
417 86, 87, 88, 89, 90, 91, 92, 93,
418 94, 95, 97, 98, 99, 100, 101, 102,
419 103, 104, 105, 106, 108, 109, 110, 111,
420 112, 113, 114, 115, 116, 117, 119, 120,
421 121, 122, 123, 124, 125, 126, 127, 128,
422 130, 131, 132, 133, 134, 135, 136, 137,
423 138, 139, 141, 142, 143, 144, 145, 146
427 fbmcompile(str, iflag)
431 register unsigned char *s;
432 register unsigned char *table;
434 register int len = str->str_cur;
438 Str_Grow(str,len+258);
440 table = (unsigned char*)(str->str_ptr + len + 1);
442 table = Null(unsigned char*);
445 for (i = 0; i < 256; i++) {
450 while (s >= (unsigned char*)(str->str_ptr))
453 if (table[*s] == len) {
456 table[*s] = table[fold[*s]] = i;
470 str->str_pok |= SP_FBM; /* deep magic */
473 s = (unsigned char*)(str->str_ptr); /* deeper magic */
475 s = Null(unsigned char*);
478 register int tmp, foldtmp;
479 str->str_pok |= SP_CASEFOLD;
480 for (i = 0; i < len; i++) {
482 foldtmp=freq[fold[s[i]]];
483 if (tmp < frequency && foldtmp < frequency) {
485 /* choose most frequent among the two */
486 frequency = (tmp > foldtmp) ? tmp : foldtmp;
491 for (i = 0; i < len; i++) {
492 if (freq[s[i]] < frequency) {
494 frequency = freq[s[i]];
498 str->str_rare = s[rarest];
499 str->str_state = rarest;
502 fprintf(stderr,"rarest char %c at %d\n",str->str_rare, str->str_state);
507 fbminstr(big, bigend, littlestr)
509 register unsigned char *bigend;
512 register unsigned char *s;
514 register int littlelen;
515 register unsigned char *little;
516 register unsigned char *table;
517 register unsigned char *olds;
518 register unsigned char *oldlittle;
521 if (!(littlestr->str_pok & SP_FBM))
522 return ninstr((char*)big,(char*)bigend,
523 littlestr->str_ptr, littlestr->str_ptr + littlestr->str_cur);
526 littlelen = littlestr->str_cur;
528 if (littlestr->str_pok & SP_TAIL && !multiline) { /* tail anchored? */
529 if (littlelen > bigend - big)
531 little = (unsigned char*)littlestr->str_ptr;
532 if (littlestr->str_pok & SP_CASEFOLD) { /* oops, fake it */
533 big = bigend - littlelen; /* just start near end */
534 if (bigend[-1] == '\n' && little[littlelen-1] != '\n')
538 s = bigend - littlelen;
539 if (*s == *little && bcmp(s,little,littlelen)==0)
540 return (char*)s; /* how sweet it is */
541 else if (bigend[-1] == '\n' && little[littlelen-1] != '\n') {
543 if (*s == *little && bcmp(s,little,littlelen)==0)
549 table = (unsigned char*)(littlestr->str_ptr + littlelen + 1);
551 table = Null(unsigned char*);
553 if (--littlelen >= bigend - big)
556 oldlittle = little = table - 2;
557 if (littlestr->str_pok & SP_CASEFOLD) { /* case insensitive? */
560 if (tmp = table[*s]) {
562 if (bigend - s > tmp) {
567 if ((s += tmp) < bigend)
573 tmp = littlelen; /* less expensive than calling strncmp() */
576 if (*--s == *--little || fold[*s] == *little)
578 s = olds + 1; /* here we pay the price for failure */
580 if (s < bigend) /* fake up continue to outer loop */
593 if (tmp = table[*s]) {
595 if (bigend - s > tmp) {
600 if ((s += tmp) < bigend)
606 tmp = littlelen; /* less expensive than calling strncmp() */
609 if (*--s == *--little)
611 s = olds + 1; /* here we pay the price for failure */
613 if (s < bigend) /* fake up continue to outer loop */
627 screaminstr(bigstr, littlestr)
631 register unsigned char *s, *x;
632 register unsigned char *big;
634 register int previous;
636 register unsigned char *little;
637 register unsigned char *bigend;
638 register unsigned char *littleend;
640 if ((pos = screamfirst[littlestr->str_rare]) < 0)
643 little = (unsigned char *)(littlestr->str_ptr);
645 little = Null(unsigned char *);
647 littleend = little + littlestr->str_cur;
649 previous = littlestr->str_state;
651 big = (unsigned char *)(bigstr->str_ptr);
653 big = Null(unsigned char*);
655 bigend = big + bigstr->str_cur;
657 while (pos < previous) {
659 if (!(pos += screamnext[pos]))
663 if (littlestr->str_pok & SP_CASEFOLD) { /* case insignificant? */
665 if (big[pos] != first && big[pos] != fold[first])
667 for (x=big+pos+1,s=little; s < littleend; /**/ ) {
670 if (*s++ != *x++ && fold[*(s-1)] != *(x-1)) {
677 return (char *)(big+pos);
683 pos += screamnext[pos] /* does this goof up anywhere? */
691 if (big[pos] != first)
693 for (x=big+pos+1,s=little; s < littleend; /**/ ) {
703 return (char *)(big+pos);
709 pos += screamnext[pos]
718 /* copy a string to a safe spot */
724 register char *newaddr;
726 New(902,newaddr,strlen(str)+1,char);
727 (void)strcpy(newaddr,str);
731 /* same thing but with a known length */
738 register char *newaddr;
740 New(903,newaddr,len+1,char);
741 (void)bcopy(str,newaddr,len); /* might not be null terminated */
742 newaddr[len] = '\0'; /* is now */
746 /* grow a static string to at least a certain length */
749 growstr(strptr,curlen,newlen)
754 if (newlen > *curlen) { /* need more room? */
756 Renew(*strptr,newlen,char);
758 New(905,*strptr,newlen,char);
765 mess(pat,a1,a2,a3,a4)
772 (void)sprintf(s,pat,a1,a2,a3,a4);
775 if (curcmd->c_line) {
776 (void)sprintf(s," at %s line %ld", filename, (long)curcmd->c_line);
780 stab_io(last_in_stab) &&
781 stab_io(last_in_stab)->lines ) {
782 (void)sprintf(s,", <%s> line %ld",
783 last_in_stab == argvstab ? "" : stab_name(last_in_stab),
784 (long)stab_io(last_in_stab)->lines);
787 (void)strcpy(s,".\n");
792 fatal(pat,a1,a2,a3,a4)
797 extern char *e_tmpname;
800 mess(pat,a1,a2,a3,a4);
802 str_set(stab_val(stabent("@",TRUE)),buf);
804 while (loop_ptr >= 0 && (!loop_stack[loop_ptr].loop_label ||
805 strNE(tmps,loop_stack[loop_ptr].loop_label) )) {
808 deb("(Skipping label #%d %s)\n",loop_ptr,
809 loop_stack[loop_ptr].loop_label);
816 deb("(Found label #%d %s)\n",loop_ptr,
817 loop_stack[loop_ptr].loop_label);
822 fatal("Bad label: %s", tmps);
824 longjmp(loop_stack[loop_ptr].loop_env, 1);
827 (void)fflush(stderr);
829 (void)UNLINK(e_tmpname);
831 exit(errno?errno:(statusvalue?statusvalue:255));
835 warn(pat,a1,a2,a3,a4)
839 mess(pat,a1,a2,a3,a4);
847 (void)fflush(stderr);
866 pat = va_arg(args, char *);
868 (void) vsprintf(s,pat,args);
872 if (curcmd->c_line) {
873 (void)sprintf(s," at %s line %ld", filename, (long)curcmd->c_line);
877 stab_io(last_in_stab) &&
878 stab_io(last_in_stab)->lines ) {
879 (void)sprintf(s,", <%s> line %ld",
880 last_in_stab == argvstab ? "" : last_in_stab->str_magic->str_ptr,
881 (long)stab_io(last_in_stab)->lines);
884 (void)strcpy(s,".\n");
894 extern char *e_tmpname;
905 str_set(stab_val(stabent("@",TRUE)),buf);
907 while (loop_ptr >= 0 && (!loop_stack[loop_ptr].loop_label ||
908 strNE(tmps,loop_stack[loop_ptr].loop_label) )) {
911 deb("(Skipping label #%d %s)\n",loop_ptr,
912 loop_stack[loop_ptr].loop_label);
919 deb("(Found label #%d %s)\n",loop_ptr,
920 loop_stack[loop_ptr].loop_label);
925 fatal("Bad label: %s", tmps);
927 longjmp(loop_stack[loop_ptr].loop_env, 1);
930 (void)fflush(stderr);
932 (void)UNLINK(e_tmpname);
934 exit((int)(errno?errno:(statusvalue?statusvalue:255)));
958 (void)fflush(stderr);
962 static bool firstsetenv = TRUE;
963 extern char **environ;
969 register int i=envix(nam); /* where does it go? */
973 environ[i] = environ[i+1];
978 if (!environ[i]) { /* does not exist yet */
979 if (firstsetenv) { /* need we copy environment? */
983 New(901,tmpenv, i+2, char*);
985 for (j=0; j<i; j++) /* copy environment */
986 tmpenv[j] = environ[j];
987 environ = tmpenv; /* tell exec where it is now */
990 Renew(environ, i+2, char*); /* just expand it a bit */
991 environ[i+1] = Nullch; /* make sure it's null terminated */
993 New(904, environ[i], strlen(nam) + strlen(val) + 2, char);
994 /* this may or may not be in */
995 /* the old environ structure */
997 (void)sprintf(environ[i],"%s=%s",nam,val);/* all that work just for this */
999 /* MS-DOS requires environment variable names to be in uppercase */
1000 strcpy(environ[i],nam); strupr(environ[i],nam);
1001 (void)sprintf(environ[i] + strlen(nam),"=%s",val);
1009 register int i, len = strlen(nam);
1011 for (i = 0; environ[i]; i++) {
1012 if (strnEQ(environ[i],nam,len) && environ[i][len] == '=')
1013 break; /* strnEQ must come first to avoid */
1014 } /* potential SEGV's */
1019 unlnk(f) /* unlink all versions of a file */
1024 for (i = 0; unlink(f) >= 0; i++) ;
1033 register char *from;
1068 vsprintf(dest, pat, args)
1069 char *dest, *pat, *args;
1073 fakebuf._ptr = dest;
1074 fakebuf._cnt = 32767;
1075 fakebuf._flag = _IOWRT|_IOSTRG;
1076 _doprnt(pat, args, &fakebuf); /* what a kludge */
1077 (void)putc('\0', &fakebuf);
1081 return 0; /* perl doesn't use return value */
1087 vfprintf(fd, pat, args)
1091 _doprnt(pat, args, fd);
1092 return 0; /* wrong, but perl doesn't use the return value */
1095 #endif /* VPRINTF */
1096 #endif /* VARARGS */
1099 #if BYTEORDER != 0x4321
1104 #if (BYTEORDER & 1) == 0
1107 result = ((s & 255) << 8) + ((s >> 8) & 255);
1120 char c[sizeof(long)];
1123 #if BYTEORDER == 0x1234
1124 u.c[0] = (l >> 24) & 255;
1125 u.c[1] = (l >> 16) & 255;
1126 u.c[2] = (l >> 8) & 255;
1130 #if ((BYTEORDER - 0x1111) & 0x444) || !(BYTEORDER & 0xf)
1131 fatal("Unknown BYTEORDER\n");
1136 for (o = BYTEORDER - 0x1111, s = 0; s < (sizeof(long)*8); o >>= 4, s += 8) {
1137 u.c[o & 0xf] = (l >> s) & 255;
1150 char c[sizeof(long)];
1153 #if BYTEORDER == 0x1234
1154 u.c[0] = (l >> 24) & 255;
1155 u.c[1] = (l >> 16) & 255;
1156 u.c[2] = (l >> 8) & 255;
1160 #if ((BYTEORDER - 0x1111) & 0x444) || !(BYTEORDER & 0xf)
1161 fatal("Unknown BYTEORDER\n");
1168 for (o = BYTEORDER - 0x1111, s = 0; s < (sizeof(long)*8); o >>= 4, s += 8) {
1169 l |= (u.c[o & 0xf] & 255) << s;
1176 #endif /* BYTEORDER != 0x4321 */
1186 register int this, that;
1189 int doexec = strNE(cmd,"-");
1193 this = (*mode == 'w');
1195 while ((pid = (doexec?vfork():fork())) < 0) {
1196 if (errno != EAGAIN) {
1199 fatal("Can't fork");
1208 if (p[THIS] != (*mode == 'r')) {
1209 dup2(p[THIS], *mode == 'r');
1213 #if !defined(FCNTL) || !defined(F_SETFD)
1219 for (fd = 3; fd < NOFILE; fd++)
1222 do_exec(cmd); /* may or may not use the shell */
1225 if (tmpstab = stabent("$",allstabs))
1226 str_numset(STAB_STR(tmpstab),(double)getpid());
1232 do_execfree(); /* free any memory malloced by child on vfork */
1234 if (p[that] < p[this]) {
1235 dup2(p[this], p[that]);
1239 str = afetch(pidstatary,p[this],TRUE);
1240 str_numset(str,(double)pid);
1243 return fdopen(p[this], mode);
1252 struct stat tmpstatbuf;
1254 fprintf(stderr,"%s", s);
1255 for (fd = 0; fd < 32; fd++) {
1256 if (fstat(fd,&tmpstatbuf) >= 0)
1257 fprintf(stderr," %d",fd);
1259 fprintf(stderr,"\n");
1268 #if defined(FCNTL) && defined(F_DUPFD)
1270 fcntl(oldfd, F_DUPFD, newfd);
1277 while ((fd = dup(oldfd)) != newfd) /* good enough for low fd's */
1280 close(fdtmp[--fdx]);
1291 void (*hstat)(), (*istat)(), (*qstat)();
1293 int (*hstat)(), (*istat)(), (*qstat)();
1299 str = afetch(pidstatary,fileno(ptr),TRUE);
1301 pid = (int)str_gnum(str);
1304 hstat = signal(SIGHUP, SIG_IGN);
1305 istat = signal(SIGINT, SIG_IGN);
1306 qstat = signal(SIGQUIT, SIG_IGN);
1308 if (wait4(pid,&status,0,Null(struct rusage *)) < 0)
1311 if (pid < 0) /* already exited? */
1312 status = str->str_cur;
1316 while ((result = wait(&status)) != pid && result >= 0)
1317 pidgone(result,status);
1322 signal(SIGHUP, hstat);
1323 signal(SIGINT, istat);
1324 signal(SIGQUIT, qstat);
1325 str_numset(str,0.0);
1340 for (count = pidstatary->ary_fill; count >= 0; --count) {
1341 if ((str = afetch(pidstatary,count,FALSE)) &&
1342 ((int)str->str_u.str_nval) == pid) {
1343 str_numset(str, -str->str_u.str_nval);
1344 str->str_cur = status;
1353 register unsigned char *s1;
1354 register unsigned char *s2;
1360 if (tmp = *s1++ - *s2++)
1368 repeatcpy(to,from,len,count)
1370 register char *from;
1375 register char *frombase = from;
1383 while (count-- > 0) {
1384 for (todo = len; todo > 0; todo--) {
1391 #ifndef CASTNEGFLOAT
1399 return (unsigned long)f;
1401 return (unsigned long)along;
1411 char *fa = rindex(a,'/');
1412 char *fb = rindex(b,'/');
1413 struct stat tmpstatbuf1;
1414 struct stat tmpstatbuf2;
1416 #define MAXPATHLEN 1024
1418 char tmpbuf[MAXPATHLEN+1];
1433 strncpy(tmpbuf, a, fa - a);
1434 if (stat(tmpbuf, &tmpstatbuf1) < 0)
1439 strncpy(tmpbuf, b, fb - b);
1440 if (stat(tmpbuf, &tmpstatbuf2) < 0)
1442 return tmpstatbuf1.st_dev == tmpstatbuf2.st_dev &&
1443 tmpstatbuf1.st_ino == tmpstatbuf2.st_ino;
1445 #endif /* !RENAME */