1 /* 2 * Copyright 2001-2007, Haiku. 3 * Copyright (c) 2003-2004 Kian Duffy <myob@users.sourceforge.net> 4 * Copyright (C) 1998,99 Kazuho Okui and Takashi Murai. 5 * 6 * Distributed unter the terms of the MIT license. 7 */ 8 9 10 #include "TermApp.h" 11 12 #include "CodeConv.h" 13 #include "PrefHandler.h" 14 #include "spawn.h" 15 #include "TermBuffer.h" 16 #include "TermWindow.h" 17 #include "TermConst.h" 18 19 #include <Alert.h> 20 #include <Clipboard.h> 21 #include <NodeInfo.h> 22 #include <Path.h> 23 #include <Roster.h> 24 #include <Screen.h> 25 #include <String.h> 26 #include <TextView.h> 27 28 #include <signal.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <sys/wait.h> 32 #include <unistd.h> 33 34 35 // Preference temporary 36 extern PrefHandler *gTermPref; 37 extern char gWindowName[]; 38 39 bool gUsageRequested = false; 40 bool gGeometryRequested = false; 41 bool gColorRequested = false; 42 43 struct standard_args { 44 char *name; 45 char *longname; 46 int priority; 47 int nargs; 48 const char *prefname; 49 }; 50 51 struct standard_args standard_args[] = { 52 { "-h", "--help", 90, 0, NULL }, 53 { "-f", "--fullscreen", 30, 0, NULL }, 54 { "-p", "--preference", 80, 1, NULL }, 55 { "-t", "--title", 70, 1, NULL }, 56 { "-geom", "--geometry", 50, 1, NULL }, 57 #if 0 58 { "-fg", "--text-fore-color", 40, 1, PREF_TEXT_FORE_COLOR }, 59 { "-bg", "--text-back-color", 35, 1, PREF_TEXT_BACK_COLOR }, 60 { "-curfg", "--cursor-fore-color", 30, 1, PREF_CURSOR_FORE_COLOR }, 61 { "-curbg", "--cursor-back-color", 25, 1, PREF_CURSOR_BACK_COLOR }, 62 { "-selfg", "--select-fore-color", 20, 1, PREF_SELECT_FORE_COLOR }, 63 { "-selbg", "--select-back-color", 15, 1, PREF_SELECT_BACK_COLOR }, 64 { "-imfg", "--im-fore-color", 10, 1, PREF_IM_FORE_COLOR }, 65 { "-imbg", "--im-back-color", 5, 1, PREF_IM_BACK_COLOR }, 66 { "-imsel", "--im-select-color", 0, 1, PREF_IM_SELECT_COLOR }, 67 #endif 68 }; 69 70 int argmatch(char **, int, char *, char *, int, char **, int *); 71 void sort_args(int, char **); 72 int text_to_rgb(char *, rgb_color *, char *); 73 74 75 const ulong MSG_ACTIVATE_TERM = 'msat'; 76 const ulong MSG_TERM_IS_MINIMIZE = 'mtim'; 77 78 79 TermApp::TermApp() 80 : BApplication(TERM_SIGNATURE), 81 fStartFullscreen(false), 82 fWindowNumber(-1), 83 fTermWindow(NULL) 84 { 85 fWindowTitle = "Terminal"; 86 _RegisterTerminal(); 87 88 if (fWindowNumber > 0) 89 fWindowTitle << " " << fWindowNumber; 90 91 int i = fWindowNumber / 16; 92 int j = fWindowNumber % 16; 93 int k = (j * 16) + (i * 64) + 50; 94 int l = (j * 16) + 50; 95 96 fTermFrame.Set(k, l, k + 50, k + 50); 97 98 gTermPref = new PrefHandler(); 99 } 100 101 102 TermApp::~TermApp() 103 { 104 } 105 106 107 void 108 TermApp::ReadyToRun() 109 { 110 // Prevent opeing window when option -h is given. 111 if (gUsageRequested) 112 return; 113 114 status_t status = _MakeTermWindow(fTermFrame); 115 116 // failed spawn, print stdout and open alert panel 117 if (status < B_OK) { 118 (new BAlert("alert", "Terminal couldn't start the shell. Sorry.", 119 "ok", NULL, NULL, B_WIDTH_FROM_LABEL, 120 B_INFO_ALERT))->Go(NULL); 121 PostMessage(B_QUIT_REQUESTED); 122 return; 123 } 124 125 // using BScreen::Frame isn't enough 126 if (fStartFullscreen) 127 BMessenger(fTermWindow).SendMessage(FULLSCREEN); 128 } 129 130 131 void 132 TermApp::Quit() 133 { 134 if (!gUsageRequested){ 135 int status; 136 137 kill(-sh_pid, SIGHUP); 138 wait(&status); 139 140 _UnregisterTerminal(); 141 } 142 143 delete gTermPref; 144 BApplication::Quit(); 145 } 146 147 148 void 149 TermApp::AboutRequested() 150 { 151 BAlert *alert = new BAlert("about", "Terminal\n" 152 "\twritten by Kazuho Okui and Takashi Murai\n" 153 "\tupdated by Kian Duffy and others\n\n" 154 "\tCopyright " B_UTF8_COPYRIGHT "2003-2005, Haiku.\n", "Ok"); 155 BTextView *view = alert->TextView(); 156 157 view->SetStylable(true); 158 159 BFont font; 160 view->GetFont(&font); 161 font.SetSize(18); 162 font.SetFace(B_BOLD_FACE); 163 view->SetFontAndColor(0, 8, &font); 164 165 alert->Go(); 166 } 167 168 169 void 170 TermApp::MessageReceived(BMessage* msg) 171 { 172 switch (msg->what) { 173 case MENU_SWITCH_TERM: 174 _SwitchTerm(); 175 break; 176 177 case MSG_ACTIVATE_TERM: 178 fTermWindow->TermWinActivate(); 179 break; 180 181 case MSG_TERM_IS_MINIMIZE: 182 { 183 BMessage reply(B_REPLY); 184 reply.AddBool("result", fTermWindow->IsMinimized()); 185 msg->SendReply(&reply); 186 break; 187 } 188 189 default: 190 BApplication::MessageReceived(msg); 191 break; 192 } 193 } 194 195 196 void 197 TermApp::ArgvReceived(int32 argc, char **argv) 198 { 199 int skip_args = 0; 200 char *value = 0; 201 202 if (argc < 2) 203 return; 204 205 sort_args(argc, argv); 206 argc = 0; 207 while (argv[argc]) 208 argc++; 209 210 // Print usage 211 if (argmatch(argv, argc, "-help", "--help", 3, NULL, &skip_args)) { 212 _Usage(argv[0]); 213 gUsageRequested = true; 214 PostMessage(B_QUIT_REQUESTED); 215 } 216 217 // Start fullscreen 218 if (argmatch(argv, argc, "-f", "--fullscreen", 4, NULL, &skip_args)) 219 fStartFullscreen = true; 220 221 // Load preference file 222 if (argmatch(argv, argc, "-p", "--preference", 4, &value, &skip_args)) 223 gTermPref->Open(value); 224 225 // Set window title 226 if (argmatch(argv ,argc, "-t", "--title", 3, &value, &skip_args)) 227 fWindowTitle = value; 228 229 // Set window geometry 230 if (argmatch(argv, argc, "-geom", "--geometry", 4, &value, &skip_args)) { 231 int width, height, xpos, ypos; 232 233 sscanf(value, "%dx%d+%d+%d", &width, &height, &xpos, &ypos); 234 if (width < 0 || height < 0 || xpos < 0 || ypos < 0 235 || width >= 256 || height >= 256 || xpos >= 2048 || ypos >= 2048) { 236 fprintf(stderr, "%s: invalid geometry format or value.\n", argv[0]); 237 fprintf(stderr, "Try `%s --help' for more information.\n", argv[0]); 238 gUsageRequested = true; 239 PostMessage(B_QUIT_REQUESTED); 240 } 241 gTermPref->setInt32(PREF_COLS, width); 242 gTermPref->setInt32(PREF_ROWS, height); 243 244 fTermFrame.Set(xpos, ypos, xpos + 50, ypos + 50); 245 gGeometryRequested = true; 246 } 247 248 #if 0 249 // Open '/etc/rgb.txt' file 250 BFile inFile; 251 off_t size = 0; 252 253 status_t status = inFile.SetTo("/etc/rgb.txt", B_READ_ONLY); 254 if (status != B_OK) { 255 fprintf(stderr, "%s: Can't open /etc/rgb.txt file.\n", argv[0]); 256 gUsageRequested = true; 257 PostMessage(B_QUIT_REQUESTED); 258 } 259 260 inFile.GetSize(&size); 261 char *buffer = new char [size]; 262 inFile.Read(buffer, size); 263 264 // Set window, cursor, area and IM color 265 for (int i = 4; i < 13; i++) { 266 if (argmatch(argv, argc, standard_args[i].name, standard_args[i].longname, 267 9, &value, &skip_args)) { 268 rgb_color color; 269 if (text_to_rgb(value, &color, buffer)) 270 gTermPref->setRGB(standard_args[i].prefname, color); 271 else 272 fprintf(stderr, "%s: invalid color string -- %s\n", argv[0], value); 273 } 274 } 275 276 delete[] buffer; 277 #endif 278 skip_args++; 279 280 if (skip_args < argc) { 281 // Check invalid options 282 283 if (*argv[skip_args] == '-') { 284 fprintf(stderr, "%s: invalid option `%s'\n", argv[0], argv[skip_args]); 285 fprintf(stderr, "Try `%s --help' for more information.\n", argv[0]); 286 gUsageRequested = true; 287 PostMessage(B_QUIT_REQUESTED); 288 } 289 290 CommandLine += argv[skip_args++]; 291 while (skip_args < argc) { 292 CommandLine += ' '; 293 CommandLine += argv[skip_args++]; 294 } 295 } 296 } 297 298 299 void 300 TermApp::RefsReceived(BMessage* message) 301 { 302 // Works Only Launced by Double-Click file, or Drags file to App. 303 if (!IsLaunching()) 304 return; 305 306 entry_ref ref; 307 if (message->FindRef("refs", 0, &ref) != B_OK) 308 return; 309 310 BFile file; 311 if (file.SetTo(&ref, B_READ_WRITE) != B_OK) 312 return; 313 314 BNodeInfo info(&file); 315 char mimetype[B_MIME_TYPE_LENGTH]; 316 info.GetType(mimetype); 317 318 // if App opened by Pref file 319 if (!strcmp(mimetype, PREFFILE_MIMETYPE)) { 320 321 BEntry ent(&ref); 322 BPath path(&ent); 323 gTermPref->OpenText(path.Path()); 324 return; 325 } 326 327 // if App opened by Shell Script 328 if (!strcmp(mimetype, "text/x-haiku-shscript")){ 329 // Not implemented. 330 // beep(); 331 return; 332 } 333 } 334 335 336 status_t 337 TermApp::_MakeTermWindow(BRect &frame) 338 { 339 const char *encoding = gTermPref->getString(PREF_TEXT_ENCODING); 340 341 // Get encoding name (setenv TTYPE in spawn_shell functions) 342 const etable *p = encoding_table; 343 while (p->name) { 344 if (!strcmp(p->name, encoding)) { 345 encoding = p->shortname; 346 break; 347 } 348 p++; 349 } 350 351 const char *command = NULL; 352 if (CommandLine.Length() > 0) 353 command = CommandLine.String(); 354 else 355 command = gTermPref->getString(PREF_SHELL); 356 357 int rows = gTermPref->getInt32(PREF_ROWS); 358 if (rows < 1) 359 gTermPref->setInt32(PREF_ROWS, rows = 1); 360 361 int cols = gTermPref->getInt32(PREF_COLS); 362 if (cols < MIN_COLS) 363 gTermPref->setInt32(PREF_COLS, cols = MIN_COLS); 364 365 int pfd = spawn_shell(rows, cols, command, encoding); 366 if (pfd < 0) 367 return pfd; 368 369 fTermWindow = new TermWindow(frame, fWindowTitle.String(), pfd); 370 fTermWindow->Show(); 371 372 return B_OK; 373 } 374 375 376 void 377 TermApp::_ActivateTermWindow(team_id id) 378 { 379 BMessenger app(TERM_SIGNATURE, id); 380 if (app.IsTargetLocal()) 381 fTermWindow->Activate(); 382 else 383 app.SendMessage(MSG_ACTIVATE_TERM); 384 } 385 386 387 void 388 TermApp::_SwitchTerm() 389 { 390 team_id myId = be_app->Team(); // My id 391 BList teams; 392 be_roster->GetAppList(TERM_SIGNATURE, &teams); 393 int32 numTerms = teams.CountItems(); 394 395 if (numTerms <= 1 ) 396 return; //Can't Switch !! 397 398 // Find position of mine in app teams. 399 int32 i; 400 401 for (i = 0; i < numTerms; i++) { 402 if (myId == reinterpret_cast<team_id>(teams.ItemAt(i))) 403 break; 404 } 405 406 do { 407 if (--i < 0) 408 i = numTerms - 1; 409 } while (_IsMinimized(reinterpret_cast<team_id>(teams.ItemAt(i)))); 410 411 // Activate switched terminal. 412 _ActivateTermWindow(reinterpret_cast<team_id>(teams.ItemAt(i))); 413 } 414 415 416 bool 417 TermApp::_IsMinimized(team_id id) 418 { 419 BMessenger app(TERM_SIGNATURE, id); 420 if (app.IsTargetLocal()) 421 return fTermWindow->IsMinimized(); 422 423 BMessage reply; 424 if (app.SendMessage(MSG_TERM_IS_MINIMIZE, &reply) != B_OK) 425 return true; 426 427 bool hidden; 428 reply.FindBool("result", &hidden); 429 return hidden; 430 } 431 432 433 /*! 434 Checks if all teams that have an ID-to-team mapping in the message 435 are still running. 436 The IDs for teams that are gone will be made available again, and 437 their mapping is removed from the message. 438 */ 439 void 440 TermApp::_SanitizeIDs(BMessage* data, uint8* windows, ssize_t length) 441 { 442 BList teams; 443 be_roster->GetAppList(TERM_SIGNATURE, &teams); 444 445 for (int32 i = 0; i < length; i++) { 446 if (!windows[i]) 447 continue; 448 449 BString id("id-"); 450 id << i; 451 452 team_id team; 453 if (data->FindInt32(id.String(), &team) != B_OK) 454 continue; 455 456 if (!teams.HasItem((void*)team)) { 457 windows[i] = false; 458 data->RemoveName(id.String()); 459 } 460 } 461 } 462 463 464 /*! 465 Removes the current fWindowNumber (ID) from the supplied array, or 466 finds a free ID in it, and sets fWindowNumber accordingly. 467 */ 468 bool 469 TermApp::_UpdateIDs(bool set, uint8* windows, ssize_t maxLength, 470 ssize_t* _length) 471 { 472 ssize_t length = *_length; 473 474 if (set) { 475 int32 i; 476 for (i = 0; i < length; i++) { 477 if (!windows[i]) { 478 windows[i] = true; 479 fWindowNumber = i + 1; 480 break; 481 } 482 } 483 484 if (i == length) { 485 if (length >= maxLength) 486 return false; 487 488 windows[length] = true; 489 length++; 490 fWindowNumber = length; 491 } 492 } else { 493 // update information and write it back 494 windows[fWindowNumber - 1] = false; 495 } 496 497 *_length = length; 498 return true; 499 } 500 501 502 void 503 TermApp::_UpdateRegistration(bool set) 504 { 505 if (set) 506 fWindowNumber = -1; 507 else if (fWindowNumber < 0) 508 return; 509 510 #ifdef __HAIKU__ 511 // use BClipboard - it supports atomic access in Haiku 512 BClipboard clipboard(TERM_SIGNATURE); 513 514 while (true) { 515 if (!clipboard.Lock()) 516 return; 517 518 BMessage* data = clipboard.Data(); 519 520 const uint8* windowsData; 521 uint8 windows[512]; 522 ssize_t length; 523 if (data->FindData("ids", B_RAW_TYPE, 524 (const void**)&windowsData, &length) != B_OK) 525 length = 0; 526 527 if (length > (ssize_t)sizeof(windows)) 528 length = sizeof(windows); 529 if (length > 0) 530 memcpy(windows, windowsData, length); 531 532 _SanitizeIDs(data, windows, length); 533 534 status_t status = B_OK; 535 if (_UpdateIDs(set, windows, sizeof(windows), &length)) { 536 // add/remove our ID-to-team mapping 537 BString id("id-"); 538 id << fWindowNumber; 539 540 if (set) 541 data->AddInt32(id.String(), Team()); 542 else 543 data->RemoveName(id.String()); 544 545 data->RemoveName("ids"); 546 //if (data->ReplaceData("ids", B_RAW_TYPE, windows, length) != B_OK) 547 data->AddData("ids", B_RAW_TYPE, windows, length); 548 549 status = clipboard.Commit(true); 550 } 551 552 clipboard.Unlock(); 553 554 if (status == B_OK) 555 break; 556 } 557 #else // !__HAIKU__ 558 // use a file to store the IDs - unfortunately, locking 559 // doesn't work on BeOS either here 560 int fd = open("/tmp/terminal_ids", O_RDWR | O_CREAT); 561 if (fd < 0) 562 return; 563 564 struct flock lock; 565 lock.l_type = F_WRLCK; 566 lock.l_whence = SEEK_CUR; 567 lock.l_start = 0; 568 lock.l_len = -1; 569 fcntl(fd, F_SETLKW, &lock); 570 571 uint8 windows[512]; 572 ssize_t length = read_pos(fd, 0, windows, sizeof(windows)); 573 if (length < 0) { 574 close(fd); 575 return; 576 } 577 578 if (length > (ssize_t)sizeof(windows)) 579 length = sizeof(windows); 580 581 if (_UpdateIDs(set, windows, sizeof(windows), &length)) 582 write_pos(fd, 0, windows, length); 583 584 close(fd); 585 #endif // !__HAIKU__ 586 } 587 588 589 void 590 TermApp::_UnregisterTerminal() 591 { 592 _UpdateRegistration(false); 593 } 594 595 596 void 597 TermApp::_RegisterTerminal() 598 { 599 _UpdateRegistration(true); 600 } 601 602 603 //#ifndef B_NETPOSITIVE_APP_SIGNATURE 604 //#define B_NETPOSITIVE_APP_SIGNATURE "application/x-vnd.Be-NPOS" 605 //#endif 606 // 607 //void 608 //TermApp::ShowHTML(BMessage *msg) 609 //{ 610 // const char *url; 611 // msg->FindString("Url", &url); 612 // BMessage message; 613 // 614 // message.what = B_NETPOSITIVE_OPEN_URL; 615 // message.AddString("be:url", url); 616 617 // be_roster->Launch(B_NETPOSITIVE_APP_SIGNATURE, &message); 618 // while(!(be_roster->IsRunning(B_NETPOSITIVE_APP_SIGNATURE))) 619 // snooze(10000); 620 // 621 // // Activate net+ 622 // be_roster->ActivateApp(be_roster->TeamFor(B_NETPOSITIVE_APP_SIGNATURE)); 623 //} 624 625 626 void 627 TermApp::_Usage(char *name) 628 { 629 fprintf(stderr, "Haiku Terminal\n" 630 "Copyright 2001-2007 Haiku, Inc.\n" 631 "Copyright(C) 1999 Kazuho Okui and Takashi Murai.\n" 632 "\n" 633 "Usage: %s [OPTION] [SHELL]\n", name); 634 635 fprintf(stderr, " -p, --preference load preference file\n" 636 " -t, --title set window title\n" 637 " -geom, --geometry set window geometry\n" 638 " An example of geometry is \"80x25+100+100\"\n"); 639 #if 0 640 fprintf(stderr, " -fg, --text-fore-color set window foreground color\n" 641 " -bg, --text-back-color set window background color\n" 642 " -curfg, --cursor-fore-color set cursor foreground color\n" 643 " -curbg, --cursor-back-color set cursor background color\n" 644 " -selfg, --select-fore-color set selection area foreground color\n" 645 " -selbg, --select-back-color set selection area background color\n" 646 " Examples of color are \"#FF00FF\" and \"purple\"\n"); 647 #endif 648 } 649 650 651 int 652 text_to_rgb(char *name, rgb_color *color, char *buffer) 653 { 654 if (name[0] != '#') { 655 // Convert from /etc/rgb.txt. 656 BString inStr(buffer); 657 int32 point, offset = 0; 658 659 // Search color name 660 do { 661 point = inStr.FindFirst(name, offset); 662 if (point < 0) 663 return false; 664 offset = point + 1; 665 } while(*(buffer + point -1) != '\t'); 666 667 char *p = buffer + point; 668 while (*p != '\n') 669 p--; 670 p++; 671 672 if (sscanf(p, "%d %d %d", (int *)&color->red, (int *)&color->green, 673 (int *)&color->blue) == EOF) 674 return false; 675 676 color->alpha = 0; 677 } else if (name[0] == '#') { 678 // Convert from #RRGGBB format 679 sscanf(name, "#%2x%2x%2x", (int *)&color->red, (int *)&color->green, 680 (int *)&color->blue); 681 color->alpha = 0; 682 } else 683 return false; 684 685 return true; 686 } 687 688 // This routine copy from GNU Emacs. 689 // TODO: This might be a GPL licensing issue here. Investigate. 690 int 691 argmatch(char **argv, int argc, char *sstr, char *lstr, 692 int minlen, char **valptr, int *skipptr) 693 { 694 char *p = 0; 695 int arglen; 696 char *arg; 697 698 // Don't access argv[argc]; give up in advance 699 if (argc <= *skipptr + 1) 700 return 0; 701 702 arg = argv[*skipptr+1]; 703 if (arg == NULL) 704 return 0; 705 706 if (strcmp(arg, sstr) == 0) { 707 if(valptr != NULL) { 708 *valptr = argv[*skipptr+2]; 709 *skipptr += 2; 710 } else 711 *skipptr += 1; 712 return 1; 713 } 714 715 arglen =(valptr != NULL &&(p = strchr(arg, '=')) != NULL 716 ? p - arg : strlen(arg)); 717 718 if(lstr == 0 || arglen < minlen || strncmp(arg, lstr, arglen) != 0) 719 return 0; 720 else 721 if(valptr == NULL) 722 { 723 *skipptr += 1; 724 return 1; 725 } 726 else 727 if(p != NULL) 728 { 729 *valptr = p+1; 730 *skipptr += 1; 731 return 1; 732 } 733 else 734 if(argv[*skipptr+2] != NULL) 735 { 736 *valptr = argv[*skipptr+2]; 737 *skipptr += 2; 738 return 1; 739 } 740 else 741 { 742 return 0; 743 } 744 } 745 746 // This routine copy from GNU Emacs. 747 // TODO: This might be a GPL licensing issue here. Investigate. 748 void 749 sort_args(int argc, char **argv) 750 { 751 /* 752 For each element of argv, 753 the corresponding element of options is: 754 0 for an option that takes no arguments, 755 1 for an option that takes one argument, etc. 756 -1 for an ordinary non-option argument. 757 */ 758 759 char **newargv =(char **) malloc(sizeof(char *) * argc); 760 761 int *options =(int *) malloc(sizeof(int) * argc); 762 int *priority =(int *) malloc(sizeof(int) * argc); 763 int to = 1; 764 int incoming_used = 1; 765 int from; 766 int i; 767 //int end_of_options = argc; 768 769 // Categorize all the options, 770 // and figure out which argv elts are option arguments 771 for(from = 1; from < argc; from++) 772 { 773 options[from] = -1; 774 priority[from] = 0; 775 if(argv[from][0] == '-') 776 { 777 int match, thislen; 778 char *equals; 779 780 // If we have found "--", don't consider any more arguments as options 781 if(argv[from][1] == '-' && argv[from][2] == 0) 782 { 783 // Leave the "--", and everything following it, at the end. 784 for(; from < argc; from++) 785 { 786 priority[from] = -100; 787 options[from] = -1; 788 } 789 break; 790 } 791 792 // Look for a match with a known old-fashioned option. 793 for(i = 0; i <(int)(sizeof(standard_args) / sizeof(standard_args[0])); i++) 794 if(!strcmp(argv[from], standard_args[i].name)) 795 { 796 options[from] = standard_args[i].nargs; 797 priority[from] = standard_args[i].priority; 798 if(from + standard_args[i].nargs >= argc) 799 fprintf(stderr, "Option `%s' requires an argument\n", argv[from]); 800 from += standard_args[i].nargs; 801 goto done; 802 } 803 804 /* 805 Look for a match with a known long option. 806 MATCH is -1 if no match so far, -2 if two or more matches so far, 807 >= 0(the table index of the match) if just one match so far. 808 */ 809 if(argv[from][1] == '-') 810 { 811 match = -1; 812 thislen = strlen(argv[from]); 813 equals = strchr(argv[from], '='); 814 if(equals != 0) 815 thislen = equals - argv[from]; 816 817 for(i = 0;i <(int )(sizeof(standard_args) / sizeof(standard_args[0])); i++) 818 if(standard_args[i].longname 819 && !strncmp(argv[from], standard_args[i].longname, thislen)) 820 { 821 if(match == -1) 822 match = i; 823 else 824 match = -2; 825 } 826 827 // If we found exactly one match, use that 828 if(match >= 0) 829 { 830 options[from] = standard_args[match].nargs; 831 priority[from] = standard_args[match].priority; 832 833 // If --OPTION=VALUE syntax is used, 834 // this option uses just one argv element 835 if(equals != 0) 836 options[from] = 0; 837 if(from + options[from] >= argc) 838 fprintf(stderr, "Option `%s' requires an argument\n", argv[from]); 839 from += options[from]; 840 } 841 } 842 done: ; 843 } 844 } 845 846 // Copy the arguments, in order of decreasing priority, to NEW 847 newargv[0] = argv[0]; 848 while (incoming_used < argc) { 849 int best = -1; 850 int best_priority = -9999; 851 852 // Find the highest priority remaining option. 853 // If several have equal priority, take the first of them. 854 for (from = 1; from < argc; from++) { 855 if (argv[from] != 0 && priority[from] > best_priority) { 856 best_priority = priority[from]; 857 best = from; 858 } 859 860 // Skip option arguments--they are tied to the options. 861 if (options[from] > 0) 862 from += options[from]; 863 } 864 865 if (best < 0) 866 abort(); 867 868 // Copy the highest priority remaining option, with its args, to NEW. 869 // Unless it is a duplicate of the previous one 870 if (!(options[best] == 0 && ! strcmp(newargv[to - 1], argv[best]))) { 871 newargv[to++] = argv[best]; 872 for(i = 0; i < options[best]; i++) 873 newargv[to++] = argv[best + i + 1]; 874 } 875 876 incoming_used += 1 +(options[best] > 0 ? options[best] : 0); 877 878 // Clear out this option in ARGV 879 argv[best] = 0; 880 for (i = 0; i < options[best]; i++) 881 argv[best + i + 1] = 0; 882 } 883 884 // If duplicate options were deleted, fill up extra space with null ptrs 885 while (to < argc) 886 newargv[to++] = 0; 887 888 memcpy(argv, newargv, sizeof(char *) * argc); 889 890 free(options); 891 free(newargv); 892 free(priority); 893 } 894