1 #define PETSC_DLL 2 /* 3 We define the string operations here. The reason we just do not use 4 the standard string routines in the PETSc code is that on some machines 5 they are broken or have the wrong prototypes. 6 7 */ 8 #include "petsc.h" /*I "petsc.h" I*/ 9 #include "petscsys.h" 10 #if defined(PETSC_HAVE_STRING_H) 11 #include <string.h> 12 #endif 13 #if defined(PETSC_HAVE_STRINGS_H) 14 #include <strings.h> 15 #endif 16 #include "petscfix.h" 17 18 #undef __FUNCT__ 19 #define __FUNCT__ "PetscStrlen" 20 /*@C 21 PetscStrlen - Gets length of a string 22 23 Not Collective 24 25 Input Parameters: 26 . s - pointer to string 27 28 Output Parameter: 29 . len - length in bytes 30 31 Level: intermediate 32 33 Note: 34 This routine is analogous to strlen(). 35 36 Null string returns a length of zero 37 38 Concepts: string length 39 40 @*/ 41 PetscErrorCode PETSC_DLLEXPORT PetscStrlen(const char s[],size_t *len) 42 { 43 PetscFunctionBegin; 44 if (!s) { 45 *len = 0; 46 } else { 47 *len = strlen(s); 48 } 49 PetscFunctionReturn(0); 50 } 51 52 #undef __FUNCT__ 53 #define __FUNCT__ "PetscStrallocpy" 54 /*@C 55 PetscStrallocpy - Allocates space to hold a copy of a string then copies the string 56 57 Not Collective 58 59 Input Parameters: 60 . s - pointer to string 61 62 Output Parameter: 63 . t - the copied string 64 65 Level: intermediate 66 67 Note: 68 Null string returns a new null string 69 70 Concepts: string copy 71 72 @*/ 73 PetscErrorCode PETSC_DLLEXPORT PetscStrallocpy(const char s[],char *t[]) 74 { 75 PetscErrorCode ierr; 76 size_t len; 77 char *tmp = 0; 78 79 PetscFunctionBegin; 80 if (s) { 81 ierr = PetscStrlen(s,&len);CHKERRQ(ierr); 82 ierr = PetscMalloc((1+len)*sizeof(char),&tmp);CHKERRQ(ierr); 83 ierr = PetscStrcpy(tmp,s);CHKERRQ(ierr); 84 } 85 *t = tmp; 86 PetscFunctionReturn(0); 87 } 88 89 #undef __FUNCT__ 90 #define __FUNCT__ "PetscStrcpy" 91 /*@C 92 PetscStrcpy - Copies a string 93 94 Not Collective 95 96 Input Parameters: 97 . t - pointer to string 98 99 Output Parameter: 100 . s - the copied string 101 102 Level: intermediate 103 104 Note: 105 Null string returns a string starting with zero 106 107 Concepts: string copy 108 109 .seealso: PetscStrncpy(), PetscStrcat(), PetscStrncat() 110 111 @*/ 112 113 PetscErrorCode PETSC_DLLEXPORT PetscStrcpy(char s[],const char t[]) 114 { 115 PetscFunctionBegin; 116 if (t && !s) { 117 SETERRQ(PETSC_ERR_ARG_NULL,"Trying to copy string into null pointer"); 118 } 119 if (t) {strcpy(s,t);} 120 else if (s) {s[0] = 0;} 121 PetscFunctionReturn(0); 122 } 123 124 #undef __FUNCT__ 125 #define __FUNCT__ "PetscStrncpy" 126 /*@C 127 PetscStrncpy - Copies a string up to a certain length 128 129 Not Collective 130 131 Input Parameters: 132 + t - pointer to string 133 - n - the length to copy 134 135 Output Parameter: 136 . s - the copied string 137 138 Level: intermediate 139 140 Note: 141 Null string returns a string starting with zero 142 143 Concepts: string copy 144 145 .seealso: PetscStrcpy(), PetscStrcat(), PetscStrncat() 146 147 @*/ 148 PetscErrorCode PETSC_DLLEXPORT PetscStrncpy(char s[],const char t[],size_t n) 149 { 150 PetscFunctionBegin; 151 if (t && !s) { 152 SETERRQ(PETSC_ERR_ARG_NULL,"Trying to copy string into null pointer"); 153 } 154 if (t) {strncpy(s,t,n);} 155 else if (s) {s[0] = 0;} 156 PetscFunctionReturn(0); 157 } 158 159 #undef __FUNCT__ 160 #define __FUNCT__ "PetscStrcat" 161 /*@C 162 PetscStrcat - Concatenates a string onto a given string 163 164 Not Collective 165 166 Input Parameters: 167 + s - string to be added to 168 - t - pointer to string to be added to end 169 170 Level: intermediate 171 172 Concepts: string copy 173 174 .seealso: PetscStrcpy(), PetscStrncpy(), PetscStrncat() 175 176 @*/ 177 PetscErrorCode PETSC_DLLEXPORT PetscStrcat(char s[],const char t[]) 178 { 179 PetscFunctionBegin; 180 if (!t) PetscFunctionReturn(0); 181 strcat(s,t); 182 PetscFunctionReturn(0); 183 } 184 185 #undef __FUNCT__ 186 #define __FUNCT__ "PetscStrncat" 187 /*@C 188 PetscStrncat - Concatenates a string onto a given string, up to a given length 189 190 Not Collective 191 192 Input Parameters: 193 + s - pointer to string to be added to end 194 . t - string to be added to 195 . n - maximum length to copy 196 197 Level: intermediate 198 199 Concepts: string copy 200 201 .seealso: PetscStrcpy(), PetscStrncpy(), PetscStrcat() 202 203 @*/ 204 PetscErrorCode PETSC_DLLEXPORT PetscStrncat(char s[],const char t[],size_t n) 205 { 206 PetscFunctionBegin; 207 strncat(s,t,n); 208 PetscFunctionReturn(0); 209 } 210 211 #undef __FUNCT__ 212 #define __FUNCT__ "PetscStrcmp" 213 /*@C 214 PetscStrcmp - Compares two strings, 215 216 Not Collective 217 218 Input Parameters: 219 + a - pointer to string first string 220 - b - pointer to second string 221 222 Output Parameter: 223 . flg - if the two strings are equal 224 225 Level: intermediate 226 227 .seealso: PetscStrgrt(), PetscStrncmp(), PetscStrcasecmp() 228 229 @*/ 230 PetscErrorCode PETSC_DLLEXPORT PetscStrcmp(const char a[],const char b[],PetscTruth *flg) 231 { 232 int c; 233 234 PetscFunctionBegin; 235 if (!a && !b) { 236 *flg = PETSC_TRUE; 237 } else if (!a || !b) { 238 *flg = PETSC_FALSE; 239 } else { 240 c = strcmp(a,b); 241 if (c) *flg = PETSC_FALSE; 242 else *flg = PETSC_TRUE; 243 } 244 PetscFunctionReturn(0); 245 } 246 247 #undef __FUNCT__ 248 #define __FUNCT__ "PetscStrgrt" 249 /*@C 250 PetscStrgrt - If first string is greater than the second 251 252 Not Collective 253 254 Input Parameters: 255 + a - pointer to first string 256 - b - pointer to second string 257 258 Output Parameter: 259 . flg - if the first string is greater 260 261 Notes: 262 Null arguments are ok, a null string is considered smaller than 263 all others 264 265 Level: intermediate 266 267 .seealso: PetscStrcmp(), PetscStrncmp(), PetscStrcasecmp() 268 269 @*/ 270 PetscErrorCode PETSC_DLLEXPORT PetscStrgrt(const char a[],const char b[],PetscTruth *t) 271 { 272 int c; 273 274 PetscFunctionBegin; 275 if (!a && !b) { 276 *t = PETSC_FALSE; 277 } else if (a && !b) { 278 *t = PETSC_TRUE; 279 } else if (!a && b) { 280 *t = PETSC_FALSE; 281 } else { 282 c = strcmp(a,b); 283 if (c > 0) *t = PETSC_TRUE; 284 else *t = PETSC_FALSE; 285 } 286 PetscFunctionReturn(0); 287 } 288 289 #undef __FUNCT__ 290 #define __FUNCT__ "PetscStrcasecmp" 291 /*@C 292 PetscStrcasecmp - Returns true if the two strings are the same 293 except possibly for case. 294 295 Not Collective 296 297 Input Parameters: 298 + a - pointer to first string 299 - b - pointer to second string 300 301 Output Parameter: 302 . flg - if the two strings are the same 303 304 Notes: 305 Null arguments are ok 306 307 Level: intermediate 308 309 .seealso: PetscStrcmp(), PetscStrncmp(), PetscStrgrt() 310 311 @*/ 312 PetscErrorCode PETSC_DLLEXPORT PetscStrcasecmp(const char a[],const char b[],PetscTruth *t) 313 { 314 int c; 315 316 PetscFunctionBegin; 317 if (!a && !b) c = 0; 318 else if (!a || !b) c = 1; 319 #if defined(PETSC_HAVE_STRCASECMP) 320 else c = strcasecmp(a,b); 321 #elif defined(PETSC_HAVE_STRICMP) 322 else c = stricmp(a,b); 323 #else 324 else { 325 char *aa,*bb; 326 PetscErrorCode ierr; 327 ierr = PetscStrallocpy(a,&aa);CHKERRQ(ierr); 328 ierr = PetscStrallocpy(b,&bb);CHKERRQ(ierr); 329 ierr = PetscStrtolower(aa);CHKERRQ(ierr); 330 ierr = PetscStrtolower(bb);CHKERRQ(ierr); 331 ierr = PetscStrcmp(aa,bb,t);CHKERRQ(ierr); 332 ierr = PetscStrfree(aa);CHKERRQ(ierr); 333 ierr = PetscStrfree(bb);CHKERRQ(ierr); 334 PetscFunctionReturn(0); 335 } 336 #endif 337 if (!c) *t = PETSC_TRUE; 338 else *t = PETSC_FALSE; 339 PetscFunctionReturn(0); 340 } 341 342 343 344 #undef __FUNCT__ 345 #define __FUNCT__ "PetscStrncmp" 346 /*@C 347 PetscStrncmp - Compares two strings, up to a certain length 348 349 Not Collective 350 351 Input Parameters: 352 + a - pointer to first string 353 . b - pointer to second string 354 - n - length to compare up to 355 356 Output Parameter: 357 . t - if the two strings are equal 358 359 Level: intermediate 360 361 .seealso: PetscStrgrt(), PetscStrcmp(), PetscStrcasecmp() 362 363 @*/ 364 PetscErrorCode PETSC_DLLEXPORT PetscStrncmp(const char a[],const char b[],size_t n,PetscTruth *t) 365 { 366 int c; 367 368 PetscFunctionBegin; 369 c = strncmp(a,b,n); 370 if (!c) *t = PETSC_TRUE; 371 else *t = PETSC_FALSE; 372 PetscFunctionReturn(0); 373 } 374 375 #undef __FUNCT__ 376 #define __FUNCT__ "PetscStrchr" 377 /*@C 378 PetscStrchr - Locates first occurance of a character in a string 379 380 Not Collective 381 382 Input Parameters: 383 + a - pointer to string 384 - b - character 385 386 Output Parameter: 387 . c - location of occurance, PETSC_NULL if not found 388 389 Level: intermediate 390 391 @*/ 392 PetscErrorCode PETSC_DLLEXPORT PetscStrchr(const char a[],char b,char *c[]) 393 { 394 PetscFunctionBegin; 395 *c = (char *)strchr(a,b); 396 PetscFunctionReturn(0); 397 } 398 399 #undef __FUNCT__ 400 #define __FUNCT__ "PetscStrrchr" 401 /*@C 402 PetscStrrchr - Locates one location past the last occurance of a character in a string, 403 if the character is not found then returns entire string 404 405 Not Collective 406 407 Input Parameters: 408 + a - pointer to string 409 - b - character 410 411 Output Parameter: 412 . tmp - location of occurance, a if not found 413 414 Level: intermediate 415 416 @*/ 417 PetscErrorCode PETSC_DLLEXPORT PetscStrrchr(const char a[],char b,char *tmp[]) 418 { 419 PetscFunctionBegin; 420 *tmp = (char *)strrchr(a,b); 421 if (!*tmp) *tmp = (char*)a; else *tmp = *tmp + 1; 422 PetscFunctionReturn(0); 423 } 424 425 #undef __FUNCT__ 426 #define __FUNCT__ "PetscStrtolower" 427 /*@C 428 PetscStrtolower - Converts string to lower case 429 430 Not Collective 431 432 Input Parameters: 433 . a - pointer to string 434 435 Level: intermediate 436 437 @*/ 438 PetscErrorCode PETSC_DLLEXPORT PetscStrtolower(char a[]) 439 { 440 PetscFunctionBegin; 441 while (*a) { 442 if (*a >= 'A' && *a <= 'Z') *a += 'a' - 'A'; 443 a++; 444 } 445 PetscFunctionReturn(0); 446 } 447 448 struct _p_PetscToken {char token;char *array;char *current;}; 449 450 451 #undef __FUNCT__ 452 #define __FUNCT__ "PetscTokenFind" 453 /*@C 454 PetscTokenFind - Locates next "token" in a string 455 456 Not Collective 457 458 Input Parameters: 459 . a - pointer to token 460 461 Output Parameter: 462 . result - location of occurance, PETSC_NULL if not found 463 464 Notes: 465 466 This version is different from the system version in that 467 it allows you to pass a read-only string into the function. 468 469 This version also treats all characters etc. inside a double quote " 470 as a single token. 471 472 Level: intermediate 473 474 .seealso: PetscTokenCreate(), PetscTokenDestroy() 475 @*/ 476 PetscErrorCode PETSC_DLLEXPORT PetscTokenFind(PetscToken a,char *result[]) 477 { 478 char *ptr = a->current,token; 479 480 PetscFunctionBegin; 481 *result = a->current; 482 if (ptr && !*ptr) {*result = 0;PetscFunctionReturn(0);} 483 token = a->token; 484 if (ptr && (*ptr == '"')) {token = '"';(*result)++;ptr++;} 485 while (ptr) { 486 if (*ptr == token) { 487 *ptr++ = 0; 488 while (*ptr == a->token) ptr++; 489 a->current = ptr; 490 break; 491 } 492 if (!*ptr) { 493 a->current = 0; 494 break; 495 } 496 ptr++; 497 } 498 PetscFunctionReturn(0); 499 } 500 501 #undef __FUNCT__ 502 #define __FUNCT__ "PetscTokenCreate" 503 /*@C 504 PetscTokenCreate - Creates a PetscToken used to find tokens in a string 505 506 Not Collective 507 508 Input Parameters: 509 + string - the string to look in 510 - token - the character to look for 511 512 Output Parameter: 513 . a - pointer to token 514 515 Notes: 516 517 This version is different from the system version in that 518 it allows you to pass a read-only string into the function. 519 520 Level: intermediate 521 522 .seealso: PetscTokenFind(), PetscTokenDestroy() 523 @*/ 524 PetscErrorCode PETSC_DLLEXPORT PetscTokenCreate(const char a[],const char b,PetscToken *t) 525 { 526 PetscErrorCode ierr; 527 528 PetscFunctionBegin; 529 ierr = PetscNew(struct _p_PetscToken,t);CHKERRQ(ierr); 530 ierr = PetscStrallocpy(a,&(*t)->array);CHKERRQ(ierr); 531 (*t)->current = (*t)->array; 532 (*t)->token = b; 533 PetscFunctionReturn(0); 534 } 535 536 #undef __FUNCT__ 537 #define __FUNCT__ "PetscTokenDestroy" 538 /*@C 539 PetscTokenDestroy - Destroys a PetscToken 540 541 Not Collective 542 543 Input Parameters: 544 . a - pointer to token 545 546 Level: intermediate 547 548 .seealso: PetscTokenCreate(), PetscTokenFind() 549 @*/ 550 PetscErrorCode PETSC_DLLEXPORT PetscTokenDestroy(PetscToken a) 551 { 552 PetscErrorCode ierr; 553 554 PetscFunctionBegin; 555 ierr = PetscFree(a->array);CHKERRQ(ierr); 556 ierr = PetscFree(a);CHKERRQ(ierr); 557 PetscFunctionReturn(0); 558 } 559 560 #undef __FUNCT__ 561 #define __FUNCT__ "PetscStrrstr" 562 /*@C 563 PetscStrrstr - Locates last occurance of string in another string 564 565 Not Collective 566 567 Input Parameters: 568 + a - pointer to string 569 - b - string to find 570 571 Output Parameter: 572 . tmp - location of occurance 573 574 Level: intermediate 575 576 @*/ 577 PetscErrorCode PETSC_DLLEXPORT PetscStrrstr(const char a[],const char b[],char *tmp[]) 578 { 579 const char *stmp = a, *ltmp = 0; 580 581 PetscFunctionBegin; 582 while (stmp) { 583 stmp = (char *)strstr(stmp,b); 584 if (stmp) {ltmp = stmp;stmp++;} 585 } 586 *tmp = (char *)ltmp; 587 PetscFunctionReturn(0); 588 } 589 590 #undef __FUNCT__ 591 #define __FUNCT__ "PetscStrstr" 592 /*@C 593 PetscStrstr - Locates first occurance of string in another string 594 595 Not Collective 596 597 Input Parameters: 598 + a - pointer to string 599 - b - string to find 600 601 Output Parameter: 602 . tmp - location of occurance 603 604 Level: intermediate 605 606 @*/ 607 PetscErrorCode PETSC_DLLEXPORT PetscStrstr(const char a[],const char b[],char *tmp[]) 608 { 609 PetscFunctionBegin; 610 *tmp = (char *)strstr(a,b); 611 PetscFunctionReturn(0); 612 } 613 614 #undef __FUNCT__ 615 #define __FUNCT__ "PetscGetPetscDir" 616 /*@C 617 PetscGetPetscDir - Gets the directory PETSc is installed in 618 619 Not Collective 620 621 Output Parameter: 622 . dir - the directory 623 624 Level: developer 625 626 @*/ 627 PetscErrorCode PETSC_DLLEXPORT PetscGetPetscDir(const char *dir[]) 628 { 629 PetscFunctionBegin; 630 *dir = PETSC_DIR; 631 PetscFunctionReturn(0); 632 } 633 634 #undef __FUNCT__ 635 #define __FUNCT__ "PetscStrreplace" 636 /*@C 637 PetscStrreplace - Replaces substrings in string with other substrings 638 639 Not Collective 640 641 Input Parameters: 642 + comm - MPI_Comm of processors that are processing the string 643 . aa - the string to look in 644 . b - the resulting copy of a with replaced strings (b can be the same as a) 645 - len - the length of b 646 647 Notes: 648 Replaces ${PETSC_ARCH},${PETSC_DIR},${PETSC_LIB_DIR},${DISPLAY}, 649 ${HOMEDIRECTORY},${WORKINGDIRECTORY},${USERNAME} with appropriate values 650 as well as any environmental variables. 651 652 Note: PETSC_LIB_DIR uses the environmental variable if it exists. PETSC_ARCH and PETSC_DIR use what 653 PETSc was built with and do not use environmental variables. 654 655 Level: intermediate 656 657 @*/ 658 PetscErrorCode PETSC_DLLEXPORT PetscStrreplace(MPI_Comm comm,const char aa[],char b[],size_t len) 659 { 660 PetscErrorCode ierr; 661 int i = 0; 662 size_t l,l1,l2,l3; 663 char *work,*par,*epar,env[1024],*tfree,*a = (char*)aa; 664 const char *s[] = {"${PETSC_ARCH}","${PETSC_DIR}","${PETSC_LIB_DIR}","${DISPLAY}","${HOMEDIRECTORY}","${WORKINGDIRECTORY}","${USERNAME}",0}; 665 const char *r[] = {0,0,0,0,0,0,0,0}; 666 PetscTruth flag; 667 668 PetscFunctionBegin; 669 if (!a || !b) SETERRQ(PETSC_ERR_ARG_NULL,"a and b strings must be nonnull"); 670 if (aa == b) { 671 ierr = PetscStrallocpy(aa,(char **)&a);CHKERRQ(ierr); 672 } 673 ierr = PetscMalloc(len*sizeof(char*),&work);CHKERRQ(ierr); 674 675 /* get values for replaced variables */ 676 ierr = PetscStrallocpy(PETSC_ARCH,(char**)&r[0]);CHKERRQ(ierr); 677 ierr = PetscStrallocpy(PETSC_DIR,(char**)&r[1]);CHKERRQ(ierr); 678 ierr = PetscStrallocpy(PETSC_LIB_DIR,(char**)&r[2]);CHKERRQ(ierr); 679 ierr = PetscMalloc(256*sizeof(char),&r[3]);CHKERRQ(ierr); 680 ierr = PetscMalloc(PETSC_MAX_PATH_LEN*sizeof(char),&r[4]);CHKERRQ(ierr); 681 ierr = PetscMalloc(PETSC_MAX_PATH_LEN*sizeof(char),&r[5]);CHKERRQ(ierr); 682 ierr = PetscMalloc(256*sizeof(char),&r[6]);CHKERRQ(ierr); 683 ierr = PetscGetDisplay((char*)r[3],256);CHKERRQ(ierr); 684 ierr = PetscGetHomeDirectory((char*)r[4],PETSC_MAX_PATH_LEN);CHKERRQ(ierr); 685 ierr = PetscGetWorkingDirectory((char*)r[5],PETSC_MAX_PATH_LEN);CHKERRQ(ierr); 686 ierr = PetscGetUserName((char*)r[6],256);CHKERRQ(ierr); 687 688 /* replace that are in environment */ 689 ierr = PetscOptionsGetenv(comm,"PETSC_LIB_DIR",env,1024,&flag);CHKERRQ(ierr); 690 if (flag) { 691 ierr = PetscStrallocpy(env,(char**)&r[2]);CHKERRQ(ierr); 692 } 693 694 /* replace the requested strings */ 695 ierr = PetscStrncpy(b,a,len);CHKERRQ(ierr); 696 while (s[i]) { 697 ierr = PetscStrlen(s[i],&l);CHKERRQ(ierr); 698 ierr = PetscStrstr(b,s[i],&par);CHKERRQ(ierr); 699 while (par) { 700 *par = 0; 701 par += l; 702 703 ierr = PetscStrlen(b,&l1);CHKERRQ(ierr); 704 ierr = PetscStrlen(r[i],&l2);CHKERRQ(ierr); 705 ierr = PetscStrlen(par,&l3);CHKERRQ(ierr); 706 if (l1 + l2 + l3 >= len) { 707 SETERRQ(PETSC_ERR_ARG_SIZ,"b len is not long enough to hold new values"); 708 } 709 ierr = PetscStrcpy(work,b);CHKERRQ(ierr); 710 ierr = PetscStrcat(work,r[i]);CHKERRQ(ierr); 711 ierr = PetscStrcat(work,par);CHKERRQ(ierr); 712 ierr = PetscStrncpy(b,work,len);CHKERRQ(ierr); 713 ierr = PetscStrstr(b,s[i],&par);CHKERRQ(ierr); 714 } 715 i++; 716 } 717 i = 0; 718 while (r[i]) { 719 tfree = (char*)r[i]; 720 ierr = PetscFree(tfree);CHKERRQ(ierr); 721 i++; 722 } 723 724 /* look for any other ${xxx} strings to replace from environmental variables */ 725 ierr = PetscStrstr(b,"${",&par);CHKERRQ(ierr); 726 while (par) { 727 *par = 0; 728 par += 2; 729 ierr = PetscStrcpy(work,b);CHKERRQ(ierr); 730 ierr = PetscStrstr(par,"}",&epar);CHKERRQ(ierr); 731 *epar = 0; 732 epar += 1; 733 ierr = PetscOptionsGetenv(comm,par,env,256,&flag);CHKERRQ(ierr); 734 if (!flag) { 735 SETERRQ1(PETSC_ERR_ARG_WRONG,"Substitution string ${%s} not found as environmental variable",par); 736 } 737 ierr = PetscStrcat(work,env);CHKERRQ(ierr); 738 ierr = PetscStrcat(work,epar);CHKERRQ(ierr); 739 ierr = PetscStrcpy(b,work);CHKERRQ(ierr); 740 ierr = PetscStrstr(b,"${",&par);CHKERRQ(ierr); 741 } 742 ierr = PetscFree(work);CHKERRQ(ierr); 743 if (aa == b) { 744 ierr = PetscFree(a);CHKERRQ(ierr); 745 } 746 PetscFunctionReturn(0); 747 } 748 749 /*MC 750 PetscStrfree - Frees a string (if it is not null) 751 752 Not Collective 753 754 Synopsis: 755 PetscErrorCode PetscStrfree(char *s) 756 757 Input Parameter: 758 . s - pointer to string 759 760 Level: intermediate 761 762 Concepts: string free 763 764 .seealso: PetscStrncpy(), PetscStrcat(), PetscStrncat(), PetscStrallocpy() 765 766 M*/ 767