00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00034 #include <stdio.h>
00035 #include <stdlib.h>
00036 #include <unistd.h>
00037 #ifdef __MINGW32__
00038 #define EX_OK 0
00039 #define EX_USAGE 64
00040 #else
00041 #include <sysexits.h>
00042 #endif
00043 #include <string.h>
00044 #include <ctype.h>
00045 #include <assert.h>
00046 #include "smf.h"
00047 #include "config.h"
00048
00049 #ifdef HAVE_LIBREADLINE
00050 #include <readline/readline.h>
00051 #include <readline/history.h>
00052 #endif
00053
00054 smf_track_t *selected_track = NULL;
00055 smf_event_t *selected_event = NULL;
00056 smf_t *smf = NULL;
00057 char *last_file_name = NULL;
00058
00059 #define COMMAND_LENGTH 10
00060
00061 static void
00062 log_handler(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer notused)
00063 {
00064 if (strcmp(log_domain, "smfsh") == 0)
00065 fprintf(stderr, "%s\n", message);
00066 else
00067 fprintf(stderr, "%s: %s\n", log_domain, message);
00068 }
00069
00070 static int cmd_track(char *arg);
00071
00072 static int
00073 cmd_load(char *file_name)
00074 {
00075 char *decoded;
00076
00077 if (file_name == NULL) {
00078 if (last_file_name == NULL) {
00079 g_critical("Please specify file name.");
00080 return (-1);
00081 }
00082
00083 file_name = strdup(last_file_name);
00084 } else {
00085 file_name = strdup(file_name);
00086 }
00087
00088 selected_track = NULL;
00089 selected_event = NULL;
00090
00091 if (smf != NULL) {
00092 smf_delete(smf);
00093 smf = NULL;
00094 }
00095
00096 if (last_file_name != NULL)
00097 free(last_file_name);
00098 last_file_name = strdup(file_name);
00099
00100 smf = smf_load(file_name);
00101 if (smf == NULL) {
00102 g_critical("Couldn't load '%s'.", file_name);
00103
00104 smf = smf_new();
00105 if (smf == NULL) {
00106 g_critical("Cannot initialize smf_t.");
00107 return (-1);
00108 }
00109
00110 return (-2);
00111 }
00112
00113 g_message("File '%s' loaded.", file_name);
00114 decoded = smf_decode(smf);
00115 g_message("%s.", decoded);
00116 free(decoded);
00117
00118 cmd_track("1");
00119
00120 free(file_name);
00121
00122 return (0);
00123 }
00124
00125 static int
00126 cmd_save(char *file_name)
00127 {
00128 int ret;
00129
00130 if (file_name == NULL) {
00131 if (last_file_name == NULL) {
00132 g_critical("Please specify file name.");
00133 return (-1);
00134 }
00135
00136 file_name = strdup(last_file_name);
00137 } else {
00138 file_name = strdup(file_name);
00139 }
00140
00141 if (last_file_name != NULL)
00142 free(last_file_name);
00143 last_file_name = strdup(file_name);
00144
00145 ret = smf_save(smf, file_name);
00146 if (ret) {
00147 g_critical("Couldn't save '%s'", file_name);
00148 return (-1);
00149 }
00150
00151 g_message("File '%s' saved.", file_name);
00152
00153 free(file_name);
00154
00155 return (0);
00156 }
00157
00158 static int
00159 cmd_ppqn(char *new_ppqn)
00160 {
00161 int tmp;
00162 char *end;
00163
00164 if (new_ppqn == NULL) {
00165 g_message("Pulses Per Quarter Note (aka Division) is %d.", smf->ppqn);
00166 } else {
00167 tmp = strtol(new_ppqn, &end, 10);
00168 if (end - new_ppqn != strlen(new_ppqn)) {
00169 g_critical("Invalid PPQN, garbage characters after the number.");
00170 return (-1);
00171 }
00172
00173 if (tmp <= 0) {
00174 g_critical("Invalid PPQN, valid values are greater than zero.");
00175 return (-2);
00176 }
00177
00178 if (smf_set_ppqn(smf, tmp)) {
00179 g_message("smf_set_ppqn failed.");
00180 return (-3);
00181 }
00182
00183 g_message("Pulses Per Quarter Note changed to %d.", smf->ppqn);
00184 }
00185
00186 return (0);
00187 }
00188
00189 static int
00190 cmd_format(char *new_format)
00191 {
00192 int tmp;
00193 char *end;
00194
00195 if (new_format == NULL) {
00196 g_message("Format is %d.", smf->format);
00197 } else {
00198 tmp = strtol(new_format, &end, 10);
00199 if (end - new_format != strlen(new_format)) {
00200 g_critical("Invalid format value, garbage characters after the number.");
00201 return (-1);
00202 }
00203
00204 if (tmp < 0 || tmp > 2) {
00205 g_critical("Invalid format value, valid values are in range 0 - 2, inclusive.");
00206 return (-2);
00207 }
00208
00209 if (smf_set_format(smf, tmp)) {
00210 g_critical("smf_set_format failed.");
00211 return (-3);
00212 }
00213
00214 g_message("Forma changed to %d.", smf->format);
00215 }
00216
00217 return (0);
00218 }
00219
00220 static int
00221 cmd_tracks(char *notused)
00222 {
00223 if (smf->number_of_tracks > 0)
00224 g_message("There are %d tracks, numbered from 1 to %d.", smf->number_of_tracks, smf->number_of_tracks);
00225 else
00226 g_message("There are no tracks.");
00227
00228 return (0);
00229 }
00230
00231 static int
00232 parse_track_number(const char *arg)
00233 {
00234 int num;
00235 char *end;
00236
00237 if (arg == NULL) {
00238 if (selected_track == NULL) {
00239 g_message("No track currently selected and no track number given.");
00240 return (-1);
00241 } else {
00242 return (selected_track->track_number);
00243 }
00244 }
00245
00246 num = strtol(arg, &end, 10);
00247 if (end - arg != strlen(arg)) {
00248 g_critical("Invalid track number, garbage characters after the number.");
00249 return (-1);
00250 }
00251
00252 if (num < 1 || num > smf->number_of_tracks) {
00253 if (smf->number_of_tracks > 0) {
00254 g_critical("Invalid track number specified; valid choices are 1 - %d.", smf->number_of_tracks);
00255 } else {
00256 g_critical("There are no tracks.");
00257 }
00258
00259 return (-1);
00260 }
00261
00262 return (num);
00263 }
00264
00265 static int
00266 cmd_track(char *arg)
00267 {
00268 int num;
00269
00270 if (arg == NULL) {
00271 if (selected_track == NULL)
00272 g_message("No track currently selected.");
00273 else
00274 g_message("Currently selected is track number %d, containing %d events.",
00275 selected_track->track_number, selected_track->number_of_events);
00276 } else {
00277 if (smf->number_of_tracks == 0) {
00278 g_message("There are no tracks.");
00279 return (-1);
00280 }
00281
00282 num = parse_track_number(arg);
00283 if (num < 0)
00284 return (-1);
00285
00286 selected_track = smf_get_track_by_number(smf, num);
00287 if (selected_track == NULL) {
00288 g_critical("smf_get_track_by_number() failed, track not selected.");
00289 return (-3);
00290 }
00291
00292 selected_event = NULL;
00293
00294 g_message("Track number %d selected; it contains %d events.",
00295 selected_track->track_number, selected_track->number_of_events);
00296 }
00297
00298 return (0);
00299 }
00300
00301 static int
00302 cmd_trackadd(char *notused)
00303 {
00304 selected_track = smf_track_new();
00305 if (selected_track == NULL) {
00306 g_critical("smf_track_new() failed, track not created.");
00307 return (-1);
00308 }
00309
00310 smf_add_track(smf, selected_track);
00311
00312 selected_event = NULL;
00313
00314 g_message("Created new track; track number %d selected.", selected_track->track_number);
00315
00316 return (0);
00317 }
00318
00319 static int
00320 cmd_trackrm(char *arg)
00321 {
00322 int num = parse_track_number(arg);
00323
00324 if (num < 0)
00325 return (-1);
00326
00327 if (selected_track != NULL && num == selected_track->track_number) {
00328 selected_track = NULL;
00329 selected_event = NULL;
00330 }
00331
00332 smf_track_delete(smf_get_track_by_number(smf, num));
00333
00334 g_message("Track %d removed.", num);
00335
00336 return (0);
00337 }
00338
00339 #define BUFFER_SIZE 1024
00340
00341 static int
00342 show_event(smf_event_t *event)
00343 {
00344 int off = 0, i;
00345 char *decoded, *type;
00346
00347 if (smf_event_is_metadata(event))
00348 type = "Metadata";
00349 else
00350 type = "Event";
00351
00352 decoded = smf_event_decode(event);
00353
00354 if (decoded == NULL) {
00355 decoded = malloc(BUFFER_SIZE);
00356 if (decoded == NULL) {
00357 g_critical("show_event: malloc failed.");
00358 return (-1);
00359 }
00360
00361 off += snprintf(decoded + off, BUFFER_SIZE - off, "Unknown event:");
00362
00363 for (i = 0; i < event->midi_buffer_length && i < 5; i++)
00364 off += snprintf(decoded + off, BUFFER_SIZE - off, " 0x%x", event->midi_buffer[i]);
00365 }
00366
00367 g_message("%d: %s: %s, %f seconds, %d pulses, %d delta pulses", event->event_number, type, decoded,
00368 event->time_seconds, event->time_pulses, event->delta_time_pulses);
00369
00370 free(decoded);
00371
00372 return (0);
00373 }
00374
00375 static int
00376 cmd_events(char *notused)
00377 {
00378 smf_event_t *event;
00379
00380 if (selected_track == NULL) {
00381 g_critical("No track selected - please use 'track <number>' command first.");
00382 return (-1);
00383 }
00384
00385 if (selected_track->number_of_events == 0) {
00386 g_message("Selected track is empty.");
00387 return (0);
00388 }
00389
00390 g_message("List of events in track %d follows:", selected_track->track_number);
00391
00392 smf_rewind(smf);
00393
00394 while ((event = smf_track_get_next_event(selected_track)) != NULL)
00395 show_event(event);
00396
00397 smf_rewind(smf);
00398
00399 return (0);
00400 }
00401
00402 static int
00403 parse_event_number(const char *arg)
00404 {
00405 int num;
00406 char *end;
00407
00408 if (selected_track == NULL) {
00409 g_critical("You need to select track first (using 'track <number>').");
00410 return (-1);
00411 }
00412
00413 if (arg == NULL) {
00414 if (selected_event == NULL) {
00415 g_message("No event currently selected and no event number given.");
00416 return (-1);
00417 } else {
00418 return (selected_event->event_number);
00419 }
00420 }
00421
00422 num = strtol(arg, &end, 10);
00423 if (end - arg != strlen(arg)) {
00424 g_critical("Invalid event number, garbage characters after the number.");
00425 return (-1);
00426 }
00427
00428 if (num < 1 || num > selected_track->number_of_events) {
00429 if (selected_track->number_of_events > 0)
00430 g_critical("Invalid event number specified; valid choices are 1 - %d.", selected_track->number_of_events);
00431 else
00432 g_critical("There are no events in currently selected track.");
00433
00434 return (-1);
00435 }
00436
00437 return (num);
00438 }
00439
00440 static int
00441 cmd_event(char *arg)
00442 {
00443 int num;
00444
00445 if (arg == NULL) {
00446 if (selected_event == NULL) {
00447 g_message("No event currently selected.");
00448 } else {
00449 g_message("Currently selected is event %d, track %d.", selected_event->event_number, selected_track->track_number);
00450 show_event(selected_event);
00451 }
00452 } else {
00453 num = parse_event_number(arg);
00454 if (num < 0)
00455 return (-1);
00456
00457 selected_event = smf_track_get_event_by_number(selected_track, num);
00458 if (selected_event == NULL) {
00459 g_critical("smf_get_event_by_number() failed, event not selected.");
00460 return (-2);
00461 }
00462
00463 g_message("Event number %d selected.", selected_event->event_number);
00464 show_event(selected_event);
00465 }
00466
00467 return (0);
00468 }
00469
00470 static int
00471 decode_hex(char *str, unsigned char **buffer, int *length)
00472 {
00473 int i, value, midi_buffer_length;
00474 char buf[3];
00475 unsigned char *midi_buffer = NULL;
00476 char *end = NULL;
00477
00478 if ((strlen(str) % 2) != 0) {
00479 g_critical("Hex value should have even number of characters, you know.");
00480 goto error;
00481 }
00482
00483 midi_buffer_length = strlen(str) / 2;
00484 midi_buffer = malloc(midi_buffer_length);
00485 if (midi_buffer == NULL) {
00486 g_critical("malloc() failed.");
00487 goto error;
00488 }
00489
00490 for (i = 0; i < midi_buffer_length; i++) {
00491 buf[0] = str[i * 2];
00492 buf[1] = str[i * 2 + 1];
00493 buf[2] = '\0';
00494 value = strtoll(buf, &end, 16);
00495
00496 if (end - buf != 2) {
00497 g_critical("Garbage characters detected after hex.");
00498 goto error;
00499 }
00500
00501 midi_buffer[i] = value;
00502 }
00503
00504 *buffer = midi_buffer;
00505 *length = midi_buffer_length;
00506
00507 return (0);
00508
00509 error:
00510 if (midi_buffer != NULL)
00511 free(midi_buffer);
00512
00513 return (-1);
00514 }
00515
00516 static void
00517 eventadd_usage(void)
00518 {
00519 g_message("Usage: add <time-in-seconds> <midi-in-hex> - for example, 'add 1 903C7F' will add");
00520 g_message("Note On event, note C4, velocity 127, channel 1, one second from the start of song, channel 1.");
00521 }
00522
00523 static int
00524 cmd_eventadd(char *str)
00525 {
00526 int midi_buffer_length;
00527 double seconds;
00528 unsigned char *midi_buffer;
00529 char *time, *endtime;
00530
00531 if (selected_track == NULL) {
00532 g_critical("Please select a track first, using 'track <number>' command.");
00533 return (-1);
00534 }
00535
00536 if (str == NULL) {
00537 eventadd_usage();
00538 return (-2);
00539 }
00540
00541
00542 time = str;
00543 str = strchr(str, ' ');
00544 if (str != NULL) {
00545 *str = '\0';
00546 str++;
00547 }
00548
00549 seconds = strtod(time, &endtime);
00550 if (endtime - time != strlen(time)) {
00551 g_critical("Time is supposed to be a number, without trailing characters.");
00552 return (-3);
00553 }
00554
00555
00556 if (str == NULL) {
00557 eventadd_usage();
00558 return (-4);
00559 }
00560
00561 if (decode_hex(str, &midi_buffer, &midi_buffer_length)) {
00562 eventadd_usage();
00563 return (-5);
00564 }
00565
00566 selected_event = smf_event_new();
00567 if (selected_event == NULL) {
00568 g_critical("smf_event_new() failed, event not created.");
00569 return (-6);
00570 }
00571
00572 selected_event->midi_buffer = midi_buffer;
00573 selected_event->midi_buffer_length = midi_buffer_length;
00574
00575 if (smf_event_is_valid(selected_event) == 0) {
00576 g_critical("Event is invalid from the MIDI specification point of view, not created.");
00577 smf_event_delete(selected_event);
00578 selected_event = NULL;
00579 return (-7);
00580 }
00581
00582 smf_track_add_event_seconds(selected_track, selected_event, seconds);
00583
00584 g_message("Event created.");
00585
00586 return (0);
00587 }
00588
00589 static int
00590 cmd_text(char *str)
00591 {
00592 double seconds, type;
00593 char *time, *typestr, *end;
00594
00595 if (selected_track == NULL) {
00596 g_critical("Please select a track first, using 'track <number>' command.");
00597 return (-1);
00598 }
00599
00600 if (str == NULL) {
00601 g_critical("Usage: text <time-in-seconds> <event-type> <text-itself>");
00602 return (-2);
00603 }
00604
00605
00606 time = str;
00607 str = strchr(str, ' ');
00608 if (str != NULL) {
00609 *str = '\0';
00610 str++;
00611 }
00612
00613 seconds = strtod(time, &end);
00614 if (end - time != strlen(time)) {
00615 g_critical("Time is supposed to be a number, without trailing characters.");
00616 return (-3);
00617 }
00618
00619
00620 if (str == NULL) {
00621 g_critical("Usage: text <time-in-seconds> <event-type> <text-itself>");
00622 return (-4);
00623 }
00624
00625
00626 typestr = str;
00627 str = strchr(str, ' ');
00628 if (str != NULL) {
00629 *str = '\0';
00630 str++;
00631 }
00632
00633 type = strtod(typestr, &end);
00634 if (end - typestr != strlen(typestr)) {
00635 g_critical("Type is supposed to be a number, without trailing characters.");
00636 return (-4);
00637 }
00638
00639 if (type < 1 || type > 9) {
00640 g_critical("Valid values for type are 1 - 9, inclusive.");
00641 return (-5);
00642 }
00643
00644
00645 if (str == NULL) {
00646 g_critical("Usage: text <time-in-seconds> <event-type> <text-itself>");
00647 return (-4);
00648 }
00649
00650 selected_event = smf_event_new_textual(type, str);
00651 if (selected_event == NULL) {
00652 g_critical("smf_event_new_textual() failed, event not created.");
00653 return (-6);
00654 }
00655
00656 assert(smf_event_is_valid(selected_event));
00657
00658 smf_track_add_event_seconds(selected_track, selected_event, seconds);
00659
00660 g_message("Event created.");
00661
00662 return (0);
00663 }
00664
00665
00666 static int
00667 cmd_eventaddeot(char *time)
00668 {
00669 double seconds;
00670 char *end;
00671
00672 if (selected_track == NULL) {
00673 g_critical("Please select a track first, using 'track <number>' command.");
00674 return (-1);
00675 }
00676
00677 if (time == NULL) {
00678 g_critical("Please specify the time, in seconds.");
00679 return (-2);
00680 }
00681
00682 seconds = strtod(time, &end);
00683 if (end - time != strlen(time)) {
00684 g_critical("Time is supposed to be a number, without trailing characters.");
00685 return (-3);
00686 }
00687
00688 if (smf_track_add_eot_seconds(selected_track, seconds)) {
00689 g_critical("smf_track_add_eot() failed.");
00690 return (-4);
00691 }
00692
00693 g_message("Event created.");
00694
00695 return (0);
00696 }
00697
00698 static int
00699 cmd_eventrm(char *number)
00700 {
00701 int num = parse_event_number(number);
00702
00703 if (num < 0)
00704 return (-1);
00705
00706 if (selected_event != NULL && num == selected_event->event_number)
00707 selected_event = NULL;
00708
00709 smf_event_delete(smf_track_get_event_by_number(selected_track, num));
00710
00711 g_message("Event #%d removed.", num);
00712
00713 return (0);
00714 }
00715
00716 static int
00717 cmd_tempo(char *notused)
00718 {
00719 int i;
00720 smf_tempo_t *tempo;
00721
00722 for (i = 0;; i++) {
00723 tempo = smf_get_tempo_by_number(smf, i);
00724 if (tempo == NULL)
00725 break;
00726
00727 g_message("Tempo #%d: Starts at %d pulses, %f seconds, setting %d microseconds per quarter note, %.2f BPM.",
00728 i, tempo->time_pulses, tempo->time_seconds, tempo->microseconds_per_quarter_note,
00729 60000000.0 / (double)tempo->microseconds_per_quarter_note);
00730 g_message("Time signature: %d/%d, %d clocks per click, %d 32nd notes per quarter note.",
00731 tempo->numerator, tempo->denominator, tempo->clocks_per_click, tempo->notes_per_note);
00732 }
00733
00734 return (0);
00735 }
00736
00737 static int
00738 cmd_length(char *notused)
00739 {
00740 g_message("Length: %d pulses, %f seconds.", smf_get_length_pulses(smf), smf_get_length_seconds(smf));
00741
00742 return (0);
00743 }
00744
00745 static int
00746 cmd_version(char *notused)
00747 {
00748 g_message("libsmf version %s.", smf_get_version());
00749
00750 return (0);
00751 }
00752
00753 static int
00754 cmd_exit(char *notused)
00755 {
00756 g_debug("Good bye.");
00757 exit(0);
00758 }
00759
00760 static int cmd_help(char *notused);
00761
00762 static struct command_struct {
00763 char *name;
00764 int (*function)(char *command);
00765 char *help;
00766 } commands[] = {{"help", cmd_help, "Show this help."},
00767 {"?", cmd_help, NULL},
00768 {"load", cmd_load, "Load named file."},
00769 {"open", cmd_load},
00770 {"save", cmd_save, "Save to named file."},
00771 {"ppqn", cmd_ppqn, "Show ppqn (aka division), or set ppqn if used with parameter."},
00772 {"format", cmd_format, "Show format, or set format if used with parameter."},
00773 {"tracks", cmd_tracks, "Show number of tracks."},
00774 {"track", cmd_track, "Show number of currently selected track, or select a track."},
00775 {"trackadd", cmd_trackadd, "Add a track and select it."},
00776 {"trackrm", cmd_trackrm, "Remove currently selected track."},
00777 {"events", cmd_events, "Show events in the currently selected track."},
00778 {"event", cmd_event, "Show number of currently selected event, or select an event."},
00779 {"add", cmd_eventadd, "Add an event and select it."},
00780 {"text", cmd_text, "Add textual event and select it."},
00781 {"eventadd", cmd_eventadd, NULL},
00782 {"eot", cmd_eventaddeot, "Add an End Of Track event."},
00783 {"eventaddeot", cmd_eventaddeot, NULL},
00784 {"eventrm", cmd_eventrm, NULL},
00785 {"rm", cmd_eventrm, "Remove currently selected event."},
00786 {"tempo", cmd_tempo, "Show tempo map."},
00787 {"length", cmd_length, "Show length of the song."},
00788 {"version", cmd_version, "Show libsmf version."},
00789 {"exit", cmd_exit, "Exit to shell."},
00790 {"quit", cmd_exit, NULL},
00791 {"bye", cmd_exit, NULL},
00792 {NULL, NULL, NULL}};
00793
00794 static int
00795 cmd_help(char *notused)
00796 {
00797 int i, padding_length;
00798 char padding[COMMAND_LENGTH + 1];
00799 struct command_struct *tmp;
00800
00801 g_message("Available commands:");
00802
00803 for (tmp = commands; tmp->name != NULL; tmp++) {
00804
00805 if (tmp->help == NULL)
00806 continue;
00807
00808 padding_length = COMMAND_LENGTH - strlen(tmp->name);
00809 assert(padding_length >= 0);
00810 for (i = 0; i < padding_length; i++)
00811 padding[i] = ' ';
00812 padding[i] = '\0';
00813
00814 g_message("%s:%s%s", tmp->name, padding, tmp->help);
00815 }
00816
00817 return (0);
00818 }
00819
00825 static void
00826 strip_unneeded_whitespace(char *str, int len)
00827 {
00828 char *src, *dest;
00829 int skip_white = 1;
00830
00831 for (src = str, dest = str; src < dest + len; src++) {
00832 if (*src == '\n' || *src == '\0') {
00833 *dest = '\0';
00834 break;
00835 }
00836
00837 if (isspace(*src)) {
00838 if (skip_white)
00839 continue;
00840
00841 skip_white = 1;
00842 } else {
00843 skip_white = 0;
00844 }
00845
00846 *dest = *src;
00847 dest++;
00848 }
00849
00850
00851 len = strlen(dest);
00852 if (isspace(dest[len - 1]))
00853 dest[len - 1] = '\0';
00854 }
00855
00856 static char *
00857 read_command(void)
00858 {
00859 char *buf;
00860 int len;
00861
00862 #ifdef HAVE_LIBREADLINE
00863 buf = readline("smfsh> ");
00864 #else
00865 buf = malloc(1024);
00866 if (buf == NULL) {
00867 g_critical("Malloc failed.");
00868 return (NULL);
00869 }
00870
00871 fprintf(stdout, "smfsh> ");
00872 fflush(stdout);
00873
00874 buf = fgets(buf, 1024, stdin);
00875 #endif
00876
00877 if (buf == NULL) {
00878 fprintf(stdout, "exit\n");
00879 return (strdup("exit"));
00880 }
00881
00882 strip_unneeded_whitespace(buf, 1024);
00883
00884 len = strlen(buf);
00885
00886 if (len == 0)
00887 return (read_command());
00888
00889 #ifdef HAVE_LIBREADLINE
00890 add_history(buf);
00891 #endif
00892
00893 return (buf);
00894 }
00895
00896 static int
00897 execute_command(char *line)
00898 {
00899 char *command, *args;
00900 struct command_struct *tmp;
00901
00902 command = line;
00903 args = strchr(line, ' ');
00904 if (args != NULL) {
00905 *args = '\0';
00906 args++;
00907 }
00908
00909 for (tmp = commands; tmp->name != NULL; tmp++) {
00910 if (strcmp(tmp->name, command) == 0)
00911 return ((tmp->function)(args));
00912 }
00913
00914 g_warning("No such command: '%s'. Type 'help' to see available commands.", command);
00915
00916 return (-1);
00917 }
00918
00919 static void
00920 read_and_execute_command(void)
00921 {
00922 int ret;
00923 char *command_line, *command, *next_command;
00924
00925 command = command_line = read_command();
00926
00927 do {
00928 next_command = strchr(command, ';');
00929 if (next_command != NULL) {
00930 *next_command = '\0';
00931 next_command++;
00932 }
00933
00934 strip_unneeded_whitespace(command, 1024);
00935 if (strlen(command) > 0) {
00936 ret = execute_command(command);
00937 if (ret)
00938 g_warning("Command finished with error.");
00939 }
00940
00941 command = next_command;
00942
00943 } while (command);
00944
00945 free(command_line);
00946 }
00947
00948 #ifdef HAVE_LIBREADLINE
00949
00950 static char *
00951 smfsh_command_generator(const char *text, int state)
00952 {
00953 static struct command_struct *command = commands;
00954 char *tmp;
00955
00956 if (state == 0)
00957 command = commands;
00958
00959 while (command->name != NULL) {
00960 tmp = command->name;
00961 command++;
00962
00963 if (strncmp(tmp, text, strlen(text)) == 0)
00964 return (strdup(tmp));
00965 }
00966
00967 return (NULL);
00968 }
00969
00970 static char **
00971 smfsh_completion(const char *text, int start, int end)
00972 {
00973 int i;
00974
00975
00976 if (start != 0) {
00977 for (i = 0; i < start; i++) {
00978 if (!isspace(rl_line_buffer[i]))
00979 return (NULL);
00980 }
00981 }
00982
00983 return (rl_completion_matches(text, smfsh_command_generator));
00984 }
00985
00986 #endif
00987
00988 static void
00989 usage(void)
00990 {
00991 fprintf(stderr, "usage: smfsh [-V | file]\n");
00992
00993 exit(EX_USAGE);
00994 }
00995
00996 int
00997 main(int argc, char *argv[])
00998 {
00999 int ch;
01000
01001 while ((ch = getopt(argc, argv, "V")) != -1) {
01002 switch (ch) {
01003 case 'V':
01004 cmd_version(NULL);
01005 exit(EX_OK);
01006
01007 case '?':
01008 default:
01009 usage();
01010 }
01011 }
01012
01013 if (argc > 2)
01014 usage();
01015
01016 g_log_set_default_handler(log_handler, NULL);
01017
01018 smf = smf_new();
01019 if (smf == NULL) {
01020 g_critical("Cannot initialize smf_t.");
01021 return (-1);
01022 }
01023
01024 if (argc == 2)
01025 cmd_load(argv[1]);
01026 else
01027 cmd_trackadd(NULL);
01028
01029 #ifdef HAVE_LIBREADLINE
01030 rl_readline_name = "smfsh";
01031 rl_attempted_completion_function = smfsh_completion;
01032 #endif
01033
01034 for (;;)
01035 read_and_execute_command();
01036
01037 return (0);
01038 }
01039