libcon.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "libcon.h" | ||
| 2 | #include "libstr.h" | ||
| 3 | #include "unicode.h" | ||
| 4 | #include <csignal> | ||
| 5 | |||
| 6 | #ifdef windows | ||
| 7 | // for readconsole | ||
| 8 | #include <windows.h> | ||
| 9 | #endif | ||
| 10 | |||
| 11 | static const char * progname; | ||
| 12 | static const char ** args; | ||
| 13 | static int argsleft; | ||
| 14 | bool libcon_interactive; | ||
| 15 | static const char * usage; | ||
| 16 | |||
| 17 | static bool confirmclose=true; | ||
| 18 | ✗ | void libcon_pause() | |
| 19 | { | ||
| 20 | ✗ | if (confirmclose) | |
| 21 | { | ||
| 22 | ✗ | confirmclose=false; | |
| 23 | #if defined(_WIN32) | ||
| 24 | ✗ | system("pause"); | |
| 25 | #else | ||
| 26 | ✗ | printf("Press Enter to continue"); | |
| 27 | ✗ | getchar(); | |
| 28 | #endif | ||
| 29 | ✗ | confirmclose=true; | |
| 30 | } | ||
| 31 | ✗ | } | |
| 32 | |||
| 33 | ✗ | void libcon_badusage() | |
| 34 | { | ||
| 35 | ✗ | printf("usage: %s %s", progname, usage); | |
| 36 | ✗ | exit(1); | |
| 37 | } | ||
| 38 | |||
| 39 | 8 | static const char * getarg(bool tellusage, const char * defval= nullptr) | |
| 40 | { | ||
| 41 | 8 | if (!argsleft) | |
| 42 | { | ||
| 43 | ✗ | if (tellusage) libcon_badusage(); | |
| 44 | ✗ | return defval; | |
| 45 | } | ||
| 46 | 8 | args++; | |
| 47 | 8 | argsleft--; | |
| 48 | 8 | return args[0]; | |
| 49 | } | ||
| 50 | |||
| 51 | 2 | static bool u8_fgets(char* buffer, int buffer_size, FILE* handle) | |
| 52 | { | ||
| 53 | #if defined(windows) | ||
| 54 | // RPG Hacker: Using buffer_size * 2 here to account for potential surrogate pairs. | ||
| 55 | // The idea is that our buffer here should be able to at least hold the same amount | ||
| 56 | // of characters as the old ANSI version would have supported. | ||
| 57 | ✗ | DWORD num_chars = buffer_size * 2; | |
| 58 | ✗ | wchar_t* w_buf = (wchar_t*)malloc(num_chars * sizeof(wchar_t)); | |
| 59 | // this was originally using fgetws, but it was broken on mingw for some | ||
| 60 | // weird reason. (or, more specifically, msvcrt.dll is broken. msvc itself | ||
| 61 | // doesn't use that one.) | ||
| 62 | ✗ | BOOL res = ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), w_buf, num_chars-1, &num_chars, nullptr); | |
| 63 | ✗ | if(!res) { free(w_buf); return false; } | |
| 64 | ✗ | w_buf[num_chars] = 0; | |
| 65 | //(void)fgetws(w_buf, num_chars, stdin); | ||
| 66 | ✗ | string u8_str; | |
| 67 | ✗ | if (utf16_to_utf8(&u8_str, w_buf)) | |
| 68 | { | ||
| 69 | ✗ | strncpy(buffer, u8_str, buffer_size); | |
| 70 | ✗ | buffer[buffer_size-1] = '\0'; | |
| 71 | // no point doing more than 1 since the next function cuts off input at the first newline anyways | ||
| 72 | ✗ | if(strchr(buffer, '\r')) *(strchr(buffer, '\r')) = '\n'; | |
| 73 | } | ||
| 74 | ✗ | free(w_buf); | |
| 75 | ✗ | return true; | |
| 76 | #else | ||
| 77 | 2 | char* out = fgets(buffer, buffer_size, handle); | |
| 78 | 2 | return (out != NULL); | |
| 79 | #endif | ||
| 80 | ✗ | } | |
| 81 | |||
| 82 | 1 | static const char * requirestrfromuser(const char * question, bool filename) | |
| 83 | { | ||
| 84 | 1 | confirmclose=false; | |
| 85 | 1 | char * rval=(char*)malloc(256); | |
| 86 | 1 | *rval=0; | |
| 87 | 2 | while (!strchr(rval, '\n') || *rval=='\n') | |
| 88 | { | ||
| 89 | 1 | *rval=0; | |
| 90 | 1 | printf("%s ", question); | |
| 91 | 1 | if(!u8_fgets(rval, 250, stdin)) { | |
| 92 | ✗ | fprintf(stderr, "Unexpected end of input\n"); | |
| 93 | ✗ | exit(1); | |
| 94 | } | ||
| 95 | } | ||
| 96 | 1 | *strchr(rval, '\n')=0; | |
| 97 | 1 | confirmclose=true; | |
| 98 | #ifdef _WIN32 | ||
| 99 | ✗ | if (filename && rval[0]=='"' && rval[2]==':') | |
| 100 | { | ||
| 101 | ✗ | char * rvalend=strchr(rval, '\0'); | |
| 102 | ✗ | if (rvalend[-1]=='"') rvalend[-1]='\0'; | |
| 103 | ✗ | return rval+1; | |
| 104 | } | ||
| 105 | #endif | ||
| 106 | 1 | return rval; | |
| 107 | } | ||
| 108 | |||
| 109 | 1 | static const char * requeststrfromuser(const char * question, bool filename, const char * defval) | |
| 110 | { | ||
| 111 | 1 | confirmclose=false; | |
| 112 | 1 | char * rval=(char*)malloc(256); | |
| 113 | 1 | *rval=0; | |
| 114 | 1 | printf("%s ", question); | |
| 115 | 1 | if(!u8_fgets(rval, 250, stdin)) return defval; | |
| 116 | 1 | char *eol = strchr(rval, '\n'); | |
| 117 | 1 | if(!eol) return defval; | |
| 118 | 1 | *eol = 0; | |
| 119 | 1 | confirmclose=true; | |
| 120 | 1 | if (!*rval) return defval; | |
| 121 | #ifdef _WIN32 | ||
| 122 | ✗ | if (filename && rval[0]=='"' && rval[2]==':') | |
| 123 | { | ||
| 124 | ✗ | char * rvalend=strchr(rval, '\0'); | |
| 125 | ✗ | if (rvalend[-1]=='"') rvalend[-1]='\0'; | |
| 126 | ✗ | return rval+1; | |
| 127 | } | ||
| 128 | #endif | ||
| 129 | 1 | return rval; | |
| 130 | } | ||
| 131 | |||
| 132 | 3 | void libcon_init(int argc, const char ** argv, const char * usage_) | |
| 133 | { | ||
| 134 | 3 | progname=argv[0]; | |
| 135 | 3 | args=argv; | |
| 136 | 3 | argsleft=argc-1; | |
| 137 | 3 | usage=usage_; | |
| 138 | 3 | libcon_interactive=(!argsleft); | |
| 139 | #if defined(_WIN32) | ||
| 140 | ✗ | if (libcon_interactive) atexit(libcon_pause); | |
| 141 | #endif | ||
| 142 | 3 | } | |
| 143 | |||
| 144 | 3 | const char * libcon_require_filename(const char * desc) | |
| 145 | { | ||
| 146 | 3 | if (libcon_interactive) return requirestrfromuser(desc, true); | |
| 147 | 2 | else return getarg(true); | |
| 148 | } | ||
| 149 | |||
| 150 | ✗ | const char * libcon_optional(const char * desc, const char * defval) | |
| 151 | { | ||
| 152 | ✗ | if (libcon_interactive) return requeststrfromuser(desc, false, defval); | |
| 153 | ✗ | else return getarg(false, defval); | |
| 154 | } | ||
| 155 | |||
| 156 | 3 | const char * libcon_optional_filename(const char * desc, const char * defval) | |
| 157 | { | ||
| 158 | 3 | if (libcon_interactive) return requeststrfromuser(desc, true, defval); | |
| 159 | 2 | else return getarg(false, defval); | |
| 160 | } | ||
| 161 | |||
| 162 | 6 | const char * libcon_option() | |
| 163 | { | ||
| 164 | 6 | if (!libcon_interactive && argsleft && args[1][0]=='-') return getarg(false); | |
| 165 | 3 | return nullptr; | |
| 166 | } | ||
| 167 | |||
| 168 | 1 | const char * libcon_option_value() | |
| 169 | { | ||
| 170 | 1 | if (!libcon_interactive) return getarg(false); | |
| 171 | ✗ | return nullptr; | |
| 172 | } | ||
| 173 | |||
| 174 | ✗ | bool libcon_question_bool(const char * desc, bool defval) | |
| 175 | { | ||
| 176 | ✗ | if (!libcon_interactive) return defval; | |
| 177 | while (true) | ||
| 178 | { | ||
| 179 | ✗ | const char * answer=requeststrfromuser(desc, false, defval?"y":"n"); | |
| 180 | ✗ | if (!stricmp(answer, "y") || !stricmp(answer, "yes")) return true; | |
| 181 | ✗ | if (!stricmp(answer, "n") || !stricmp(answer, "no")) return false; | |
| 182 | ✗ | } | |
| 183 | } | ||
| 184 | |||
| 185 | 3 | void libcon_end() | |
| 186 | { | ||
| 187 | 3 | if (!libcon_interactive && argsleft) libcon_badusage(); | |
| 188 | 3 | } | |
| 189 |