1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 2 /* */ 3 /* This file is part of the program and library */ 4 /* SCIP --- Solving Constraint Integer Programs */ 5 /* */ 6 /* Copyright (c) 2002-2023 Zuse Institute Berlin (ZIB) */ 7 /* */ 8 /* Licensed under the Apache License, Version 2.0 (the "License"); */ 9 /* you may not use this file except in compliance with the License. */ 10 /* You may obtain a copy of the License at */ 11 /* */ 12 /* http://www.apache.org/licenses/LICENSE-2.0 */ 13 /* */ 14 /* Unless required by applicable law or agreed to in writing, software */ 15 /* distributed under the License is distributed on an "AS IS" BASIS, */ 16 /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ 17 /* See the License for the specific language governing permissions and */ 18 /* limitations under the License. */ 19 /* */ 20 /* You should have received a copy of the Apache-2.0 license */ 21 /* along with SCIP; see the file LICENSE. If not visit scipopt.org. */ 22 /* */ 23 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 24 25 /**@file dialog.c 26 * @ingroup OTHER_CFILES 27 * @brief methods for user interface dialog 28 * @author Tobias Achterberg 29 */ 30 31 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/ 32 33 #include <assert.h> 34 #include <string.h> 35 #include <ctype.h> 36 37 #include "scip/scip.h" 38 #include "scip/def.h" 39 #include "blockmemshell/memory.h" 40 #include "scip/set.h" 41 #include "scip/pub_misc.h" 42 #include "scip/dialog.h" 43 44 #include "scip/struct_dialog.h" 45 46 #ifdef SCIP_WITH_READLINE 47 #include <stdio.h> 48 #include <readline/readline.h> 49 #include <readline/history.h> 50 #endif 51 52 53 54 /* 55 * read line methods 56 */ 57 58 #ifdef SCIP_WITH_READLINE 59 60 /** reads a line of input from stdin */ 61 static 62 SCIP_RETCODE readLine( 63 SCIP_DIALOGHDLR* dialoghdlr, /**< dialog handler */ 64 const char* prompt, /**< prompt to display */ 65 SCIP_Bool* endoffile /**< pointer to store whether the end of the input file was reached */ 66 ) 67 { 68 char* s; 69 70 assert(endoffile != NULL); 71 72 s = readline(prompt); 73 if( s != NULL ) 74 { 75 (void)SCIPstrncpy(&dialoghdlr->buffer[dialoghdlr->bufferpos], s, dialoghdlr->buffersize - dialoghdlr->bufferpos); 76 free(s); 77 *endoffile = FALSE; 78 } 79 else 80 *endoffile = TRUE; 81 82 return SCIP_OKAY; 83 } 84 85 /** puts the given string on the command history */ 86 static 87 SCIP_RETCODE addHistory( 88 const char* s /**< string to add to the command history */ 89 ) 90 { 91 add_history(s); 92 93 return SCIP_OKAY; 94 } 95 96 /** returns the current length of the history list */ 97 static 98 int getHistoryLength( 99 void 100 ) 101 { 102 #ifndef NO_REMOVE_HISTORY 103 return history_length; 104 #else 105 return 0; 106 #endif 107 } 108 109 /** removes a single element from the history list */ 110 static 111 SCIP_RETCODE removeHistory( 112 int pos /**< list position of history entry to remove */ 113 ) 114 { 115 #ifndef NO_REMOVE_HISTORY 116 HIST_ENTRY* entry; 117 118 entry = remove_history(pos); 119 120 /* Free readline/history storage: there seem to be differences in the versions (and the amount of 121 * data to be freed). The following should be a good approximation; if it doesn't work define 122 * NO_REMOVE_HISTORY - see the INSTALL file. This will produce minor memory leaks. 123 */ 124 #if RL_VERSION_MAJOR >= 5 125 (void)free_history_entry(entry); 126 #else 127 if( entry != NULL ) 128 { 129 free((void*)entry->line); 130 free(entry); 131 } 132 #endif 133 #endif 134 135 return SCIP_OKAY; 136 } 137 138 /** writes command history into file of the specified name */ 139 static 140 SCIP_RETCODE writeHistory( 141 const char* filename /**< name of file to (over)write history to */ 142 ) 143 { 144 int retval = write_history(filename); 145 146 if( retval == 0 ) 147 return SCIP_OKAY; 148 else 149 return SCIP_FILECREATEERROR; 150 } 151 152 #else 153 154 /** reads a line of input from stdin */ 155 static 156 SCIP_RETCODE readLine( 157 SCIP_DIALOGHDLR* dialoghdlr, /**< dialog handler */ 158 const char* prompt, /**< prompt to display */ 159 SCIP_Bool* endoffile /**< pointer to store whether the end of the input file was reached */ 160 ) 161 { 162 char* s; 163 164 assert(dialoghdlr != NULL); 165 assert(dialoghdlr->buffer != NULL); 166 assert(dialoghdlr->bufferpos < dialoghdlr->buffersize); 167 assert(dialoghdlr->buffer[dialoghdlr->bufferpos] == '\0'); 168 assert(endoffile != NULL); 169 170 /* check for EOF (due to CTRL-D or unexpected end of piped-in file) */ 171 if( feof(stdin) ) 172 *endoffile = TRUE; 173 else 174 { 175 char* result; 176 177 /* display prompt */ 178 printf("%s", prompt); 179 180 /* read line from stdin */ 181 result = fgets(&dialoghdlr->buffer[dialoghdlr->bufferpos], dialoghdlr->buffersize - dialoghdlr->bufferpos, stdin); 182 assert(result != NULL); 183 (void) result; /* disable compiler warning [-Wunused-result] */ 184 185 /* replace newline with \0 */ 186 s = strchr(&dialoghdlr->buffer[dialoghdlr->bufferpos], '\n'); 187 if( s != NULL ) 188 *s = '\0'; 189 *endoffile = FALSE; 190 } 191 192 return SCIP_OKAY; 193 } 194 195 /** puts the given string on the command history */ /*lint -e715*/ 196 static 197 SCIP_RETCODE addHistory( 198 const char* s /**< string to add to the command history */ 199 ) 200 { /*lint --e{715}*/ 201 /* nothing to do here */ 202 return SCIP_OKAY; 203 } 204 205 /** returns the current length of the history list */ 206 static 207 int getHistoryLength( 208 void 209 ) 210 { 211 return 0; 212 } 213 214 /** removes a single element from the history list */ /*lint -e715*/ 215 static 216 SCIP_RETCODE removeHistory( 217 int pos /**< list position of history entry to remove */ 218 ) 219 { /*lint --e{715}*/ 220 /* nothing to do here */ 221 return SCIP_OKAY; 222 } 223 224 225 /** writes command history into file of the specified name */ 226 static 227 SCIP_RETCODE writeHistory( 228 const char* filename /**< name of file to (over)write history to */ 229 ) 230 { /*lint --e{715}*/ 231 assert(filename != NULL); 232 233 /* nothing to do here */ 234 return SCIP_OKAY; 235 } 236 237 #endif 238 239 /** frees a single linelist entry, but not its successors */ 240 static 241 void linelistFree( 242 SCIP_LINELIST** linelist /**< pointer to line list */ 243 ) 244 { 245 assert(linelist != NULL); 246 247 BMSfreeMemoryArray(&(*linelist)->inputline); 248 BMSfreeMemory(linelist); 249 } 250 251 /** frees a linelist entry and all of its successors */ 252 static 253 void linelistFreeAll( 254 SCIP_LINELIST** linelist /**< pointer to line list */ 255 ) 256 { 257 assert(linelist != NULL); 258 259 while( *linelist != NULL ) 260 { 261 SCIP_LINELIST* nextline; 262 263 nextline = (*linelist)->nextline; 264 linelistFree(linelist); 265 *linelist = nextline; 266 } 267 } 268 269 /** reads a line of input from stdin or from the stored input lines in the input list */ 270 static 271 SCIP_RETCODE readInputLine( 272 SCIP_DIALOGHDLR* dialoghdlr, /**< dialog handler */ 273 const char* prompt, /**< prompt to display */ 274 SCIP_Bool* endoffile /**< pointer to store whether the end of the input file was reached */ 275 ) 276 { 277 assert(dialoghdlr != NULL); 278 assert(dialoghdlr->buffer != NULL); 279 assert(dialoghdlr->bufferpos < dialoghdlr->buffersize); 280 assert(dialoghdlr->buffer[dialoghdlr->bufferpos] == '\0'); 281 assert(endoffile != NULL); 282 283 *endoffile = FALSE; 284 285 if( dialoghdlr->inputlist == NULL ) 286 { 287 /* read a line from stdin */ 288 SCIP_CALL( readLine(dialoghdlr, prompt, endoffile) ); 289 } 290 else 291 { 292 SCIP_LINELIST* nextline; 293 294 /* copy the next input line into the input buffer */ 295 (void)SCIPstrncpy(&dialoghdlr->buffer[dialoghdlr->bufferpos], dialoghdlr->inputlist->inputline, dialoghdlr->buffersize - dialoghdlr->bufferpos); 296 297 /* free the input line */ 298 nextline = dialoghdlr->inputlist->nextline; 299 if( dialoghdlr->inputlistptr == &(dialoghdlr->inputlist->nextline) ) 300 dialoghdlr->inputlistptr = &dialoghdlr->inputlist; 301 linelistFree(&dialoghdlr->inputlist); 302 dialoghdlr->inputlist = nextline; 303 assert(dialoghdlr->inputlistptr != NULL); 304 assert(*dialoghdlr->inputlistptr == NULL); 305 } 306 307 return SCIP_OKAY; 308 } 309 310 311 312 313 /* 314 * dialog handler 315 */ 316 317 /** copies the given dialog to a new scip */ 318 SCIP_RETCODE SCIPdialogCopyInclude( 319 SCIP_DIALOG* dialog, /**< dialog */ 320 SCIP_SET* set /**< SCIP_SET of SCIP to copy to */ 321 ) 322 { 323 assert(dialog != NULL); 324 assert(set != NULL); 325 assert(set->scip != NULL); 326 327 if( dialog->dialogcopy != NULL ) 328 { 329 SCIPsetDebugMsg(set, "including dialog %s in subscip %p\n", SCIPdialogGetName(dialog), (void*)set->scip); 330 SCIP_CALL( dialog->dialogcopy(set->scip, dialog) ); 331 } 332 return SCIP_OKAY; 333 } 334 335 /** creates a dialog handler */ 336 SCIP_RETCODE SCIPdialoghdlrCreate( 337 SCIP_SET* set, /**< global SCIP settings */ 338 SCIP_DIALOGHDLR** dialoghdlr /**< pointer to store dialog handler */ 339 ) 340 { /*lint --e{715}*/ 341 #ifdef SCIP_WITH_READLINE 342 char readlineversion[20]; 343 #endif 344 345 assert(set != NULL); 346 assert(dialoghdlr != NULL); 347 348 SCIP_ALLOC( BMSallocMemory(dialoghdlr) ); 349 (*dialoghdlr)->rootdialog = NULL; 350 (*dialoghdlr)->inputlist = NULL; 351 (*dialoghdlr)->inputlistptr = &(*dialoghdlr)->inputlist; 352 (*dialoghdlr)->buffersize = SCIP_MAXSTRLEN; 353 (*dialoghdlr)->nprotectedhistelems = -1; 354 SCIP_ALLOC( BMSallocMemoryArray(&(*dialoghdlr)->buffer, (*dialoghdlr)->buffersize) ); 355 356 SCIPdialoghdlrClearBuffer(*dialoghdlr); 357 358 #ifdef SCIP_WITH_READLINE 359 (void) SCIPsnprintf(readlineversion, (int)sizeof(readlineversion), "Readline %s", rl_library_version); 360 SCIP_CALL( SCIPsetIncludeExternalCode(set, readlineversion, "GNU library for command line editing (gnu.org/s/readline)") ); 361 #endif 362 363 return SCIP_OKAY; 364 } 365 366 /** frees a dialog handler and it's dialog tree */ 367 SCIP_RETCODE SCIPdialoghdlrFree( 368 SCIP* scip, /**< SCIP data structure */ 369 SCIP_DIALOGHDLR** dialoghdlr /**< pointer to dialog handler */ 370 ) 371 { 372 assert(dialoghdlr != NULL); 373 if( *dialoghdlr == NULL ) 374 return SCIP_OKAY; 375 376 SCIP_CALL( SCIPdialoghdlrSetRoot(scip, *dialoghdlr, NULL) ); 377 linelistFreeAll(&(*dialoghdlr)->inputlist); 378 BMSfreeMemoryArray(&(*dialoghdlr)->buffer); 379 BMSfreeMemory(dialoghdlr); 380 381 return SCIP_OKAY; 382 } 383 384 /** executes the root dialog of the dialog handler */ 385 SCIP_RETCODE SCIPdialoghdlrExec( 386 SCIP_DIALOGHDLR* dialoghdlr, /**< dialog handler */ 387 SCIP_SET* set /**< global SCIP settings */ 388 ) 389 { 390 SCIP_DIALOG* dialog; 391 392 assert(dialoghdlr != NULL); 393 assert(dialoghdlr->buffer != NULL); 394 395 /* clear the buffer, start with the root dialog */ 396 SCIPdialoghdlrClearBuffer(dialoghdlr); 397 dialog = dialoghdlr->rootdialog; 398 399 /* execute dialogs until a NULL is returned as next dialog */ 400 while( dialog != NULL ) 401 { 402 SCIP_CALL( SCIPdialogExec(dialog, set, dialoghdlr, &dialog) ); 403 404 /* reset buffer, it is was consumed completely */ 405 if( dialoghdlr->buffer[dialoghdlr->bufferpos] == '\0' ) 406 SCIPdialoghdlrClearBuffer(dialoghdlr); 407 } 408 409 return SCIP_OKAY; 410 } 411 412 /** makes given dialog the root dialog of dialog handler; captures dialog and releases former root dialog */ 413 SCIP_RETCODE SCIPdialoghdlrSetRoot( 414 SCIP* scip, /**< SCIP data structure */ 415 SCIP_DIALOGHDLR* dialoghdlr, /**< dialog handler */ 416 SCIP_DIALOG* dialog /**< dialog to be the root */ 417 ) 418 { 419 assert(dialoghdlr != NULL); 420 421 if( dialoghdlr->rootdialog != NULL ) 422 { 423 SCIP_CALL( SCIPdialogRelease(scip, &dialoghdlr->rootdialog) ); 424 } 425 assert(dialoghdlr->rootdialog == NULL); 426 427 dialoghdlr->rootdialog = dialog; 428 429 if( dialog != NULL ) 430 SCIPdialogCapture(dialog); 431 432 return SCIP_OKAY; 433 } 434 435 /** returns the root dialog of the dialog handler */ 436 SCIP_DIALOG* SCIPdialoghdlrGetRoot( 437 SCIP_DIALOGHDLR* dialoghdlr /**< dialog handler */ 438 ) 439 { 440 assert(dialoghdlr != NULL); 441 442 return dialoghdlr->rootdialog; 443 } 444 445 /** clears the input command buffer of the dialog handler */ 446 void SCIPdialoghdlrClearBuffer( 447 SCIP_DIALOGHDLR* dialoghdlr /**< dialog handler */ 448 ) 449 { 450 assert(dialoghdlr != NULL); 451 452 dialoghdlr->buffer[0] = '\0'; 453 dialoghdlr->bufferpos = 0; 454 } 455 456 /** returns TRUE iff input command buffer is empty */ 457 SCIP_Bool SCIPdialoghdlrIsBufferEmpty( 458 SCIP_DIALOGHDLR* dialoghdlr /**< dialog handler */ 459 ) 460 { 461 assert(dialoghdlr != NULL); 462 assert(dialoghdlr->bufferpos < dialoghdlr->buffersize); 463 464 return (dialoghdlr->buffer[dialoghdlr->bufferpos] == '\0'); 465 } 466 467 /** returns the next line in the handler's command buffer; if the buffer is empty, displays the given prompt or the 468 * current dialog's path and asks the user for further input; the user must not free or modify the returned string 469 */ 470 SCIP_RETCODE SCIPdialoghdlrGetLine( 471 SCIP_DIALOGHDLR* dialoghdlr, /**< dialog handler */ 472 SCIP_DIALOG* dialog, /**< current dialog */ 473 const char* prompt, /**< prompt to display, or NULL to display the current dialog's path */ 474 char** inputline, /**< pointer to store the complete line in the handler's command buffer */ 475 SCIP_Bool* endoffile /**< pointer to store whether the end of the input file was reached */ 476 ) 477 { 478 char path[SCIP_MAXSTRLEN+1]; 479 char p[SCIP_MAXSTRLEN]; 480 481 assert(dialoghdlr != NULL); 482 assert(dialoghdlr->buffer != NULL); 483 assert(dialoghdlr->bufferpos < dialoghdlr->buffersize); 484 assert(inputline != NULL); 485 486 /* get input from the user, if the buffer is empty */ 487 if( SCIPdialoghdlrIsBufferEmpty(dialoghdlr) ) 488 { 489 int len; 490 491 /* clear the buffer */ 492 SCIPdialoghdlrClearBuffer(dialoghdlr); 493 494 if( prompt == NULL ) 495 { 496 /* use current dialog's path as prompt */ 497 SCIPdialogGetPath(dialog, '/', path); 498 (void) SCIPsnprintf(p, SCIP_MAXSTRLEN, "%s> ", path); 499 prompt = p; 500 } 501 502 /* read command line from stdin or from the input line list */ 503 SCIP_CALL( readInputLine(dialoghdlr, prompt, endoffile) ); 504 505 /* strip trailing spaces */ 506 len = (int)strlen(&dialoghdlr->buffer[dialoghdlr->bufferpos]); 507 if( len > 0 ) 508 { 509 while( isspace((unsigned char)dialoghdlr->buffer[dialoghdlr->bufferpos + len - 1]) ) 510 { 511 dialoghdlr->buffer[dialoghdlr->bufferpos + len - 1] = '\0'; 512 len--; 513 } 514 } 515 516 /* insert command in command history */ 517 if( dialoghdlr->buffer[dialoghdlr->bufferpos] != '\0' ) 518 { 519 SCIP_CALL( SCIPdialoghdlrAddHistory(dialoghdlr, NULL, &dialoghdlr->buffer[dialoghdlr->bufferpos], FALSE) ); 520 } 521 } 522 523 /* the last character in the buffer must be a '\0' */ 524 dialoghdlr->buffer[dialoghdlr->buffersize-1] = '\0'; 525 526 /* skip leading spaces: find start of first word */ 527 while( isspace((unsigned char)dialoghdlr->buffer[dialoghdlr->bufferpos]) ) 528 dialoghdlr->bufferpos++; 529 530 /* copy the complete line */ 531 *inputline = &dialoghdlr->buffer[dialoghdlr->bufferpos]; 532 533 /* go to the end of the line */ 534 dialoghdlr->bufferpos += (int)strlen(&dialoghdlr->buffer[dialoghdlr->bufferpos]); 535 536 if( dialoghdlr->buffer[dialoghdlr->buffersize-1] == '\0' ) 537 *endoffile = TRUE; 538 539 return SCIP_OKAY; 540 } 541 542 543 /** returns the next word in the handler's command buffer; if the buffer is empty, displays the given prompt or the 544 * current dialog's path and asks the user for further input; the user must not free or modify the returned string 545 */ 546 SCIP_RETCODE SCIPdialoghdlrGetWord( 547 SCIP_DIALOGHDLR* dialoghdlr, /**< dialog handler */ 548 SCIP_DIALOG* dialog, /**< current dialog */ 549 const char* prompt, /**< prompt to display, or NULL to display the current dialog's path */ 550 char** inputword, /**< pointer to store the next word in the handler's command buffer */ 551 SCIP_Bool* endoffile /**< pointer to store whether the end of the input file was reached */ 552 ) 553 { 554 char path[SCIP_MAXSTRLEN+1]; 555 char p[SCIP_MAXSTRLEN]; 556 char* firstword; 557 int pos; 558 559 assert(dialoghdlr != NULL); 560 assert(dialoghdlr->buffer != NULL); 561 assert(dialoghdlr->bufferpos < dialoghdlr->buffersize); 562 assert(inputword != NULL); 563 assert(endoffile != NULL); 564 565 *endoffile = FALSE; 566 567 /* get input from the user, if the buffer is empty */ 568 if( SCIPdialoghdlrIsBufferEmpty(dialoghdlr) ) 569 { 570 int len; 571 572 /* clear the buffer */ 573 SCIPdialoghdlrClearBuffer(dialoghdlr); 574 575 if( prompt == NULL ) 576 { 577 /* use current dialog's path as prompt */ 578 SCIPdialogGetPath(dialog, '/', path); 579 (void) SCIPsnprintf(p, SCIP_MAXSTRLEN, "%s> ", path); 580 prompt = p; 581 } 582 583 /* read command line from stdin or from the input line list */ 584 SCIP_CALL( readInputLine(dialoghdlr, prompt, endoffile) ); 585 586 /* strip trailing spaces */ 587 len = (int)strlen(&dialoghdlr->buffer[dialoghdlr->bufferpos]); 588 if( len > 0 ) 589 { 590 while( isspace((unsigned char)dialoghdlr->buffer[dialoghdlr->bufferpos + len - 1]) ) 591 { 592 dialoghdlr->buffer[dialoghdlr->bufferpos + len - 1] = '\0'; 593 len--; 594 } 595 } 596 597 /* insert command in command history */ 598 if( dialoghdlr->buffer[dialoghdlr->bufferpos] != '\0' ) 599 { 600 SCIP_CALL( SCIPdialoghdlrAddHistory(dialoghdlr, NULL, &dialoghdlr->buffer[dialoghdlr->bufferpos], FALSE) ); 601 } 602 } 603 604 /* the last character in the buffer must be a '\0' */ 605 dialoghdlr->buffer[dialoghdlr->buffersize-1] = '\0'; 606 607 /* skip leading spaces: find start of first word */ 608 while( isspace((unsigned char)dialoghdlr->buffer[dialoghdlr->bufferpos]) ) 609 dialoghdlr->bufferpos++; 610 firstword = &dialoghdlr->buffer[dialoghdlr->bufferpos]; 611 612 pos = dialoghdlr->bufferpos; 613 while( dialoghdlr->buffer[dialoghdlr->bufferpos] != '\0' && !isspace((unsigned char)dialoghdlr->buffer[dialoghdlr->bufferpos]) ) 614 { 615 assert(pos <= dialoghdlr->bufferpos); 616 617 switch( dialoghdlr->buffer[dialoghdlr->bufferpos] ) 618 { 619 case '"': 620 dialoghdlr->bufferpos++; 621 /* read characters as they are until the next " */ 622 while( dialoghdlr->buffer[dialoghdlr->bufferpos] != '\0' && dialoghdlr->buffer[dialoghdlr->bufferpos] != '"' ) 623 { 624 /* watch out for \" and \\ which should be treated as " and \, respectively */ 625 if( dialoghdlr->buffer[dialoghdlr->bufferpos] == '\\' 626 && (dialoghdlr->buffer[dialoghdlr->bufferpos+1] == '"' 627 || dialoghdlr->buffer[dialoghdlr->bufferpos+1] == '\\') ) 628 { 629 dialoghdlr->bufferpos++; 630 } 631 dialoghdlr->buffer[pos] = dialoghdlr->buffer[dialoghdlr->bufferpos]; 632 pos++; 633 dialoghdlr->bufferpos++; 634 } 635 if( dialoghdlr->buffer[dialoghdlr->bufferpos] == '"' ) 636 dialoghdlr->bufferpos++; /* skip final " */ 637 break; 638 case '\'': 639 dialoghdlr->bufferpos++; 640 /* read characters as they are until the next ' */ 641 while( dialoghdlr->buffer[dialoghdlr->bufferpos] != '\0' && dialoghdlr->buffer[dialoghdlr->bufferpos] != '\'' ) 642 { 643 /* watch out for \' and \\ which should be treated as ' and \, respectively */ 644 if( dialoghdlr->buffer[dialoghdlr->bufferpos] == '\\' 645 && (dialoghdlr->buffer[dialoghdlr->bufferpos+1] == '\'' 646 || dialoghdlr->buffer[dialoghdlr->bufferpos+1] == '\\') ) 647 { 648 dialoghdlr->bufferpos++; 649 } 650 dialoghdlr->buffer[pos] = dialoghdlr->buffer[dialoghdlr->bufferpos]; 651 pos++; 652 dialoghdlr->bufferpos++; 653 } 654 if( dialoghdlr->buffer[dialoghdlr->bufferpos] == '\'' ) 655 dialoghdlr->bufferpos++; /* skip final ' */ 656 break; 657 case '\\': 658 /* if the next character is a space, a ", or a ', read next character as it is; 659 * otherwise, treat the \ as normal character 660 */ 661 if( dialoghdlr->buffer[dialoghdlr->bufferpos+1] == ' ' 662 || dialoghdlr->buffer[dialoghdlr->bufferpos+1] == '"' 663 || dialoghdlr->buffer[dialoghdlr->bufferpos+1] == '\'' ) 664 { 665 dialoghdlr->bufferpos++; 666 } 667 /*lint -fallthrough*/ 668 default: 669 dialoghdlr->buffer[pos] = dialoghdlr->buffer[dialoghdlr->bufferpos]; 670 pos++; 671 dialoghdlr->bufferpos++; 672 break; 673 } 674 } 675 assert(pos <= dialoghdlr->bufferpos); 676 677 /* move buffer to the next position */ 678 if( dialoghdlr->buffer[dialoghdlr->bufferpos] != '\0' ) 679 dialoghdlr->bufferpos++; 680 681 /* truncate the command word in the buffer */ 682 if( dialoghdlr->buffer[pos] != '\0' ) 683 dialoghdlr->buffer[pos] = '\0'; 684 685 /* remove additional spaces */ 686 while( isspace((unsigned char)dialoghdlr->buffer[dialoghdlr->bufferpos]) ) 687 dialoghdlr->bufferpos++; 688 689 *inputword = firstword; 690 691 SCIPdebugMessage("next word: <%s>\n", *inputword); 692 693 return SCIP_OKAY; 694 } 695 696 /** adds a single line of input to the dialog handler which is treated as if the user entered the command line */ 697 SCIP_RETCODE SCIPdialoghdlrAddInputLine( 698 SCIP_DIALOGHDLR* dialoghdlr, /**< dialog handler */ 699 const char* inputline /**< input line to add */ 700 ) 701 { 702 SCIP_LINELIST* linelist; 703 SCIP_RETCODE retcode = SCIP_OKAY; 704 705 assert(dialoghdlr != NULL); 706 assert(dialoghdlr->inputlistptr != NULL); 707 assert(*dialoghdlr->inputlistptr == NULL); 708 assert(inputline != NULL); 709 710 SCIP_ALLOC( BMSallocMemory(&linelist) ); 711 SCIP_ALLOC_TERMINATE( retcode, BMSduplicateMemoryArray(&linelist->inputline, inputline, strlen(inputline)+1), TERMINATE ); 712 linelist->nextline = NULL; 713 *dialoghdlr->inputlistptr = linelist; 714 dialoghdlr->inputlistptr = &linelist->nextline; 715 716 TERMINATE: 717 if( retcode != SCIP_OKAY ) 718 BMSfreeMemory(&linelist); 719 720 return retcode; 721 } 722 723 /** adds a command to the command history of the dialog handler; if a dialog is given, the command is preceeded 724 * by the dialog's command path; if no command is given, only the path to the dialog is added to the command history 725 */ 726 SCIP_RETCODE SCIPdialoghdlrAddHistory( 727 SCIP_DIALOGHDLR* dialoghdlr, /**< dialog handler */ 728 SCIP_DIALOG* dialog, /**< current dialog, or NULL */ 729 const char* command, /**< command string to add to the command history, or NULL */ 730 SCIP_Bool escapecommand /**< should special characters in command be prefixed by an escape char? */ 731 ) 732 { 733 char s[SCIP_MAXSTRLEN]; 734 char h[SCIP_MAXSTRLEN+1]; 735 SCIP_Bool cleanuphistory; 736 737 assert(dialoghdlr != NULL); 738 739 /* the current history list should be cleaned up if a dialog is given (i.e. the command is not partial) */ 740 cleanuphistory = (dialog != NULL); 741 742 /* generate the string to add to the history */ 743 s[SCIP_MAXSTRLEN-1] = '\0'; 744 745 if( command != NULL ) 746 { 747 if( escapecommand ) 748 SCIPescapeString(h, SCIP_MAXSTRLEN, command); 749 else 750 (void)SCIPstrncpy(h, command, SCIP_MAXSTRLEN); 751 } 752 else 753 h[0] = '\0'; 754 755 while( dialog != NULL && dialog != dialoghdlr->rootdialog ) 756 { 757 if( h[0] == '\0' ) 758 (void)SCIPstrncpy(h, dialog->name, SCIP_MAXSTRLEN); 759 else 760 { 761 (void)SCIPsnprintf(s, SCIP_MAXSTRLEN, "%s %s", dialog->name, h); 762 (void)SCIPstrncpy(h, s, SCIP_MAXSTRLEN); 763 } 764 dialog = dialog->parent; 765 } 766 767 /* clean up the unmarked history entries */ 768 if( cleanuphistory ) 769 { 770 int i; 771 772 for( i = getHistoryLength()-1; i >= dialoghdlr->nprotectedhistelems; --i ) 773 { 774 SCIP_CALL( removeHistory(i) ); 775 } 776 } 777 778 /* add command to history */ 779 if( h[0] != '\0' ) 780 { 781 SCIP_CALL( addHistory(h) ); 782 } 783 784 /* if the history string was a full command line, protect the history entry from future cleanups */ 785 if( cleanuphistory ) 786 { 787 dialoghdlr->nprotectedhistelems = getHistoryLength(); 788 } 789 790 return SCIP_OKAY; 791 } 792 793 794 795 796 /* 797 * dialog 798 */ 799 800 /** ensures, that sub-dialogs array can store at least the given number of sub-dialogs */ 801 static 802 SCIP_RETCODE ensureSubdialogMem( 803 SCIP_DIALOG* dialog, /**< dialog */ 804 SCIP_SET* set, /**< global SCIP settings */ 805 int num /**< minimal storage size for sub-dialogs */ 806 ) 807 { 808 assert(dialog != NULL); 809 810 if( num > dialog->subdialogssize ) 811 { 812 int newsize; 813 814 newsize = SCIPsetCalcMemGrowSize(set, num); 815 SCIP_ALLOC( BMSreallocMemoryArray(&(dialog->subdialogs), newsize) ); 816 dialog->subdialogssize = newsize; 817 } 818 assert(num <= dialog->subdialogssize); 819 820 return SCIP_OKAY; 821 } 822 823 /** creates and captures a user interface dialog */ 824 SCIP_RETCODE SCIPdialogCreate( 825 SCIP_DIALOG** dialog, /**< pointer to store the dialog */ 826 SCIP_DECL_DIALOGCOPY ((*dialogcopy)), /**< copy method of dialog or NULL if you don't want to copy your plugin into sub-SCIPs */ 827 SCIP_DECL_DIALOGEXEC ((*dialogexec)), /**< execution method of dialog */ 828 SCIP_DECL_DIALOGDESC ((*dialogdesc)), /**< description output method of dialog, or NULL */ 829 SCIP_DECL_DIALOGFREE ((*dialogfree)), /**< destructor of dialog to free user data, or NULL */ 830 const char* name, /**< name of dialog: command name appearing in parent's dialog menu */ 831 const char* desc, /**< description of dialog used if description output method is NULL */ 832 SCIP_Bool issubmenu, /**< is the dialog a sub-menu? */ 833 SCIP_DIALOGDATA* dialogdata /**< user defined dialog data */ 834 ) 835 { 836 SCIP_RETCODE retcode; 837 838 assert(dialog != NULL); 839 assert(name != NULL); 840 841 retcode = SCIP_OKAY; 842 843 SCIP_ALLOC( BMSallocMemory(dialog) ); 844 (*dialog)->dialogcopy = dialogcopy; 845 (*dialog)->dialogexec = dialogexec; 846 (*dialog)->dialogdesc = dialogdesc; 847 (*dialog)->dialogfree = dialogfree; 848 849 SCIP_ALLOC_TERMINATE( retcode, BMSduplicateMemoryArray(&(*dialog)->name, name, strlen(name)+1), TERMINATE ); 850 if( desc != NULL ) 851 { 852 SCIP_ALLOC_TERMINATE( retcode, BMSduplicateMemoryArray(&(*dialog)->desc, desc, strlen(desc)+1), TERMINATE ); 853 } 854 else 855 (*dialog)->desc = NULL; 856 857 (*dialog)->issubmenu = issubmenu; 858 (*dialog)->parent = NULL; 859 (*dialog)->subdialogs = NULL; 860 (*dialog)->nsubdialogs = 0; 861 (*dialog)->subdialogssize = 0; 862 (*dialog)->nuses = 0; 863 (*dialog)->dialogdata = dialogdata; 864 865 /* capture dialog */ 866 SCIPdialogCapture(*dialog); 867 868 TERMINATE: 869 if( retcode != SCIP_OKAY ) 870 { 871 BMSfreeMemoryArrayNull(&(*dialog)->name); 872 BMSfreeMemory(dialog); 873 } 874 875 return retcode; 876 } 877 878 /** frees dialog and all of its sub-dialogs */ 879 static 880 SCIP_RETCODE dialogFree( 881 SCIP* scip, /**< SCIP data structure */ 882 SCIP_DIALOG** dialog /**< pointer to dialog */ 883 ) 884 { 885 int i; 886 887 assert(dialog != NULL); 888 assert(*dialog != NULL); 889 assert((*dialog)->nuses == 0); 890 891 /* call destructor of dialog */ 892 if( (*dialog)->dialogfree != NULL ) 893 { 894 SCIP_CALL( (*dialog)->dialogfree(scip, *dialog) ); 895 } 896 897 /* release sub-dialogs */ 898 for( i = 0; i < (*dialog)->nsubdialogs; ++i ) 899 { 900 SCIP_CALL( SCIPdialogRelease(scip, &(*dialog)->subdialogs[i]) ); 901 } 902 BMSfreeMemoryArrayNull(&(*dialog)->subdialogs); 903 904 BMSfreeMemoryArrayNull(&(*dialog)->name); 905 BMSfreeMemoryArrayNull(&(*dialog)->desc); 906 BMSfreeMemory(dialog); 907 908 return SCIP_OKAY; 909 } 910 911 /** captures a dialog */ 912 void SCIPdialogCapture( 913 SCIP_DIALOG* dialog /**< dialog */ 914 ) 915 { 916 assert(dialog != NULL); 917 918 dialog->nuses++; 919 } 920 921 /** releases a dialog */ 922 SCIP_RETCODE SCIPdialogRelease( 923 SCIP* scip, /**< SCIP data structure */ 924 SCIP_DIALOG** dialog /**< pointer to dialog */ 925 ) 926 { 927 assert(dialog != NULL); 928 929 (*dialog)->nuses--; 930 if( (*dialog)->nuses == 0 ) 931 { 932 SCIP_CALL( dialogFree(scip, dialog) ); 933 } 934 935 return SCIP_OKAY; 936 } 937 938 /** executes dialog */ 939 SCIP_RETCODE SCIPdialogExec( 940 SCIP_DIALOG* dialog, /**< dialog */ 941 SCIP_SET* set, /**< global SCIP settings */ 942 SCIP_DIALOGHDLR* dialoghdlr, /**< dialog handler */ 943 SCIP_DIALOG** nextdialog /**< pointer to store the next dialog to process */ 944 ) 945 { 946 assert(dialog != NULL); 947 assert(dialog->dialogexec != NULL); 948 assert(set != NULL); 949 assert(nextdialog != NULL); 950 951 SCIP_CALL( dialog->dialogexec(set->scip, dialog, dialoghdlr, nextdialog) ); 952 953 return SCIP_OKAY; 954 } 955 956 /** comparison method for sorting dialogs w.r.t. to their name */ 957 static 958 SCIP_DECL_SORTPTRCOMP(dialogComp) 959 { 960 return strcmp( SCIPdialogGetName((SCIP_DIALOG*)elem1), SCIPdialogGetName((SCIP_DIALOG*)elem2) ); 961 } 962 963 /** adds a sub-dialog to the given dialog as menu entry and captures the sub-dialog */ 964 SCIP_RETCODE SCIPdialogAddEntry( 965 SCIP_DIALOG* dialog, /**< dialog */ 966 SCIP_SET* set, /**< global SCIP settings */ 967 SCIP_DIALOG* subdialog /**< sub-dialog to add as menu entry in dialog */ 968 ) 969 { 970 assert(dialog != NULL); 971 assert(subdialog != NULL); 972 973 /* check, if sub-dialog already exists */ 974 if( SCIPdialogHasEntry(dialog, SCIPdialogGetName(subdialog)) ) 975 { 976 SCIPerrorMessage("dialog entry with name <%s> already exists in dialog <%s>\n", 977 SCIPdialogGetName(subdialog), SCIPdialogGetName(dialog)); 978 return SCIP_INVALIDDATA; 979 } 980 981 /* resize the sub-dialogs array */ 982 SCIP_CALL( ensureSubdialogMem(dialog, set, dialog->nsubdialogs+1) ); 983 984 /* link the dialogs as parent-child pair; the sub-dialogs are sorted non-decreasing w.r.t. their name */ 985 SCIPsortedvecInsertPtr((void**)dialog->subdialogs, dialogComp, (void*)subdialog, &dialog->nsubdialogs, NULL); 986 subdialog->parent = dialog; 987 988 /* capture sub-dialog */ 989 SCIPdialogCapture(subdialog); 990 991 return SCIP_OKAY; 992 } 993 994 /** returns TRUE iff a dialog entry matching exactly the given name is existing in the given dialog */ 995 SCIP_Bool SCIPdialogHasEntry( 996 SCIP_DIALOG* dialog, /**< dialog */ 997 const char* entryname /**< name of the dialog entry to find */ 998 ) 999 { 1000 SCIP_DIALOG** subdialogs; 1001 int nsubdialogs; 1002 int i; 1003 1004 assert(dialog != NULL); 1005 assert(entryname != NULL); 1006 1007 /* check entryname w.r.t. available dialog options */ 1008 subdialogs = SCIPdialogGetSubdialogs(dialog); 1009 nsubdialogs = SCIPdialogGetNSubdialogs(dialog); 1010 for( i = 0; i < nsubdialogs; ++i ) 1011 { 1012 /* check, if the sub-dialog's name matches entryname */ 1013 if( strcmp(entryname, SCIPdialogGetName(subdialogs[i])) == 0 ) 1014 return TRUE; 1015 } 1016 1017 return FALSE; 1018 } 1019 1020 /** searches the dialog for entries corresponding to the given name; 1021 * If a complete match is found, the entry is returned as "subdialog" and 1022 * the return value is 1. 1023 * If no dialog entry completely matches the given "entryname", the number 1024 * of entries with names beginning with "entryname" is returned. If this 1025 * number is 1, the single match is returned as "subdialog". Otherwise, 1026 * "subdialog" is set to NULL. 1027 */ 1028 int SCIPdialogFindEntry( 1029 SCIP_DIALOG* dialog, /**< dialog */ 1030 const char* entryname, /**< name of the dialog entry to find */ 1031 SCIP_DIALOG** subdialog /**< pointer to store the found dialog entry */ 1032 ) 1033 { 1034 SCIP_DIALOG** subdialogs; 1035 unsigned int namelen; 1036 int nsubdialogs; 1037 int nfound; 1038 int i; 1039 1040 assert(dialog != NULL); 1041 assert(entryname != NULL); 1042 assert(subdialog != NULL); 1043 1044 *subdialog = NULL; 1045 1046 /* check entryname w.r.t. available dialog options */ 1047 subdialogs = SCIPdialogGetSubdialogs(dialog); 1048 nsubdialogs = SCIPdialogGetNSubdialogs(dialog); 1049 namelen = (unsigned int) strlen(entryname); 1050 nfound = 0; 1051 for( i = 0; i < nsubdialogs; ++i ) 1052 { 1053 /* check, if the beginning of the sub-dialog's name matches entryname */ 1054 if( strncmp(entryname, SCIPdialogGetName(subdialogs[i]), namelen) == 0 ) 1055 { 1056 *subdialog = subdialogs[i]; 1057 nfound++; 1058 1059 /* if entryname exactly matches the sub-dialog's name, use this sub-dialog */ 1060 if( namelen == (unsigned int) strlen(SCIPdialogGetName(subdialogs[i])) ) 1061 return 1; 1062 } 1063 } 1064 1065 if( nfound != 1 ) 1066 *subdialog = NULL; 1067 1068 return nfound; 1069 } 1070 1071 /** displays the dialog's menu */ 1072 SCIP_RETCODE SCIPdialogDisplayMenu( 1073 SCIP_DIALOG* dialog, /**< dialog */ 1074 SCIP* scip /**< SCIP data structure */ 1075 ) 1076 { 1077 int i; 1078 1079 assert(dialog != NULL); 1080 1081 /* display the dialog's sub menus */ 1082 for( i = 0; i < dialog->nsubdialogs; ++i ) 1083 { 1084 if( SCIPdialogIsSubmenu(dialog->subdialogs[i]) ) 1085 { 1086 SCIP_CALL( SCIPdialogDisplayMenuEntry(dialog->subdialogs[i], scip) ); 1087 } 1088 } 1089 1090 /* display the dialog's menu options */ 1091 for( i = 0; i < dialog->nsubdialogs; ++i ) 1092 { 1093 if( !SCIPdialogIsSubmenu(dialog->subdialogs[i]) ) 1094 { 1095 SCIP_CALL( SCIPdialogDisplayMenuEntry(dialog->subdialogs[i], scip) ); 1096 } 1097 } 1098 1099 if( dialog->nsubdialogs == 0 ) 1100 SCIPdialogMessage(scip, NULL, "<no options available>\n"); 1101 1102 return SCIP_OKAY; 1103 } 1104 1105 /** displays the entry for the dialog in it's parent's menu */ 1106 SCIP_RETCODE SCIPdialogDisplayMenuEntry( 1107 SCIP_DIALOG* dialog, /**< dialog */ 1108 SCIP* scip /**< SCIP data structure */ 1109 ) 1110 { 1111 char name[SCIP_MAXSTRLEN]; 1112 1113 assert(dialog != NULL); 1114 1115 /* display the dialog's name */ 1116 if( dialog->issubmenu ) 1117 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "<%s>", dialog->name); 1118 else 1119 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s", dialog->name); 1120 SCIPdialogMessage(scip, NULL, " %-21s ", name); 1121 if( strlen(name) > 21 ) 1122 { 1123 /* break the line, and start the description in the next line */ 1124 SCIPdialogMessage(scip, NULL, "\n --> "); 1125 } 1126 1127 /* display the dialog's description */ 1128 if( dialog->dialogdesc != NULL ) 1129 { 1130 SCIP_CALL( dialog->dialogdesc(scip, dialog) ); 1131 } 1132 else 1133 SCIPdialogMessage(scip, NULL, "%s",dialog->desc); 1134 SCIPdialogMessage(scip, NULL, "\n"); 1135 1136 return SCIP_OKAY; 1137 } 1138 1139 /** displays all dialog entries with names starting with the given "entryname" */ 1140 SCIP_RETCODE SCIPdialogDisplayCompletions( 1141 SCIP_DIALOG* dialog, /**< dialog */ 1142 SCIP* scip, /**< SCIP data structure */ 1143 const char* entryname /**< name of the dialog entry to find */ 1144 ) 1145 { 1146 SCIP_DIALOG** subdialogs; 1147 unsigned int namelen; 1148 int nsubdialogs; 1149 int i; 1150 1151 assert(dialog != NULL); 1152 assert(entryname != NULL); 1153 1154 /* check entryname w.r.t. available dialog options */ 1155 subdialogs = SCIPdialogGetSubdialogs(dialog); 1156 nsubdialogs = SCIPdialogGetNSubdialogs(dialog); 1157 namelen = (unsigned int) strlen(entryname); 1158 for( i = 0; i < nsubdialogs; ++i ) 1159 { 1160 /* check, if the beginning of the sub-dialog's name matches entryname */ 1161 if( strncmp(entryname, SCIPdialogGetName(subdialogs[i]), namelen) == 0 ) 1162 { 1163 SCIP_CALL( SCIPdialogDisplayMenuEntry(subdialogs[i], scip) ); 1164 } 1165 } 1166 1167 return SCIP_OKAY; 1168 } 1169 1170 /** gets the name of the current path in the dialog tree, separated by the given character */ 1171 void SCIPdialogGetPath( 1172 SCIP_DIALOG* dialog, /**< dialog */ 1173 const char sepchar, /**< separation character to insert in path */ 1174 char* path /**< string buffer to store the path */ 1175 ) 1176 { 1177 char s[SCIP_MAXSTRLEN]; 1178 1179 assert(dialog != NULL); 1180 1181 (void)SCIPstrncpy(path, dialog->name, SCIP_MAXSTRLEN); 1182 1183 dialog = dialog->parent; 1184 while( dialog != NULL ) 1185 { 1186 (void)SCIPsnprintf(s, SCIP_MAXSTRLEN, "%s%c%s", dialog->name, sepchar, path); 1187 (void)SCIPstrncpy(path, s, SCIP_MAXSTRLEN); 1188 dialog = dialog->parent; 1189 } 1190 } 1191 1192 /** gets the command name of the dialog */ 1193 const char* SCIPdialogGetName( 1194 SCIP_DIALOG* dialog /**< dialog */ 1195 ) 1196 { 1197 assert(dialog != NULL); 1198 1199 return dialog->name; 1200 } 1201 1202 /** gets the description of the dialog */ 1203 const char* SCIPdialogGetDesc( 1204 SCIP_DIALOG* dialog /**< dialog */ 1205 ) 1206 { 1207 assert(dialog != NULL); 1208 1209 return dialog->desc; 1210 } 1211 1212 /** returns whether the dialog is a sub menu */ 1213 SCIP_Bool SCIPdialogIsSubmenu( 1214 SCIP_DIALOG* dialog /**< dialog */ 1215 ) 1216 { 1217 assert(dialog != NULL); 1218 1219 return dialog->issubmenu; 1220 } 1221 1222 /** gets the parent dialog of the given dialog */ 1223 SCIP_DIALOG* SCIPdialogGetParent( 1224 SCIP_DIALOG* dialog /**< dialog */ 1225 ) 1226 { 1227 assert(dialog != NULL); 1228 1229 return dialog->parent; 1230 } 1231 1232 /** gets the array of sub-dialogs associated with the given dialog */ 1233 SCIP_DIALOG** SCIPdialogGetSubdialogs( 1234 SCIP_DIALOG* dialog /**< dialog */ 1235 ) 1236 { 1237 assert(dialog != NULL); 1238 1239 return dialog->subdialogs; 1240 } 1241 1242 /** gets the number of sub-dialogs associated with the given dialog */ 1243 int SCIPdialogGetNSubdialogs( 1244 SCIP_DIALOG* dialog /**< dialog */ 1245 ) 1246 { 1247 assert(dialog != NULL); 1248 1249 return dialog->nsubdialogs; 1250 } 1251 1252 /** gets the user defined data associated with the given dialog */ 1253 SCIP_DIALOGDATA* SCIPdialogGetData( 1254 SCIP_DIALOG* dialog /**< dialog */ 1255 ) 1256 { 1257 assert(dialog != NULL); 1258 1259 return dialog->dialogdata; 1260 } 1261 1262 /** sets user data of dialog; user has to free old data in advance! */ 1263 void SCIPdialogSetData( 1264 SCIP_DIALOG* dialog, /**< dialog */ 1265 SCIP_DIALOGDATA* dialogdata /**< new dialog user data */ 1266 ) 1267 { 1268 assert(dialog != NULL); 1269 1270 dialog->dialogdata = dialogdata; 1271 } 1272 1273 /** writes command history to specified filename */ 1274 SCIP_RETCODE SCIPdialogWriteHistory( 1275 const char* filename /**< file name for (over)writing history */ 1276 ) 1277 { 1278 return writeHistory(filename); 1279 } 1280