shell.c (16909B)
1 #include <inttypes.h> 2 #include <errno.h> 3 #include <stdarg.h> 4 #include <stdbool.h> 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <string.h> 8 #include <time.h> 9 10 #include "src/nissy.h" 11 12 #define PRINTCUBE_BUFFER_SIZE 1024 /* Should be enough */ 13 #define SOLUTIONS_BUFFER_SIZE 500000 /* Should be enough */ 14 #define MAX_PATH_LENGTH 10000 /* Should be enough */ 15 16 #define _flag_cube "-cube" 17 #define _flag_perm "-perm" 18 #define _flag_command "-command" 19 #define _flag_str_cube "-cubestr" 20 #define _flag_format "-format" 21 #define _flag_format_in "-fin" 22 #define _flag_format_out "-fout" 23 #define _flag_moves "-moves" 24 #define _flag_trans "-trans" 25 #define _flag_solver "-solver" 26 #define _flag_options "-options" 27 #define _flag_nisstype "-nisstype" 28 #define _flag_minmoves "-m" 29 #define _flag_maxmoves "-M" 30 #define _flag_optimal "-O" 31 #define _flag_maxsolutions "-n" 32 33 #define _info_cubeformat(cube) cube " must be given in B32 format." 34 #define _info_movesformat "The accepted moves are U, D, R, L, F and B, " \ 35 "optionally followed by a 2, a ' or a 3." 36 #define _info_transformat "The transformation must be given in the format " \ 37 "(rotation|mirrored) (2 letters), for exmple " \ 38 "'rotation UF' or 'mirrored BL'." 39 #define _info_formats "The available formats are H48, B32 and SRC." 40 41 typedef struct { 42 int command_index; 43 char cube[22]; 44 char cube_perm[22]; 45 char *str_command; 46 char *str_cube; 47 char *str_format; 48 char *str_format_in; 49 char *str_format_out; 50 char *str_moves; 51 char *str_trans; 52 char *str_solver; 53 char *str_options; /* TODO: remove, use only solver */ 54 char *str_nisstype; /* TODO: remove, use flags */ 55 int8_t minmoves; 56 int8_t maxmoves; 57 int8_t optimal; 58 int64_t maxsolutions; 59 } args_t; 60 61 static void print_cube_result(int64_t, char [static 22]); 62 static void print_str_result(int64_t, char *); 63 64 static int64_t compose_exec(args_t *); 65 static int64_t inverse_exec(args_t *); 66 static int64_t applymoves_exec(args_t *); 67 static int64_t applytrans_exec(args_t *); 68 static int64_t frommoves_exec(args_t *); 69 static int64_t convert_exec(args_t *); 70 static int64_t randomcube_exec(args_t *); 71 static int64_t datasize_exec(args_t *); 72 static int64_t gendata_exec(args_t *); 73 static int64_t solve_exec(args_t *); 74 static int64_t help_exec(args_t *); 75 76 static int parse_args(int, char **, args_t *); 77 static bool parse_int8(char *, int8_t *); 78 static bool parse_int64(char *, int64_t *); 79 80 static bool set_cube(int, char **, args_t *); 81 static bool set_cube_perm(int, char **, args_t *); 82 static bool set_str_command(int, char **, args_t *); 83 static bool set_str_cube(int, char **, args_t *); 84 static bool set_str_format(int, char **, args_t *); 85 static bool set_str_format_in(int, char **, args_t *); 86 static bool set_str_format_out(int, char **, args_t *); 87 static bool set_str_moves(int, char **, args_t *); 88 static bool set_str_trans(int, char **, args_t *); 89 static bool set_str_solver(int, char **, args_t *); 90 static bool set_str_options(int, char **, args_t *); 91 static bool set_str_nisstype(int, char **, args_t *); 92 static bool set_minmoves(int, char **, args_t *); 93 static bool set_maxmoves(int, char **, args_t *); 94 static bool set_optimal(int, char **, args_t *); 95 static bool set_maxsolutions(int, char **, args_t *); 96 static bool set_id(int, char **, args_t *); 97 98 static uint64_t rand64(void); 99 100 #define OPTION(N, A, S) { .name = N, .nargs = A, .set = S } 101 struct { 102 char *name; 103 int nargs; 104 bool (*set)(int, char **, args_t *); 105 } options[] = { 106 OPTION(_flag_cube, 1, set_cube), 107 OPTION(_flag_perm, 1, set_cube_perm), 108 OPTION(_flag_command, 1, set_str_command), 109 OPTION(_flag_str_cube, 1, set_str_cube), 110 OPTION(_flag_format, 1, set_str_format), 111 OPTION(_flag_format_in, 1, set_str_format_in), 112 OPTION(_flag_format_out, 1, set_str_format_out), 113 OPTION(_flag_moves, 1, set_str_moves), 114 OPTION(_flag_trans, 1, set_str_trans), 115 OPTION(_flag_solver, 1, set_str_solver), 116 OPTION(_flag_options, 1, set_str_options), /* TODO: remove, use only solver */ 117 OPTION(_flag_nisstype, 1, set_str_nisstype), /* TODO: remove, use flags */ 118 OPTION(_flag_minmoves, 1, set_minmoves), 119 OPTION(_flag_maxmoves, 1, set_maxmoves), 120 OPTION(_flag_optimal, 1, set_optimal), 121 OPTION(_flag_maxsolutions, 1, set_maxsolutions), 122 OPTION(NULL, 0, NULL) 123 }; 124 125 #define COMMAND(N, S, D, E) { .name = N, .syn = S, .desc = D, .exec = E } 126 struct { 127 char *name; 128 char *syn; 129 char *desc; 130 int64_t (*exec)(args_t *); 131 } commands[] = { 132 /* TODO: add synopsis and description here */ 133 COMMAND( 134 "compose", 135 "compose " _flag_cube " CUBE " _flag_perm " PERM", 136 "Apply on CUBE the permutation defined by PERM. " 137 _info_cubeformat("CUBE and PERM"), 138 compose_exec 139 ), 140 COMMAND( 141 "inverse", 142 "inverse " _flag_cube " CUBE ", 143 "Compute the inverse of the given CUBE. " 144 _info_cubeformat("CUBE"), 145 inverse_exec 146 ), 147 COMMAND( 148 "applymoves", 149 "applymoves " _flag_cube " CUBE " _flag_moves " MOVES", 150 "Apply the given MOVES to the given CUBE. " 151 _info_cubeformat("CUBE") " " _info_movesformat, 152 applymoves_exec 153 ), 154 COMMAND( 155 "applytrans", 156 "applytrans " _flag_cube " CUBE " _flag_trans " TRANS", 157 "Apply the single transformation TRANS to the given CUBE. " 158 _info_cubeformat("CUBE") " " _info_transformat, 159 applytrans_exec 160 ), 161 COMMAND( 162 "frommoves", 163 "frommoves " _flag_moves " MOVES", 164 "Return the cube obtained by applying the given MOVES " 165 "to a solved cube. " _info_movesformat, 166 frommoves_exec 167 ), 168 COMMAND( 169 "convert", 170 "convert " _flag_str_cube " CUBESTR " 171 _flag_format_in " FORMAT_IN " _flag_format_out " FORMAT_OUT", 172 "Convert the cube described by CUBESTR from FORMAT_IN to " 173 "FORMAT_OUT." 174 _info_formats " " 175 "CUBESTR must be a valid cube in the FORMAT_IN format.", 176 convert_exec 177 ), 178 COMMAND( 179 "randomcube", 180 "randomcube", 181 "Returns a random cube in B32 format.", 182 randomcube_exec 183 ), 184 COMMAND( 185 "datasize", 186 "datasize " _flag_solver " SOLVER " _flag_options " OPTIONS", 187 "Return the size in bytes of the data table used by " 188 "SOLVER when called with the given OPTIONS.", 189 datasize_exec 190 ), 191 COMMAND( 192 "gendata", 193 "gendata " _flag_solver " SOLVER " _flag_options " OPTIONS", 194 "Generate the data table used by " 195 "SOLVER when called with the given OPTIONS.", 196 gendata_exec 197 ), 198 COMMAND( 199 "solve", 200 "solve " _flag_solver " SOLVER " _flag_options " OPTIONS " 201 "[" _flag_minmoves " n] [" _flag_maxmoves " N] " 202 _flag_cube " CUBE", 203 "Solve the given CUBE using SOLVER with the given OPTIONS, " 204 "using at least n and at most N moves. " 205 _info_cubeformat("CUBE"), 206 solve_exec 207 ), 208 COMMAND( 209 "help", 210 "help [" _flag_command " COMMAND]", 211 "If no COMMAND is specified, prints some generic information " 212 "and the list of commands. Otherwise it prints detailed " 213 "information about the specified COMMAND.", 214 help_exec 215 ), 216 COMMAND(NULL, NULL, NULL, NULL) 217 }; 218 219 char *tablepaths[] = { 220 "tables/", 221 "", 222 NULL 223 }; 224 225 static uint64_t 226 rand64(void) 227 { 228 uint64_t i, ret; 229 230 for (i = 0, ret = 0; i < 64; i++) 231 ret |= (uint64_t)(rand() % 2) << i; 232 233 return ret; 234 } 235 236 static void 237 print_cube_result(int64_t ret, char result[static 22]) 238 { 239 switch (ret) { 240 case 0: 241 break; 242 case 1: 243 fprintf(stderr, "Warning: resulting cube not solvable\n"); 244 break; 245 case 2: /* Fallthrough */ 246 default: 247 fprintf(stderr, "Unknown error (result is inconsistent)\n"); 248 } 249 250 printf("%s\n", result); 251 } 252 253 static void 254 print_str_result(int64_t ret, char *result) 255 { 256 switch (ret) { 257 case 0: 258 break; 259 default: 260 fprintf(stderr, "Unknown error\n"); 261 } 262 263 printf("%s\n", result); 264 } 265 266 static int64_t 267 compose_exec(args_t *args) 268 { 269 char result[22]; 270 int64_t ret; 271 272 ret = nissy_compose(args->cube, args->cube_perm, result); 273 print_cube_result(ret, result); 274 275 return ret; 276 } 277 278 static int64_t 279 inverse_exec(args_t *args) 280 { 281 char result[22]; 282 int64_t ret; 283 284 ret = nissy_inverse(args->cube, result); 285 print_cube_result(ret, result); 286 287 return ret; 288 } 289 290 static int64_t 291 applymoves_exec(args_t *args) 292 { 293 char result[22]; 294 int64_t ret; 295 296 ret = nissy_applymoves(args->cube, args->str_moves, result); 297 print_cube_result(ret, result); 298 299 return ret; 300 } 301 302 static int64_t 303 applytrans_exec(args_t *args) 304 { 305 char result[22]; 306 int64_t ret; 307 308 ret = nissy_applytrans(args->cube, args->str_trans, result); 309 print_cube_result(ret, result); 310 311 return ret; 312 } 313 314 static int64_t 315 frommoves_exec(args_t *args) 316 { 317 char result[22]; 318 int64_t ret; 319 320 ret = nissy_frommoves(args->str_moves, result); 321 print_cube_result(ret, result); 322 323 return ret; 324 } 325 326 static int64_t 327 convert_exec(args_t *args) 328 { 329 char result[PRINTCUBE_BUFFER_SIZE]; 330 int64_t ret; 331 332 ret = nissy_convert( 333 args->str_format_in, args->str_format_out, args->str_cube, result); 334 print_str_result(ret, result); 335 336 return ret; 337 } 338 339 static int64_t 340 randomcube_exec(args_t *args) 341 { 342 char result[PRINTCUBE_BUFFER_SIZE]; 343 int64_t ret, ep, eo, cp, co; 344 345 ep = rand64(); 346 eo = rand64(); 347 cp = rand64(); 348 co = rand64(); 349 ret = nissy_getcube(ep, eo, cp, co, "fix", result); 350 print_str_result(ret, result); 351 352 return ret; 353 } 354 355 static int64_t 356 datasize_exec(args_t *args) 357 { 358 int64_t ret; 359 360 ret = nissy_datasize(args->str_solver, args->str_options); 361 if (ret < 0) 362 fprintf(stderr, "Unknown error (make sure solver is valid)\n"); 363 printf("%" PRId64 "\n", ret); 364 365 return ret; 366 } 367 368 static int64_t 369 gendata_exec(args_t *args) 370 { 371 int i; 372 FILE *file; 373 char *buf, path[MAX_PATH_LENGTH]; 374 int64_t ret, size; 375 size_t written; 376 377 /* TODO: should give warning if overwriting existing file */ 378 for (i = 0; tablepaths[i] != NULL; i++) { 379 strcpy(path, tablepaths[i]); 380 strcat(path, args->str_solver); 381 file = fopen(path, "wb"); 382 if (file != NULL) 383 break; 384 } 385 386 if (tablepaths[i] == NULL) { 387 fprintf(stderr, "Cannot write data to file\n"); 388 fclose(file); 389 return -2; 390 } 391 392 size = nissy_datasize(args->str_solver, args->str_options); 393 394 if (size < 0) { 395 fprintf(stderr, 396 "Unknown error in retrieving data size" 397 "(make sure solver is valid)\n"); 398 fclose(file); 399 return -3; 400 } 401 402 buf = malloc(size); 403 404 ret = nissy_gendata(args->str_solver, args->str_options, buf); 405 if (ret < 0) { 406 fprintf(stderr, "Unknown error in generating data\n"); 407 fclose(file); 408 free(buf); 409 return -4; 410 } 411 if (ret != size) { 412 fprintf(stderr, "Unknown error: unexpected data size " 413 "got %" PRId64 ", expected %" PRId64 ")\n", ret, size); 414 fclose(file); 415 free(buf); 416 return -5; 417 } 418 419 written = fwrite(buf, size, 1, file); 420 fclose(file); 421 free(buf); 422 423 if (written != 1) { 424 fprintf(stderr, 425 "Error: data was generated correctly, but could not be " 426 "written to file (generated %" PRId64 " bytes, written " 427 "%zu)\n", size, written); 428 return -6; 429 } 430 431 fprintf(stderr, "Data written to %s\n", path); 432 433 return 0; 434 } 435 436 static int64_t 437 solve_exec(args_t *args) 438 { 439 int i; 440 FILE *file; 441 char *buf, solutions[SOLUTIONS_BUFFER_SIZE], path[MAX_PATH_LENGTH]; 442 int64_t ret, gendata_ret, size; 443 size_t read; 444 445 for (i = 0; tablepaths[i] != NULL; i++) { 446 strcpy(path, tablepaths[i]); 447 strcat(path, args->str_solver); 448 file = fopen(path, "rb"); 449 if (file != NULL) 450 break; 451 } 452 453 if (tablepaths[i] == NULL) { 454 fprintf(stderr, 455 "Cannot read data file, " 456 "generating it (this can take a while)\n"); 457 gendata_ret = gendata_exec(args); 458 if (gendata_ret) 459 return gendata_ret; 460 } 461 462 /* Ugh, this is not elegant TODO */ 463 if (file == NULL) { 464 for (i = 0; tablepaths[i] != NULL; i++) { 465 strcpy(path, tablepaths[i]); 466 strcat(path, args->str_solver); 467 file = fopen(path, "rb"); 468 if (file != NULL) 469 break; 470 } 471 } 472 473 if (tablepaths[i] == NULL) { 474 fprintf(stderr, "Error: data file not found\n"); 475 fclose(file); 476 return -1; 477 } 478 479 size = nissy_datasize(args->str_solver, args->str_options); 480 buf = malloc(size); 481 read = fread(buf, size, 1, file); 482 fclose(file); 483 if (read != 1) { 484 fprintf(stderr, "Error reading data from file: " 485 "fread() returned %zu instead of 1 when attempting to" 486 "read %" PRId64 " bytes from file %s\n", read, size, path); 487 return -2; 488 } 489 490 ret = nissy_solve( 491 args->cube, args->str_solver, args->str_options, args->str_nisstype, 492 args->minmoves, args->maxmoves, args->maxsolutions, args->optimal, 493 buf, solutions); 494 495 free(buf); 496 497 if (ret == 0) 498 fprintf(stderr, "No solutions found\n"); 499 else 500 printf("%s", solutions); 501 502 return 0; 503 } 504 505 static int64_t 506 help_exec(args_t *args) 507 { 508 int i; 509 510 if (args->str_command == NULL || args->str_command[0] == '\0') { 511 printf("This is a rudimentary shell for the H48 library.\n"); 512 printf("Available commands and usage:\n\n"); 513 for (i = 0; commands[i].name != NULL; i++) 514 printf("%-15s%s\n", commands[i].name, commands[i].syn); 515 printf("\nUse 'help COMMAND' for more information.\n"); 516 } else { 517 for (i = 0; commands[i].name != NULL; i++) 518 if (!strcmp(args->str_command, commands[i].name)) 519 break; 520 if (commands[i].name == NULL) { 521 printf("Unknown command %s\n", args->str_command); 522 return 1; 523 } 524 printf("Command %s\n\n", commands[i].name); 525 printf("Synopsis: %s\n\n", commands[i].syn); 526 printf("Description: %s\n", commands[i].desc); 527 } 528 529 return 0; 530 } 531 532 static int 533 parse_args(int argc, char **argv, args_t *args) 534 { 535 int i, j, n; 536 537 *args = (args_t) { 538 .command_index = -1, 539 .cube = "", 540 .cube_perm = "", 541 .str_cube = "", 542 .str_format = "", 543 .str_format_in = "", 544 .str_format_out = "", 545 .str_moves = "", 546 .str_trans = "", 547 .str_solver = "", 548 .str_options = "", 549 .str_nisstype = "", 550 .minmoves = 0, 551 .maxmoves = 20, 552 .optimal = -1, 553 .maxsolutions = 1, 554 }; 555 556 if (argc == 0) { 557 printf("No command given\n"); 558 return 1; 559 } 560 561 for (i = 0; commands[i].name != NULL; i++) { 562 if (!strcmp(argv[0], commands[i].name)) { 563 args->command_index = i; 564 break; 565 } 566 } 567 568 if (commands[i].name == NULL) { 569 fprintf(stderr, "Unknown command %s\n", argv[0]); 570 return 1; 571 } 572 573 for (i = 1; i < argc; i++) { 574 for (j = 0; options[j].name != NULL; j++) { 575 n = argc - i - 1; 576 if (strcmp(argv[i], options[j].name)) 577 continue; 578 if (n < options[j].nargs) { 579 fprintf(stderr, 580 "Too few arguments for option %s\n", 581 options[j].name); 582 return 1; 583 } 584 if (!options[j].set(n, argv+i+1, args)) { 585 fprintf(stderr, 586 "Error parsing arguments for option %s\n", 587 options[j].name); 588 return 1; 589 } 590 i += options[j].nargs; 591 break; 592 } 593 if (options[j].name == NULL) { 594 fprintf(stderr, "Unknown option %s\n", argv[i]); 595 return 1; 596 } 597 } 598 599 return 0; 600 } 601 602 bool 603 parse_int8(char *argv, int8_t *result) 604 { 605 bool noerror; 606 int64_t n; 607 608 noerror = parse_int64(argv, &n); 609 *result = (int8_t)n; 610 611 return noerror && n >= INT8_MIN && n <= INT8_MAX; 612 } 613 614 bool 615 parse_int64(char *argv, int64_t *result) 616 { 617 *result = strtoll(argv, NULL, 10); 618 619 /* TODO: figure out how errno works and use it */ 620 return true; 621 } 622 623 static bool 624 set_cube(int argc, char **argv, args_t *args) 625 { 626 memcpy(args->cube, argv[0], 22); 627 args->cube[21] = 0; 628 629 return true; 630 } 631 632 static bool 633 set_cube_perm(int argc, char **argv, args_t *args) 634 { 635 memcpy(args->cube_perm, argv[0], 22); 636 args->cube_perm[21] = 0; 637 638 return true; 639 } 640 641 static bool 642 set_str_command(int argc, char **argv, args_t *args) 643 { 644 args->str_command = argv[0]; 645 646 return true; 647 } 648 649 static bool 650 set_str_cube(int argc, char **argv, args_t *args) 651 { 652 args->str_cube = argv[0]; 653 654 return true; 655 } 656 657 static bool 658 set_str_format(int argc, char **argv, args_t *args) 659 { 660 args->str_format = argv[0]; 661 662 return true; 663 } 664 665 static bool 666 set_str_format_in(int argc, char **argv, args_t *args) 667 { 668 args->str_format_in = argv[0]; 669 670 return true; 671 } 672 673 static bool 674 set_str_format_out(int argc, char **argv, args_t *args) 675 { 676 args->str_format_out = argv[0]; 677 678 return true; 679 } 680 681 static bool 682 set_str_moves(int argc, char **argv, args_t *args) 683 { 684 args->str_moves = argv[0]; 685 686 return true; 687 } 688 689 static bool 690 set_str_trans(int argc, char **argv, args_t *args) 691 { 692 args->str_trans = argv[0]; 693 694 return true; 695 } 696 697 static bool 698 set_str_solver(int argc, char **argv, args_t *args) 699 { 700 args->str_solver = argv[0]; 701 702 return true; 703 } 704 705 static bool 706 set_str_options(int argc, char **argv, args_t *args) 707 { 708 args->str_options = argv[0]; 709 710 return true; 711 } 712 713 static bool 714 set_str_nisstype(int argc, char **argv, args_t *args) 715 { 716 args->str_nisstype = argv[0]; 717 718 return true; 719 } 720 721 static bool 722 set_minmoves(int argc, char **argv, args_t *args) 723 { 724 return parse_int8(argv[0], &args->minmoves); 725 } 726 727 static bool 728 set_maxmoves(int argc, char **argv, args_t *args) 729 { 730 return parse_int8(argv[0], &args->maxmoves); 731 } 732 733 static bool 734 set_optimal(int argc, char **argv, args_t *args) 735 { 736 return parse_int8(argv[0], &args->optimal); 737 } 738 739 static bool 740 set_maxsolutions(int argc, char **argv, args_t *args) 741 { 742 return parse_int64(argv[0], &args->maxsolutions); 743 } 744 745 void 746 log_stderr(const char *str, ...) 747 { 748 va_list args; 749 750 va_start(args, str); 751 vfprintf(stderr, str, args); 752 va_end(args); 753 } 754 755 int 756 main(int argc, char **argv) 757 { 758 int parse_error; 759 args_t args; 760 761 srand(time(NULL)); 762 nissy_setlogger(log_stderr); 763 764 parse_error = parse_args(argc-1, argv+1, &args); 765 if (parse_error) 766 return parse_error; 767 768 return (int)commands[args.command_index].exec(&args); 769 }