├── .gitignore ├── CMDread.c ├── CMDread.cmd ├── CMDread.h ├── CMDread.rc ├── CMDread.txt ├── README.md ├── edit.c ├── edit.rc ├── makefile.gcc ├── makefile.vc └── version.h /.gitignore: -------------------------------------------------------------------------------- 1 | # The various object files. 2 | *.[oa] 3 | *.obj 4 | *.res 5 | *.lib 6 | *.exp 7 | 8 | # The binaries. 9 | *.exe 10 | *.dll 11 | 12 | # Configuration. 13 | *.cfg 14 | *.hst 15 | -------------------------------------------------------------------------------- /CMDread.c: -------------------------------------------------------------------------------- 1 | /* 2 | CMDread.c - Enhanced command line editing for CMD.EXE. 3 | 4 | Jason Hood, 24 October to 21 November, 2005 and 20 to 23 December, 2006. 5 | 6 | Injection code derived from Console Manager by Sergey Oblomov (hoopoepg). 7 | Additional information from "Process-wide API spying - an ultimate hack" By 8 | Anton Bassov's article in "The Code Project" (use of OpenThread). 9 | 10 | v1.02, 23 July, 2010: 11 | + add -I/-U to use HKLM. 12 | 13 | v2.00, 22 July to 8 August, 2011: 14 | * compile cleanly with GCC 4; 15 | * slight improvements in finding parent process; 16 | * install as a batch file (CMDread.cmd) to improve load time for "cmd /c"; 17 | * -e applies to any search, not just blank; 18 | - fixed updating the config file; 19 | + option to specify file for a persistent history; 20 | * use specific options for each prompt colour; 21 | + added colour for the prompt's base directory; 22 | + added -_ to control whether underscore is part of a word; 23 | + added -km to choose the selection colour; 24 | * test the config file is readable here, not in edit; 25 | + added --version; 26 | - fixed the status (using wrong value of enabled; future-proof); 27 | * removed NT version 28 | - fixed initial install. 29 | 30 | 14 & 15 June, 2012: 31 | * modified injection (use VirtualAllocEx method, not stack); 32 | + 64-bit version; 33 | - search for the local export (improved future-proofing); 34 | - install/uninstall will replace/remove a string containing "cmdread" or 35 | "cmdkey". 36 | 37 | 21 May, 2013: 38 | - fixed status in 64-bit version. 39 | 40 | 27 May, 2013: 41 | * use CreateRemoteThread injection method (and LoadLibraryW); 42 | - prevent 32/64 mismatch. 43 | 44 | 6 June, 2013: 45 | * renamed from CMDkey to CMDread to avoid potential confusion/conflict with 46 | Microsoft's Cmdkey. 47 | 48 | v2.10, 11 to 24 June, 2013: 49 | * use Unicode; 50 | + -q option to set prefix character to always update the history line; 51 | - verify the registry key is created (HKLM requires admin privileges); 52 | * remove the initial blank line in the stats, add underscore setting, add 53 | processor type; 54 | * use %USERPROFILE% as the default config/history path. 55 | 56 | v2.11, 4 July, 2013: 57 | - fixed file names (GetFullPathNameW doesn't like the same buffers). 58 | 59 | v2.12, 10 July, 2013: 60 | * only write to the registry with an explicit -i; 61 | * read the options here, not from edit. 62 | */ 63 | 64 | #define PDATE L"10 July, 2013" 65 | 66 | #include "CMDread.h" 67 | #include "version.h" 68 | #include 69 | 70 | #ifndef offsetof 71 | # define offsetof(type, member) (size_t)(&(((type*)0)->member)) 72 | #endif 73 | 74 | #ifdef __MINGW32__ 75 | int _CRT_glob = 0; 76 | #endif 77 | 78 | 79 | #define CMDREAD L"Software\\Microsoft\\Command Processor" 80 | #define AUTORUN L"AutoRun" 81 | 82 | #ifdef _WIN64 83 | #define ARCH L"amd64" 84 | #define EDITDLL L"edit_" ARCH L".dll" 85 | #else 86 | #define ARCH L"x86" 87 | #define EDITDLL L"edit.dll" 88 | #endif 89 | 90 | 91 | void status( void ); 92 | void help( void ); 93 | 94 | BOOL find_proc_id( HANDLE snap, DWORD id, LPPROCESSENTRY32, LPPROCESSENTRY32 ); 95 | DWORD GetParentProcessId( void ); 96 | BOOL IsInstalled( DWORD id, PBYTE* base ); 97 | void GetStatus( DWORD id, PBYTE base ); 98 | void Inject( HANDLE hProcess ); 99 | BOOL GetRegKey( LPCWSTR, HKEY, LPCWSTR, PHKEY, LPDWORD ); 100 | BOOL ReadOptions( HKEY, BOOL ); 101 | 102 | 103 | __declspec(dllimport) DWORD parent_pid; 104 | __declspec(dllimport) Option option; 105 | WCHAR cmdname[MAX_PATH]; 106 | __declspec(dllimport) WCHAR cfgname[MAX_PATH]; 107 | __declspec(dllimport) WCHAR hstname[MAX_PATH]; 108 | __declspec(dllimport) BOOL cmd_history; 109 | __declspec(dllimport) Status local; 110 | 111 | 112 | #if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) 113 | // Thanks to Coder for Life. 114 | // http://www.coderforlife.com/projects/utilities/ 115 | int wmain(); 116 | void __wgetmainargs( int*, wchar_t***, wchar_t***, int, int* ); 117 | int main() 118 | { 119 | wchar_t **argv, **envp; 120 | int argc, si = 0; 121 | __wgetmainargs( &argc, &argv, &envp, 0, &si ); 122 | return wmain( argc, argv ); 123 | } 124 | #endif 125 | 126 | int wmain( int argc, wchar_t* argv[] ) 127 | { 128 | DWORD pid; 129 | HANDLE ph; 130 | PBYTE base; 131 | BOOL active, update; 132 | LPWSTR arg; 133 | LPWSTR end; 134 | char* opt; 135 | LPWSTR ops; 136 | char state; 137 | LPWSTR hname; 138 | ULONG num; 139 | HKEY key, root; 140 | DWORD exist; 141 | WCHAR CMDread[MAX_PATH+4]; 142 | UCHAR* colour = NULL; 143 | int j; 144 | DWORD len, type; 145 | LPWSTR cmdpos; 146 | char cp[16]; 147 | 148 | // Thanks to Michael Kaplan. 149 | // http://blogs.msdn.com/b/michkap/archive/2010/10/07/10072032.aspx 150 | // However, it seems the fputws in MSVCRT.DLL (Win7 HP 64-bit) doesn't work 151 | // with _O_U16TEXT. 152 | if (_isatty( 1 )) 153 | _setmode( 1, _O_U16TEXT ); 154 | 155 | // Set the locale code page so wide-string conversions work as expected. 156 | sprintf( cp, ".%u", GetConsoleOutputCP() ); 157 | setlocale( LC_CTYPE, cp ); 158 | 159 | if (argc > 1) 160 | { 161 | if (wcscmp( argv[1], L"--help" ) == 0 || 162 | ((argv[1][0] == '-' || argv[1][0] == '/') && argv[1][1] == '?')) 163 | { 164 | help(); 165 | return 0; 166 | } 167 | if (wcscmp( argv[1], L"--version" ) == 0) 168 | { 169 | _putws( L"CMDread (" ARCH L") version " PVERS L" (" PDATE L")." ); 170 | return 0; 171 | } 172 | } 173 | 174 | pid = GetParentProcessId(); 175 | active = IsInstalled( pid, &base ); 176 | if (!ReadOptions( HKEY_CURRENT_USER, active )) 177 | ReadOptions( HKEY_LOCAL_MACHINE, active ); 178 | if (active) 179 | { 180 | GetStatus( pid, base ); 181 | if (argc == 1) 182 | { 183 | status(); 184 | return 0; 185 | } 186 | } 187 | 188 | update = FALSE; 189 | hname = NULL; 190 | root = HKEY_CURRENT_USER; 191 | 192 | for (j = 1; j < argc; ++j) 193 | { 194 | if (argv[j][0] == '-' || argv[j][0] == '/') 195 | { 196 | if (!argv[j][1]) 197 | { 198 | wprintf( L"CMDread: missing option (argument %d).\n", j ); 199 | return 1; 200 | } 201 | for (arg = argv[j] + 1; *arg; arg = end) 202 | { 203 | if (*arg == '-') 204 | ++arg, state = 0; 205 | else if (*arg == '+') 206 | ++arg, state = 1; 207 | else 208 | state = (active) ? -1 : 1; 209 | if (!*arg) 210 | { 211 | wprintf( L"CMDread: missing option (argument %d).\n", j ); 212 | return 1; 213 | } 214 | opt = NULL; 215 | num = wcstoul( arg + 1, &end, 10 ); 216 | 217 | switch (towlower( *arg )) 218 | { 219 | case '/': break; // allow something like /b/e 220 | 221 | case 'b': opt = &option.no_slash; break; 222 | case 'e': opt = &option.empty_hist; break; 223 | case 'g': opt = &option.silent; break; 224 | case 'o': opt = &option.overwrite; break; 225 | case 'r': opt = &option.auto_recall; break; 226 | case 't': opt = &option.disable_macro; break; 227 | case '_': opt = &option.underscore; break; 228 | 229 | case 'z': option.disable_CMDread = 1; break; 230 | 231 | case 'c': 232 | if (end == arg + 1) 233 | { 234 | if (*end != ',') 235 | { 236 | char temp = option.cursor_size[0]; 237 | option.cursor_size[0] = option.cursor_size[1]; 238 | option.cursor_size[1] = temp; 239 | } 240 | break; 241 | } 242 | 243 | case ',': 244 | if (end == arg + 1) 245 | { 246 | _putws( L"CMDread: missing cursor size." ); 247 | return 1; 248 | } 249 | if (num > 100) 250 | { 251 | _putws( L"CMDread: cursor size must be between 0 and 100." ); 252 | return 1; 253 | } 254 | option.cursor_size[(*arg == ',')] = (char)num; 255 | break; 256 | 257 | case 'k': 258 | end = arg + 1; 259 | switch (*end | 0x20) 260 | { 261 | case 'c': colour = &option.cmd_col; break; 262 | case 'r': colour = &option.rec_col; break; 263 | case 'd': colour = &option.drv_col; break; 264 | case 's': colour = &option.sep_col; break; 265 | case 'p': colour = &option.dir_col; break; 266 | case 'b': colour = &option.base_col; break; 267 | case 'g': colour = &option.gt_col; break; 268 | case 'm': colour = &option.sel_col; break; 269 | default: opt = &option.nocolour; break; 270 | } 271 | if (opt) 272 | break; 273 | ++end; 274 | if (!iswxdigit( *end )) 275 | { 276 | wprintf( L"CMDread: expecting hexadecimal digit for -k%c.\n", 277 | end[-1] | 0x20 ); 278 | return 1; 279 | } 280 | num = (*end > '9') ? (*end | 0x20) - 'a' + 10 : *end - '0'; 281 | if (iswxdigit( *++end )) 282 | { 283 | num = num * 16 + ((*end > '9') ? (*end | 0x20) - 'a' + 10 284 | : *end - '0'); 285 | ++end; 286 | } 287 | *colour = (UCHAR)num; 288 | break; 289 | 290 | case 'p': 291 | end = arg + 1; // on the odd chance of it being a digit 292 | if (!*end) 293 | { 294 | _putws( L"CMDread: missing macro ignore character." ); 295 | return 1; 296 | } 297 | option.ignore_char = *end++; 298 | break; 299 | 300 | case 'q': 301 | end = arg + 1; // on the odd chance of it being a digit 302 | if (!*end) 303 | { 304 | _putws( L"CMDread: missing history update character." ); 305 | return 1; 306 | } 307 | option.update_char = *end++; 308 | break; 309 | 310 | case 'l': 311 | if (end == arg + 1 || num == 0 || num > 255) 312 | { 313 | _putws( L"CMDread: line length must be between 1 and 255." ); 314 | return 1; 315 | } 316 | option.min_length = (UCHAR)num; 317 | break; 318 | 319 | case 'h': 320 | if (num > 255) 321 | { 322 | _putws( L"CMDread: history size must be between 0 and 255." ); 323 | return 1; 324 | } 325 | option.histsize = (UCHAR)num; 326 | break; 327 | 328 | case 'i': 329 | if (*arg == 'I') 330 | root = HKEY_LOCAL_MACHINE; 331 | len = GetModuleFileName( NULL, CMDread + 2, MAX_PATH ) + 2; 332 | _wcslwr( CMDread + 2 ); 333 | CMDread[0] = '&'; 334 | CMDread[1] = '"'; 335 | // Strip the processor type (too bad if it's been renamed). 336 | while (CMDread[--len] != '_' && CMDread[len] != '\\' && len != 0) ; 337 | wcscpy( CMDread + len, L".cmd\"" ); 338 | len += 5; 339 | // Add CMDread to CMD.EXE's AutoRun setting, if not already present. 340 | if (!GetRegKey( L"add AutoRun", root, CMDREAD, &key, &exist )) 341 | return 1; 342 | exist = 0; 343 | RegQueryValueEx( key, AUTORUN, NULL, NULL, NULL, &exist ); 344 | ops = malloc( exist + WSZ(len) ); 345 | if (!ops) 346 | { 347 | _putws( L"CMDread: where's all the memory gone?" ); 348 | return 1; 349 | } 350 | if (exist > sizeof(WCHAR)) 351 | { 352 | RegQueryValueEx( key, AUTORUN, NULL, &type, (LPBYTE)ops, &exist ); 353 | cmdpos = wcsstr( ops, L"cmdread" ); 354 | if (!cmdpos) 355 | cmdpos = wcsstr( ops, L"cmdkey" ); 356 | if (!cmdpos) 357 | { 358 | wcscpy( ops + --exist, CMDread ); 359 | RegSetValueEx( key, AUTORUN, 0, type, (LPBYTE)ops, 360 | exist + WSZ(len) ); 361 | } 362 | else 363 | { 364 | LPWSTR end = cmdpos + 6; 365 | while (cmdpos != ops && *--cmdpos != '"') ; 366 | while (*end != '\0' && *end++ != '"') ; 367 | memmove( cmdpos + len - 1, end, exist - WSZ(end - ops) ); 368 | memcpy( cmdpos, CMDread + 1, WSZ(len - 1) ); 369 | RegSetValueEx( key, AUTORUN, 0, type, (LPBYTE)ops, 370 | WSZ(wcslen( ops ) + 1) ); 371 | } 372 | } 373 | else 374 | { 375 | RegSetValueEx( key, AUTORUN, 0, REG_SZ, (LPBYTE)(CMDread + 1), 376 | WSZ(len) ); 377 | } 378 | RegCloseKey( key ); 379 | free( ops ); 380 | update = TRUE; 381 | break; 382 | 383 | case 'u': 384 | if (*arg == 'U') 385 | root = HKEY_LOCAL_MACHINE; 386 | // Remove CMDread from CMD.EXE's AutoRun setting. 387 | if (!GetRegKey( L"remove AutoRun", root, CMDREAD, &key, &exist )) 388 | return 1; 389 | exist = 0; 390 | RegQueryValueEx( key, AUTORUN, NULL, NULL, NULL, &exist ); 391 | if (exist) 392 | { 393 | ops = malloc( exist ); 394 | if (!ops) 395 | { 396 | _putws( L"CMDread: where's all the memory gone?" ); 397 | return 1; 398 | } 399 | RegQueryValueEx( key, AUTORUN, NULL, &type, (LPBYTE)ops, &exist ); 400 | cmdpos = wcsstr( ops, L"cmdread" ); 401 | if (!cmdpos) 402 | cmdpos = wcsstr( ops, L"cmdkey" ); 403 | if (cmdpos) 404 | { 405 | len = cmdpos - ops + 6; 406 | while (cmdpos != ops && *--cmdpos != '"') ; 407 | while (ops[len] != '\0' && ops[len++] != '"') ; 408 | len -= cmdpos - ops; 409 | if (cmdpos == ops && exist == WSZ(len + 1)) 410 | RegDeleteValue( key, AUTORUN ); 411 | else 412 | { 413 | if (cmdpos > ops && cmdpos[-1] == '&') 414 | --cmdpos, ++len; 415 | else if (cmdpos[len] == '&') 416 | ++len; 417 | memcpy( cmdpos, cmdpos + len, exist - WSZ(len) ); 418 | RegSetValueEx( key, AUTORUN, 0, type, (LPBYTE)ops, 419 | exist - WSZ(len) ); 420 | } 421 | } 422 | free( ops ); 423 | } 424 | RegCloseKey( key ); 425 | active = TRUE; 426 | break; 427 | 428 | case 'f': 429 | hname = arg + 1; 430 | end = wcschr( arg + 1, '\0' ); 431 | cmd_history = TRUE; 432 | break; 433 | 434 | default: 435 | wprintf( L"CMDread: invalid option: '%c'.\n", *arg ); 436 | return 1; 437 | } 438 | if (opt) 439 | { 440 | if (state == -1) 441 | *opt ^= 1; 442 | else 443 | *opt = state; 444 | } 445 | } 446 | } 447 | else 448 | { 449 | FILE* tmp = _wfopen( argv[j], L"r" ); 450 | if (tmp == NULL) 451 | { 452 | wprintf( L"CMDread: could not open \"%s\".\n", argv[j] ); 453 | return 1; 454 | } 455 | fclose( tmp ); 456 | wcscpy( cfgname, argv[j] ); 457 | } 458 | } 459 | if (hname) 460 | { 461 | if (*hname) 462 | GetFullPathName( hname, lenof(local.hstname), local.hstname, NULL ); 463 | else 464 | { 465 | local.hstname[0] = '-'; 466 | local.hstname[1] = '\0'; 467 | cmd_history = FALSE; 468 | } 469 | } 470 | if (hname || !active) 471 | wcscpy( hstname, local.hstname ); 472 | if (!active && !*cfgname) 473 | wcscpy( cfgname, cmdname ); 474 | 475 | if (update) 476 | { 477 | if (!GetRegKey( L"update options", root, REGKEY, &key, &exist )) 478 | return 1; 479 | 480 | if (*cfgname) 481 | GetFullPathName( cfgname, lenof(cmdname), cmdname, NULL ); 482 | 483 | RegSetValueEx( key, L"Options", 0, REG_BINARY, (LPBYTE)&option, 484 | sizeof(option) ); 485 | RegSetValueEx( key, L"Cmdfile", 0, REG_SZ, (LPBYTE)cmdname, 486 | WSZ(wcslen( cmdname ) + 1) ); 487 | RegSetValueEx( key, L"Hstfile", 0, REG_SZ, (LPBYTE)local.hstname, 488 | WSZ(wcslen( local.hstname ) + 1) ); 489 | RegCloseKey( key ); 490 | } 491 | 492 | if (!active) 493 | { 494 | ph = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pid ); 495 | if (ph == NULL) 496 | { 497 | _putws( L"CMDread: could not open parent process." ); 498 | return 1; 499 | } 500 | Inject( ph ); 501 | CloseHandle( ph ); 502 | } 503 | 504 | return 0; 505 | } 506 | 507 | 508 | BOOL ReadOptions( HKEY root, BOOL cfg_only ) 509 | { 510 | HKEY key; 511 | DWORD exist; 512 | 513 | if (RegOpenKeyEx( root, REGKEY, 0, KEY_QUERY_VALUE, &key ) != ERROR_SUCCESS) 514 | { 515 | if (root == HKEY_LOCAL_MACHINE) 516 | { 517 | exist = GetEnvironmentVariable( L"USERPROFILE", cmdname, lenof(cmdname) ); 518 | wcscpy( cmdname + exist, L"\\CMDread.cfg" ); 519 | memcpy( local.hstname, cmdname, WSZ(exist + 9) ); 520 | wcscpy( local.hstname + exist + 9, L"hst" ); 521 | } 522 | return FALSE; 523 | } 524 | 525 | exist = sizeof(cmdname); 526 | RegQueryValueEx( key, L"Cmdfile", NULL, NULL, (LPBYTE)cmdname, &exist ); 527 | exist = sizeof(local.hstname); 528 | RegQueryValueEx( key, L"Hstfile", NULL, NULL, (LPBYTE)local.hstname, &exist ); 529 | 530 | if (!cfg_only) 531 | { 532 | exist = sizeof(option); 533 | RegQueryValueEx( key, L"Options", NULL, NULL, (LPBYTE)&option, &exist ); 534 | if (exist != sizeof(option)) 535 | { 536 | // Update options from earlier versions (I really gotta stop being lazy 537 | // using binary writes...). 538 | if (exist == offsetof(Option, base_col)) 539 | option.base_col = option.dir_col; 540 | if (exist == offsetof(Option, ignore_char)) 541 | option.ignore_char = option.old_ignore_char; 542 | } 543 | } 544 | RegCloseKey( key ); 545 | 546 | return TRUE; 547 | } 548 | 549 | 550 | // Display the current status of CMDread. 551 | void status( void ) 552 | { 553 | WCHAR buf[4]; 554 | WCHAR name[MAX_PATH+2]; 555 | WCHAR hst[MAX_PATH+8]; 556 | 557 | if (local.version != PVERX) 558 | { 559 | wprintf( L"This CMDread is version %x.%.2x, but installed edit DLL is ", 560 | PVERX >> 8, PVERX & 0xFF ); 561 | if (local.version == 0) 562 | _putws( L"unknown." ); 563 | else 564 | wprintf( L"%x.%.2x.\n", local.version >> 8, local.version & 0xFF ); 565 | return; 566 | } 567 | 568 | if (option.histsize) 569 | _itow( option.histsize, buf, 10 ); 570 | else 571 | wcscpy( buf, L"all" ); 572 | 573 | if (*local.hstname) 574 | _snwprintf( hst, lenof(hst), L"\"%s\"", local.hstname ); 575 | else 576 | wcscpy( hst, L"none" ); 577 | 578 | if (*cmdname) 579 | _snwprintf( name, lenof(name), L"\"%s\"", cmdname ); 580 | else 581 | wcscpy( name, L"none" ); 582 | 583 | wprintf( L"* %s mode is default.\n" 584 | L"* Cursor sizes: insert = %d%%, overwrite = %d%%.\n" 585 | L"* Backslash appending is %sabled.\n" 586 | L"* History search %s.\n" 587 | L"* Auto-recall is %sabled.\n" 588 | L"* Translation is %sabled.\n" 589 | L"* Error bell is %sabled.\n" 590 | L"* Underscore is%s part of a word.\n" 591 | L"* Ignore character is '%c'.\n" 592 | L"* Update character is '%c'.\n" 593 | L"* Minimum history line length is %d.\n" 594 | L"* History will remember %s lines.\n" 595 | L"* History file: %s.\n" 596 | L"* Configuration file: %s.\n" 597 | L"* CMDread (" ARCH L") is %sabled.\n", 598 | (option.overwrite) ? L"Overwrite" : L"Insert", 599 | option.cursor_size[0], option.cursor_size[1], 600 | (option.no_slash) ? L"dis" : L"en", 601 | (option.empty_hist)? L"moves cursor to end" : L"doesn't move cursor", 602 | (option.auto_recall) ? L"en" : L"dis", 603 | (option.disable_macro) ? L"dis" : L"en", 604 | (option.silent) ? L"dis" : L"en", 605 | (option.underscore) ? L"" : L" not", 606 | option.ignore_char, 607 | option.update_char, 608 | option.min_length, 609 | buf, 610 | hst, 611 | name, 612 | (local.enabled) ? L"en" : L"dis" 613 | ); 614 | } 615 | 616 | 617 | // Create or open registry key "root\subkey", checking that it succeeded. 618 | BOOL GetRegKey( LPCWSTR op, HKEY root, LPCWSTR subkey, PHKEY key, LPDWORD disp ) 619 | { 620 | if (ERROR_SUCCESS != RegCreateKeyEx( root, subkey, 0, NULL, 0, 621 | KEY_ALL_ACCESS, NULL, key, disp )) 622 | { 623 | wprintf( L"CMDread: could not %s", op ); 624 | if (root == HKEY_LOCAL_MACHINE) 625 | wprintf( L" (perhaps use -i/-u, or run as admin)" ); 626 | _putws( L"." ); 627 | return FALSE; 628 | } 629 | return TRUE; 630 | } 631 | 632 | 633 | // Search each process in the snapshot for id. 634 | BOOL find_proc_id( HANDLE snap, DWORD id, LPPROCESSENTRY32 pe, 635 | LPPROCESSENTRY32 ppe ) 636 | { 637 | BOOL fOk; 638 | 639 | pe->dwSize = sizeof(PROCESSENTRY32); 640 | for (fOk = Process32First( snap, pe ); fOk; fOk = Process32Next( snap, pe )) 641 | { 642 | if (pe->th32ProcessID == id) 643 | break; 644 | *ppe = *pe; 645 | } 646 | 647 | return fOk; 648 | } 649 | 650 | 651 | // Obtain the process identifier of the parent process; verify the architecture. 652 | DWORD GetParentProcessId( void ) 653 | { 654 | HANDLE hSnap, ph; 655 | PROCESSENTRY32 pe, ppe; 656 | BOOL parent_wow64, me_wow64; 657 | typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS)( HANDLE, PBOOL ); 658 | LPFN_ISWOW64PROCESS fnIsWow64Process; 659 | 660 | hSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 ); 661 | if (hSnap == INVALID_HANDLE_VALUE) 662 | { 663 | _putws( L"CMDread: unable to obtain process snapshot." ); 664 | exit( 1 ); 665 | } 666 | 667 | if (!find_proc_id( hSnap, GetCurrentProcessId(), &pe, &ppe )) 668 | { 669 | _putws( L"CMDread: could not find my process ID." ); 670 | exit( 1 ); 671 | } 672 | if (ppe.th32ProcessID == pe.th32ParentProcessID) 673 | pe = ppe; 674 | else if (!find_proc_id( hSnap, pe.th32ParentProcessID, &pe, &ppe )) 675 | { 676 | _putws( L"CMDread: could not find my parent's process ID." ); 677 | exit( 1 ); 678 | } 679 | parent_pid = pe.th32ParentProcessID; 680 | 681 | CloseHandle( hSnap ); 682 | 683 | fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress( 684 | GetModuleHandle( L"kernel32.dll" ), "IsWow64Process" ); 685 | if (fnIsWow64Process != NULL) 686 | { 687 | ph = OpenProcess( PROCESS_QUERY_INFORMATION, FALSE, pe.th32ProcessID ); 688 | if (ph == NULL) 689 | { 690 | _putws( L"CMDread: could not open parent process." ); 691 | exit( 1 ); 692 | } 693 | fnIsWow64Process( ph, &parent_wow64 ); 694 | fnIsWow64Process( GetCurrentProcess(), &me_wow64 ); 695 | CloseHandle( ph ); 696 | 697 | if (parent_wow64 != me_wow64) 698 | { 699 | wprintf( L"CMDread: Cannot use %d-bit CMDread with %d-bit CMD.EXE.\n", 700 | (me_wow64) ? 32 : 64, (parent_wow64) ? 32 : 64 ); 701 | exit( 1 ); 702 | } 703 | } 704 | 705 | return pe.th32ProcessID; 706 | } 707 | 708 | 709 | // Determine if CMDread is already installed in the parent. 710 | BOOL IsInstalled( DWORD id, PBYTE* base ) 711 | { 712 | HANDLE hModuleSnap; 713 | MODULEENTRY32 me; 714 | BOOL fOk; 715 | 716 | *base = NULL; 717 | 718 | // Take a snapshot of all modules in the current process. 719 | hModuleSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, id ); 720 | 721 | if (hModuleSnap == INVALID_HANDLE_VALUE) 722 | { 723 | _putws( L"CMDread: unable to obtain module snapshot." ); 724 | return FALSE; 725 | } 726 | 727 | // Fill the size of the structure before using it. 728 | me.dwSize = sizeof(MODULEENTRY32); 729 | 730 | // Walk the module list of the modules 731 | for (fOk = Module32First( hModuleSnap, &me ); fOk; 732 | fOk = Module32Next( hModuleSnap, &me )) 733 | { 734 | if (_wcsicmp( me.szModule, EDITDLL ) == 0) 735 | { 736 | *base = me.modBaseAddr; 737 | break; 738 | } 739 | } 740 | CloseHandle( hModuleSnap ); 741 | 742 | return fOk; 743 | } 744 | 745 | 746 | // Read the local variables from the parent process. 747 | void GetStatus( DWORD id, PBYTE base ) 748 | { 749 | Status* plocal = NULL; 750 | HANDLE parent = OpenProcess( PROCESS_VM_READ, FALSE, id ); 751 | local.version = 0; 752 | if (parent) 753 | { 754 | PIMAGE_DOS_HEADER pDosHeader; 755 | PIMAGE_NT_HEADERS pNTHeader; 756 | PIMAGE_EXPORT_DIRECTORY pExportDir; 757 | PDWORD ExportNameTable; 758 | PBYTE ExportBase; 759 | DWORD ord; 760 | BYTE buf[512]; 761 | 762 | // Locate the "local" export. 763 | #define MakeVA( cast, base, addValue ) \ 764 | (cast)((DWORD_PTR)(base) + (DWORD_PTR)(addValue)) 765 | 766 | ReadProcessMemory( parent, base, buf, sizeof(buf), NULL ); 767 | pDosHeader = (PIMAGE_DOS_HEADER)buf; 768 | pNTHeader = MakeVA( PIMAGE_NT_HEADERS, pDosHeader, pDosHeader->e_lfanew ); 769 | pExportDir = MakeVA( PIMAGE_EXPORT_DIRECTORY, base, 770 | pNTHeader->OptionalHeader. 771 | DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]. 772 | VirtualAddress ); 773 | 774 | // Bail out if the RVA of the exports section is 0 (it doesn't exist). 775 | // This should only happen if there's another edit DLL injected before us. 776 | if ((PBYTE)pExportDir == base) 777 | { 778 | CloseHandle( parent ); 779 | return; 780 | } 781 | 782 | ReadProcessMemory( parent, pExportDir, buf, sizeof(buf), NULL ); 783 | ExportBase = buf + (base - (PBYTE)pExportDir); 784 | pExportDir = (PIMAGE_EXPORT_DIRECTORY)buf; 785 | ExportNameTable = MakeVA( PDWORD, ExportBase, pExportDir->AddressOfNames ); 786 | for (ord = pExportDir->NumberOfNames; (int)--ord >= 0;) 787 | { 788 | PSTR pszExportName = MakeVA( PSTR, ExportBase, ExportNameTable[ord] ); 789 | if (strcmp( pszExportName, "local" ) == 0) 790 | { 791 | WORD* ExportOrdinalTable = MakeVA( WORD*, ExportBase, 792 | pExportDir->AddressOfNameOrdinals ); 793 | DWORD* ExportFunctionTable = MakeVA( DWORD*, ExportBase, 794 | pExportDir->AddressOfFunctions ); 795 | ord = ExportOrdinalTable[ord]; 796 | plocal = MakeVA( Status*, base, ExportFunctionTable[ord] ); 797 | break; 798 | } 799 | } 800 | if (plocal) 801 | ReadProcessMemory( parent, plocal, &local, sizeof(local), NULL ); 802 | else 803 | { 804 | // Read the timestamp in the header to determine the released version. 805 | DWORD tstamp; 806 | ReadProcessMemory( parent, base + 0x88, &tstamp, 4, NULL ); 807 | switch (tstamp) 808 | { 809 | case 0x458cdf26: local.version = 0x100; break; 810 | case 0x45ff9109: local.version = 0x101; break; 811 | case 0x4c494fe9: local.version = 0x102; break; 812 | } 813 | } 814 | CloseHandle( parent ); 815 | } 816 | } 817 | 818 | 819 | // Inject code into the target process to load our DLL. 820 | void Inject( HANDLE hProcess ) 821 | { 822 | WCHAR dll[MAX_PATH]; 823 | LPWSTR name, path; 824 | DWORD len; 825 | LPVOID mem; 826 | LPVOID LLW; 827 | HANDLE thread; 828 | 829 | len = GetModuleFileName( NULL, dll, lenof(dll) ) + 1; 830 | for (name = path = dll; *path; ++path) 831 | if (*path == '\\') 832 | name = path + 1; 833 | wcscpy( name, EDITDLL ); 834 | 835 | LLW = GetProcAddress( GetModuleHandle( L"kernel32.dll" ), "LoadLibraryW" ); 836 | mem = VirtualAllocEx( hProcess, NULL, len, MEM_COMMIT, PAGE_READWRITE ); 837 | WriteProcessMemory( hProcess, mem, dll, WSZ(len), NULL ); 838 | thread = CreateRemoteThread( hProcess, NULL, 4096, LLW, mem, 0, NULL ); 839 | WaitForSingleObject( thread, INFINITE ); 840 | CloseHandle( thread ); 841 | VirtualFreeEx( hProcess, mem, 0, MEM_RELEASE ); 842 | } 843 | 844 | 845 | void help( void ) 846 | { 847 | _putws( 848 | L"CMDread by Jason Hood .\n" 849 | L"Version " PVERS L" (" PDATE L"). Freeware.\n" 850 | L"http://cmdkey.adoxa.vze.com/\n" 851 | L"\n" 852 | #ifdef _WIN64 853 | L"Provide enhanced command line editing for CMD.EXE (64-bit).\n" 854 | #else 855 | L"Provide enhanced command line editing for CMD.EXE (32-bit).\n" 856 | #endif 857 | L"\n" 858 | L"CMDread [-begkortz_] [-c[INS][,OVR]] [-h[HIST]] [-lLEN] [-pCHAR] [-qCHAR]\n" 859 | L" [-kcCMD] [-kmSEL] [-krREC] [-kdDRV] [-ksSEP] [-kpDIR] [-kbBASE] [-kgGT]\n" 860 | L" [-f[HISTFILE]] [CFGFILE] [-iIuU]\n" 861 | L"\n" 862 | L" -b\t\tdisable backslash appending for completed directories\n" 863 | L" -c\t\tswap insert and overwrite cursors, or set their size\n" 864 | L" -e\t\tsearching history will move cursor to the end\n" 865 | L" -f\t\tfile to store persistent history (none means don't store)\n" 866 | L" -g\t\tsilent mode\n" 867 | L" -h\t\tremember the last HIST commands (0 will remember everything)\n" 868 | L" -k\t\tdisable colouring\n" 869 | L" -l\t\tminimum line length to remember\n" 870 | L" -o\t\tdefault overwrite mode\n" 871 | L" -p\t\tuse CHAR to disable translation for the current line\n" 872 | L" -q\t\tuse CHAR to update the line in the history\n" 873 | L" -r\t\tdefault auto-recall mode\n" 874 | L" -t\t\tdisable translation\n" 875 | L" -z\t\tdisable CMDread\n" 876 | L" -_\t\tunderscore is not part of a word\n" 877 | L" CFGFILE\tfile containing CMDread commands and/or history lines\n" 878 | L"\n" 879 | L" CMD\t\tcolour of the command line\n" 880 | L" SEL\t\tcolour of selected text\n" 881 | L" REC\t\tcolour when recording a macro\n" 882 | L" DRV\t\tcolour of the prompt's drive letter and colon\n" 883 | L" SEP\t\tcolour of the prompt's directory separator\n" 884 | L" DIR\t\tcolour of the prompt's directory\n" 885 | L" BASE\tcolour of the prompt's base directory\n" 886 | L" GT\t\tcolour of the prompt's greater-than sign\n" 887 | L"\n" 888 | L" -i\t\tinstall (add to CMD's AutoRun registry entry and make the\n" 889 | L" \t\tcurrent options the default)\n" 890 | L" -u\t\tuninstall (remove from AutoRun)\n" 891 | L" -I -U\tuse local machine instead of current user (if permitted)\n" ); 892 | _putws( // too big for a single statement? 893 | L"CMDread with no arguments will either install itself into the current CMD.EXE\n" 894 | L"or display the status of the already running instance. When CMDread is already\n" 895 | L"running, options -begkort_ will toggle the current state; prefix them with '+'\n" 896 | L"to explicitly turn on (set behaviour indicated above) or with '-' to turn off\n" 897 | L"(set default behaviour). Eg: \"CMDread -+b-g\" will disable backslash appending\n" 898 | L"and enable the beep, irrespective of the current state.\n" 899 | L"A colour is one or two hex digits; see CMD's COLOR help." 900 | ); 901 | } 902 | -------------------------------------------------------------------------------- /CMDread.cmd: -------------------------------------------------------------------------------- 1 | :: Having the exe in AutoRun causes a noticeable slow down in some batch files 2 | :: (not due to anything it does, just the very act of loading it). Running a 3 | :: batch file has no such drawback, so use this to detect if the first argument 4 | :: is "/C", in which case there's no need to load it. 5 | @SetLocal EnableExtensions 6 | :: Strip quotes, since FOR really doesn't like it when they're unbalanced. 7 | @set "cmdline=%CMDCMDLINE:"=%" 8 | :: This relies on %ComSpec% not having spaces. 9 | @for /f "tokens=2" %%j in ("%cmdline%") do @set arg1=%%j 10 | @if /i "%arg1%" NEQ "/c" "%~dpn0_%PROCESSOR_ARCHITECTURE%.exe" %* 11 | -------------------------------------------------------------------------------- /CMDread.h: -------------------------------------------------------------------------------- 1 | /* 2 | CMDread.h - Header file for CMDread. 3 | 4 | Jason Hood, 24 October, 2005. 5 | */ 6 | 7 | 8 | #ifdef _MSC_VER 9 | #pragma warning (disable:4018) // ignore signed/unsigned mismatch 10 | #pragma warning (disable:4267) // ignore conversion data loss (64-bit to 32) 11 | #pragma warning (disable:4244) // ignore conversion data loss (64-bit ptr sub) 12 | #endif 13 | 14 | #ifndef UNICODE 15 | #define UNICODE 16 | #endif 17 | #define _WIN32_WINNT 0x0500 18 | #define WIN32_LEAN_AND_MEAN 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #ifndef _O_U16TEXT 29 | #define _O_U16TEXT 0x20000 30 | #endif 31 | 32 | #define WSZ(len) ((len) * sizeof(WCHAR)) // byte size of a wide string 33 | #define lenof(a) (sizeof(a) / sizeof(*a)) // elements in a static array 34 | 35 | 36 | typedef struct 37 | { 38 | char cursor_size[2]; // insert/overwrite cursor size 39 | char overwrite; // default insert/overwrite mode 40 | char silent; // don't beep on errors? 41 | char auto_recall; // auto-recall commands 42 | char disable_macro; // disable macro & symbol translation 43 | char disable_CMDread; // disable CMDread 44 | char no_slash; // don't append backslash on completed dirs 45 | char empty_hist; // move cursor to end on empty history search 46 | char old_ignore_char; // prefix character to disable translation 47 | UCHAR min_length; // minimum line length to store in history 48 | UCHAR histsize; // number of commands to store in history 49 | char nocolour; // disable colouring 50 | UCHAR cmd_col; // command line colour 51 | UCHAR rec_col; // recording colour 52 | UCHAR drv_col; // prompt's drive colour 53 | UCHAR sep_col; // prompt's directory separator colour 54 | UCHAR dir_col; // prompt's directory colour 55 | UCHAR gt_col; // prompt's greater-than colour 56 | UCHAR base_col; // prompt's base directory colour 57 | UCHAR sel_col; // selection colour 58 | char underscore; // is underscore part of a word? 59 | WCHAR ignore_char; // prefix character to disable translation 60 | WCHAR update_char; // prefix character to update history line 61 | } Option; 62 | 63 | 64 | typedef struct 65 | { 66 | int version; 67 | char enabled; // is this instance active? 68 | WCHAR hstname[MAX_PATH]; // the history file 69 | } Status; 70 | 71 | 72 | #define REGKEY L"Software\\Adoxa\\CMDkey" 73 | -------------------------------------------------------------------------------- /CMDread.rc: -------------------------------------------------------------------------------- 1 | /* 2 | CMDread.rc - Version resource for CMDread.exe. 3 | 4 | Jason Hood, 23 July, 2010. 5 | */ 6 | 7 | #include 8 | #include "version.h" 9 | 10 | #ifdef _WIN64 11 | #define CMDREADEXE "CMDread_amd64.exe" 12 | #else 13 | #define CMDREADEXE "CMDread_x86.exe" 14 | #endif 15 | 16 | VS_VERSION_INFO VERSIONINFO 17 | FILEVERSION PVERB 18 | PRODUCTVERSION PVERB 19 | FILEOS VOS_NT 20 | FILETYPE VFT_APP 21 | { 22 | BLOCK "StringFileInfo" 23 | { 24 | BLOCK "040904B0" 25 | { 26 | VALUE "Comments", "http://cmdkey.adoxa.vze.com/" 27 | VALUE "CompanyName", "Jason Hood" 28 | VALUE "FileDescription", "Command Line Editor" 29 | VALUE "FileVersion", PVERSA 30 | VALUE "InternalName", "CMDread" 31 | VALUE "LegalCopyright", "Freeware" 32 | VALUE "OriginalFilename", CMDREADEXE 33 | VALUE "ProductName", "CMDread" 34 | VALUE "ProductVersion", PVERSA 35 | } 36 | } 37 | 38 | BLOCK "VarFileInfo" 39 | { 40 | VALUE "Translation", 0x0409, 0x04B0 41 | } 42 | } 43 | 44 | // Add a manifest for the 32-bit version, to prevent registry redirection when 45 | // trying to use HKLM. 46 | #ifndef _WIN64 47 | 1 24 48 | { 49 | "\ 50 | \ 51 | \ 52 | \ 53 | \ 54 | \ 55 | \ 56 | \ 57 | \ 58 | " 59 | } 60 | #endif 61 | -------------------------------------------------------------------------------- /CMDread.txt: -------------------------------------------------------------------------------- 1 | 2 | CMDread 3 | 4 | Copyright 2006-13 Jason Hood 5 | 6 | Version 2.12. Freeware 7 | 8 | 9 | Description 10 | =========== 11 | 12 | CMDread enhances the normal command line editing of CMD.EXE. It provides 13 | more editing functions and improved file name completion, along with key- 14 | board shortcuts to most items on the Edit menu. Symbols and macros can run 15 | frequently used command lines and its own associations can run console- 16 | specific programs. Macros can also be used to record and play back a seq- 17 | uence of keystrokes. 18 | 19 | 20 | Requirements 21 | ============ 22 | 23 | Windows 2000 or later. 24 | 25 | 26 | Usage 27 | ===== 28 | 29 | Running "CMDread" will install CMDread into its parent process (typically 30 | CMD.EXE, but it may also work with other command processors). Usually, 31 | though, you will use "-i" to start it with every instance of CMD.EXE. If 32 | it's already installed, "CMDread" by itself (without any options) will 33 | display the running status. 34 | 35 | Upgrading 36 | --------- 37 | 38 | Run "CMDread -i" (and/or "CMDread -I" as admin) to reflect the new name. 39 | Delete "cmdkey.*" except for "cmdkey.{cfg,hst}". 40 | 41 | Options 42 | ------- 43 | 44 | There are numerous options to control CMDread's behaviour. Briefly: 45 | 46 | -b disable backslash appending for completed directories 47 | -c swap insert and overwrite cursors, or set their size 48 | -e searching history will move the cursor to the end 49 | -f specify/disallow history file 50 | -g silent mode 51 | -h number of commands to remember 52 | -i install 53 | -k disable colouring, or set colours 54 | -l minimum line length to remember 55 | -o default overwrite mode 56 | -p set character to disable translation for the current line 57 | -q set character to update the line in history 58 | -r default auto-recall mode 59 | -t disable translation 60 | -u uninstall 61 | -z disable CMDread 62 | -_ treat underscore as punctuation, not part of a word 63 | 64 | Options can also start with "/" and may be combined (e.g.: "-c /o", "/c/o" 65 | and "-co" are all equivalent). An option may be prefixed with "-" or "+" 66 | to explicitly set its behaviour, otherwise it toggles (e.g.: "-o" will 67 | toggle between insert and overwrite modes, "--o" will set insert mode and 68 | "-+o" will set overwrite mode). Except for "-z" all these options apply to 69 | every instance of CMDread. 70 | 71 | -b - Backslash appending 72 | 73 | When completing a directory CMDread will normally add a backslash (or slash 74 | if that's what you've used) after it ("dir" --> "directory\"). Use this 75 | option to prevent that (like CMD.EXE, "dir" --> "directory"). 76 | 77 | -c - Cursor 78 | 79 | The default insert cursor size is 25% of the font height and the overwrite 80 | size is 50%. This option will swap those values, making insert 50% and 81 | overwrite 25%. Alternatively, it can set the cursor sizes: "-c75,100" will 82 | make the insert cursor 75% and the overwrite 100%. It is possible to set 83 | one without the other: "-c75" will make insert 75%, leaving overwrite at 84 | 50%; "-c,100" will make overwrite 100%, leaving insert at 25%. 85 | 86 | -e - Move to end 87 | 88 | Searching the history for a matching command will normally leave the cursor 89 | where it is (i.e. at the common prefix); this option will move it to the 90 | end, like the history movement commands. 91 | 92 | -f - History file 93 | 94 | The "primary" instance of CMDread will save its history to this file when 95 | CMD.EXE exits and restore it when next run. The default is "CMDread.hst" 96 | in %USERPROFILE%; if no file is specified, the history will not be saved 97 | (i.e. without "-f" it will save to %USERPROFILE%\CMDread.hst; with "-f" it 98 | will not save history; and with "-fFILE" it will save to FILE). The pri- 99 | mary instance is the first one to load, or the next one loaded after the 100 | first one exits. If you've chosen to remember every line, "only" the last 101 | 1000 will be saved. 102 | 103 | -g - Silent 104 | 105 | Certain operations will beep to let you know something has happened (e.g. 106 | going through all the completed file names). Use this option to suppress 107 | the beep and make CMDread quiet. 108 | 109 | -h - History size 110 | 111 | This option sets the number of command lines CMDread will remember in its 112 | history. The default is 50; it may be set to between 0 and 255, inclu- 113 | sive, with 0 meaning it will remember every line. 114 | 115 | -i - Install 116 | 117 | Use this option to make CMDread permanent. It will append itself to 118 | CMD.EXE's AutoRun registry setting, so every time CMD.EXE runs it will also 119 | run CMDread. This option also has the effect of making the current options 120 | the new defaults. Normally it installs for the current user; use -I to 121 | install for the local machine (if permissions allow). 122 | 123 | -k - Colours 124 | 125 | CMDread can (and, by default, does) spice up the command line with a bit of 126 | colour. This option can turn off the colours, or set the colours you would 127 | like to use: 128 | 129 | -kcCMD - the command line itself 130 | -kmSEL - selected text 131 | -krREC - recording a macro 132 | -kdDRV - prompt's drive letter and colon 133 | -ksSEP - prompt's path separators 134 | -kpDIR - prompt's directories 135 | -kbBASE - prompt's base directory 136 | -kgGT - prompt's greater-than sign 137 | 138 | Each uppercase sequence above is a one- or two-digit hexadecimal number 139 | (see CMD.EXE's own COLOR help). The prompt colours are only for the usual 140 | $P$G prompt (any other prompt will remain uncoloured); the base directory 141 | is the final directory component. The default colours are "-kc1f -km79 142 | -kr1b -kd1b -ks1e -kp1a -kb1a -kg1e" which is blue background, bright white 143 | command line, bright blue on grey selection, bright cyan recording and 144 | drive letter/colon, bright yellow separator and greater-than sign and 145 | bright green directory components. Try using "-kp12" to see the effect of 146 | the base directory. 147 | 148 | -l - Length 149 | 150 | Lines smaller than a certain length can be excluded from being added to the 151 | history by using this option. The default is 1 (remember all lines) and 152 | the maximum is 255. 153 | 154 | -o - Overwrite 155 | 156 | CMDread usually functions in insert mode, where current characters are 157 | pushed aside to make room for new characters. This option sets overwrite 158 | mode, where current characters are replaced by new ones. 159 | 160 | -p - Ignore character 161 | 162 | Starting a line with the character specified by this option will cause 163 | CMDread to ignore its usual translations and leave the line unmodified. 164 | The default character is space. 165 | 166 | -q - Update character 167 | 168 | Starting a line with the character specified by this option will cause 169 | CMDread to replace the current line in history, rather than creating a new 170 | one. This character must be first; ignore character (above) and dosify 171 | (see below) must come after. This character also reverses the effects of 172 | the Enter/UpdateEnter and StoreErase/UpdateErase functions (see below). 173 | 174 | -r - Auto-recall 175 | 176 | Auto-recall automatically searches the history as each character is 177 | entered. This option will enable it by default. 178 | 179 | -t - Disable translation 180 | 181 | Prevent CMDread from applying its usual translations. 182 | 183 | -u - Uninstall 184 | 185 | Remove the AutoRun entry added by "-i" ("-U" will remove "-I"). CMDread 186 | will still remain active until CMD.EXE itself exits. 187 | 188 | -z - Disable 189 | 190 | Disable CMDread, restoring the original input behaviour. 191 | 192 | -_ - Underscore 193 | 194 | Underscore is usually treated as part of a word (e.g. "abc_123" forms a 195 | single word). This option treats underscore as punctuation (the prior 196 | example becomes two words). 197 | 198 | Finally, a file name can be given to configure CMDread. The default name 199 | is "CMDread.cfg" in %USERPROFILE%. The file can contain anything: lines 200 | beginning with a dash ("-") and blank lines are ignored (unless they are 201 | part of a macro definition); a CMDread internal command will be executed; 202 | anything else will be stored in the history. The format of the commands in 203 | the file is the same as if you typed them from the command prompt, except 204 | there is no need to escape "^". Lines may be up to 2046 characters long. 205 | The file is interpreted using the system OEM code page, unless it starts 206 | with the UTF-8 byte-order mark. 207 | 208 | Configuration can be customised according to the initial directory by 209 | starting a line with "#" followed by the directory (using the full path, 210 | without any quotes). If the immediate next line starts with "=", what 211 | follows is the history file; otherwise the normal primary rule applies. 212 | The history file's path will be taken relative to the config file. 213 | 214 | Keys 215 | ---- 216 | 217 | CMDread recognises all the normal control keys, shift and control keys, 218 | normal, shift, control, and alt editing and function keys, and normal, 219 | shift, control, and shift and control backspace, tab, enter and escape. 220 | Control is represented by "^", shift by "#", shift and control by "#^" (but 221 | not "^#") and alt by "@". The normal control keys are "^A" to "^Z", plus: 222 | 223 | ^@ - Ctrl+2 ^[ 224 | ^^ - Ctrl+6 ^] 225 | ^_ - Ctrl+- ^\ 226 | 227 | The names of the other keys are: 228 | 229 | Bksp Backspace Ins Insert 230 | Del Delete Left Arrow left 231 | Down Arrow down PgDn Page down 232 | End End PgUp Page up 233 | Enter Return Right Arrow right 234 | Esc Escape Tab Tab 235 | Home Home Up Arrow up 236 | 237 | Function keys are simply "F1" to "F12". 238 | 239 | There are also shortcut keys to the Edit menu: 240 | 241 | Alt+C Mark 242 | Alt+F Find... 243 | Alt+S Scroll 244 | Alt+V Paste 245 | 246 | Alt+keypad entry has been improved, too. A leading zero will make the num- 247 | ber hexadecimal, using "/*-+Enter." as hex digits A to F. A decimal number 248 | between 1 and 255 will be taken as an OEM (code page) character, otherwise 249 | it will be treated as Unicode (up to U+FFFF). 250 | 251 | DEFK - Define key 252 | 253 | Assign a function, macro or command to a key. Without an assignment it 254 | will remove the current one (same as DELK). 255 | 256 | DEFK ^A 257 | DEFK ^A Ignore 258 | 259 | will cause CMDread to ignore Ctrl+A. 260 | 261 | DEFK ^A =cls 262 | DEFK ^A Erase "cls" Execute 263 | 264 | will cause CMDread to replace the current line with "cls" and enter it. 265 | 266 | DEFK @Up -cd .. 267 | DEFK @Up Erase "cd .." HiddenEx 268 | 269 | will use Alt+Up to move to the parent directory, staying on the same line. 270 | 271 | DELK - Delete key(s) 272 | 273 | Remove the assignments (assign to Ignore) of the specified keys. 274 | 275 | LSTK - List key(s) 276 | 277 | List the assignments of every key, or just those specified. When listing 278 | every key, all the normal keys will be displayed, but the modified keys 279 | will not display the ignored ones. 280 | 281 | Functions 282 | --------- 283 | 284 | The editing functions (in alphabetical order) with their default keys and a 285 | brief description: 286 | 287 | Function Key(s) Description 288 | -------------------------------------------------------------------------- 289 | AutoRecall ^Y toggle auto-recall 290 | BegLine ^A Home move cursor to the beginning of the line 291 | CharLeft ^B Left move cursor one character to the left 292 | CharRight ^F Right move cursor one character to the right 293 | CmdSep ^S ^] command separator 294 | CopyFromPrev F3 copy remainder of line from previous command 295 | Cut ^C #Del cut selected text or current/previous argument 296 | Cycle ^I Tab file name completion cycle 297 | CycleBack #^I #Tab file name completion cycle backwards 298 | CycleDir ^\ directory completion cycle 299 | CycleDirBack #^\ directory completion cycle backwards 300 | Default just add the character to the line 301 | DelArg #^Bksp delete argument at or left of cursor 302 | DelBegLine ^X ^Home delete to the beginning of the line 303 | DelEndExec ^O delete to the end of the line and execute 304 | DelEndLine ^K ^End delete to the end of the line 305 | DelLeft ^H Bksp delete character to the left of the cursor 306 | DelRight ^D Del delete character under the cursor 307 | DelWordLeft ^L ^Bksp delete word at left of cursor 308 | DelWordRight ^W ^Del delete word at right of cursor 309 | EndLine ^E End move cursor to the end of the line 310 | EndWordLeft #Left move cursor to the end of the previous word 311 | EndWordRight #Right move cursor to the end of the current/next word 312 | Enter Enter accept line 313 | Erase ^[ Esc erase line (and reset history pointer) 314 | Execute ^Enter accept line without adding it to the history 315 | FindBack #^R ^Up incrementally search the history backwards 316 | FindForw #^V ^Down incrementally search the history forwards 317 | FirstLine PgUp recall first command in history 318 | Hidden #^Enter accept line, remove it & prompt from the screen 319 | HiddenEx #^^ as above, without adding it to the history 320 | Ignore totally ignore the key 321 | InsOvr Ins insert/overwrite toggle 322 | LastLine PgDn recall last command in history 323 | List #^F ^Tab file name completion list 324 | ListDir #^D #^Tab directory completion list 325 | MacroToggle ^_ macro/symbol/brace toggling 326 | NextLine ^N Down recall next command in history buffer 327 | Paste #^P #Ins paste cut text 328 | PrevLine ^P ^U Up recall previous command in history buffer 329 | Quote ^Q next key will not be interpreted as a function 330 | Record F12 record a series of keys 331 | Redo #^Z undo the undo 332 | Revert #^U undo/redo everything 333 | SearchBack ^R F8 search the history backwards 334 | SearchForw ^V #F8 search the history forwards 335 | Select ^M select text 336 | SelectFiles #^S select files from the standard Open dialog 337 | StoreErase ^G erase the line but put it in the history 338 | StringLeft @Left move cursor to start of current/previous string 339 | StringRight @Right move cursor to start of next string 340 | SwapArgs #^A swap current/previous argument with that before 341 | SwapWords #^T swap current/previous word with that before 342 | Transpose ^T swap character at the cursor with that before 343 | UnderToggle #^_ change behaviour of underscore 344 | Undo ^Z undo changes to the line 345 | UpdateEnter #Enter accept line, updating the history 346 | UpdateErase #^G erase the line but update the history 347 | VarSubst ^J inline substitution/brace expansion/association 348 | Wipe ^^ accept line but remove it from the screen 349 | WordLeft ^Left move cursor to start of current/previous word 350 | WordRight ^Right move cursor to start of next word 351 | 352 | A word is a sequence of alphanumeric characters, optionally including the 353 | underscore. A string is a sequence of characters between spaces or tabs. 354 | An argument is a string which may also contain characters between double 355 | quotes. The brief descriptions above should suffice for most functions, so 356 | I will only detail a select few. 357 | 358 | CmdSep - command separator 359 | 360 | CMDread is capable of specifying several commands on the one line. This 361 | key will add the character to separate those commands. Note that this 362 | character is a literal character 19, so commands can also be separated by 363 | quoting ^S or using the keypad. Note that spaces are significant between 364 | the separator: "a  b" represents the two commands "a " and " b". This is 365 | particularly important when the ignore character is a space. The command 366 | separator cannot be used with symbol or macro definitions. 367 | 368 | CopyFromPrev - copy from previous command 369 | 370 | Replace the line from the cursor with the previous command (that was 371 | entered, not from the history), also from the cursor. 372 | 373 | FindBack, FindForw - incremental search 374 | 375 | Searches the history for any line that contains the text you type in (or 376 | have already entered). Repeating the search will look for another line, 377 | not for another instance of the text on the same line. For example, if you 378 | have a line "CMDread.cmd" and search backwards for "cmd", it will place you 379 | at the "r" after the first "CMD"; searching again will not place you at the 380 | end of the second "cmd", but look for another line. 381 | 382 | InsOvr - insert/overwrite toggle 383 | 384 | Toggle between insert and overwrite modes for this line; once entered the 385 | default mode will be restored for the next line. 386 | 387 | MacroToggle - macro/symbol/brace toggling 388 | 389 | Disable (or enable, if "-t" has been used) the usual translations CMDread 390 | makes to the line, explained below. 391 | 392 | Quote - the next key will not be interpreted as a function 393 | 394 | Disable CMDread's function interpretation of the key and treat it as a 395 | literal character. 396 | 397 | Record - record a series of keys 398 | 399 | Prompt for a key and remember all following keystrokes, up until Record is 400 | pressed again or the line is entered. Pressing the assigned key will then 401 | play back all those keystrokes again. The assigned key cannot be any key 402 | assigned to Enter, Erase or Record. 403 | 404 | Revert - undo/redo everything 405 | 406 | If the previous function was Undo or Revert, Revert will Redo everything, 407 | otherwise it will Undo everything. 408 | 409 | Select - select text 410 | 411 | Using this will mark the current (or last, if at the end) character; use 412 | the normal movement functions to extend the selection. Cut will remember 413 | the selection, then delete it; DelLeft & DelRight will just delete it; 414 | Paste will replace it (provided something has been Cut); anything else will 415 | just remove the mark. 416 | 417 | Undo - undo changes to the line 418 | 419 | Any function that modifies the line can be undone (and redone, if no fur- 420 | ther changes are made). Repeated uses of a function are all undone at 421 | once, with two exceptions: characters will undo to the start of a word and 422 | file name completion to the prefix. Recalling a line from the history will 423 | reset the undo. 424 | 425 | UpdateEnter, UpdateErase - update the history 426 | 427 | If a previous line was edited, rather than create a new history entry, 428 | replace the existing one. 429 | 430 | VarSubst - inline variable substitution/brace expansion/association 431 | 432 | Expand braces and variables, perform association, expand a macro (using the 433 | command separator to combine multiple lines) and expand a symbol, in that 434 | order. Editing then continues on the expanded line. Variables are either 435 | environment variables or symbols, enclosed within percent signs. 436 | 437 | File Name Completion 438 | -------------------- 439 | 440 | CMDread examines the argument to the left of the cursor and tries to com- 441 | plete it as a file name. The argument may contain a full disk and path 442 | specification. If several files match, only the common part of their names 443 | will be placed on the command line, unless the argument already contained a 444 | wildcard. Subsequent action depends on the method of completion chosen: 445 | list or cycle. 446 | 447 | In list mode, a list of all possible matching names is displayed. If there 448 | are more names than would fit on the window CMDread asks if they should be 449 | displayed; if there are more names than would fit in the console buffer 450 | CMDread does not display them at all. 451 | 452 | In cycle mode, all matches will be displayed in turn. Once all matches 453 | have been displayed the original common part will be displayed, then the 454 | cycle will continue again. 455 | 456 | If the name to be completed is the first argument on the line then only 457 | executables and directories will be selected. Executables are defined by 458 | CMDread's and Window's associations and one of the "FEXEC" or "PATHEXT" 459 | environment variables. If neither of those variables is defined the def- 460 | ault list is 461 | 462 | set FEXEC=.exe.com.bat.cmd 463 | 464 | If it's not the first argument then certain extensions will be ignored. 465 | These can be selected with the "FIGNORE" environment variable. The default 466 | list is: 467 | 468 | set FIGNORE=.exe.com.dll.obj.o.bak 469 | 470 | If a file with an ignored extension is the only such file then it will be 471 | selected. Files with no extension can be selected by having a dot by it- 472 | self (e.g.: "set FIGNORE=.exe.com.dll.obj.o.bak."). 473 | 474 | If the matching name is a directory a "\" or "/" is appended (depending on 475 | what you've entered yourself), making it easier to enter a complete path 476 | specification (unless this has been disabled with the "-b" command line 477 | option, in which case nothing is appended). If the matching name is not a 478 | directory a space is placed after the name. 479 | 480 | Brace Expansion 481 | --------------- 482 | 483 | Brace expansion is usually used as a shorthand method of naming files. 484 | Instead of having to type: 485 | 486 | del temp\file1.tmp temp\file2.tmp 487 | 488 | you can type: 489 | 490 | del temp\file{1,2}.tmp 491 | 492 | The list inside the braces must be comma-separated, and it will recognise 493 | spaces: 494 | 495 | del temp\file{1, 2}.tmp 496 | 497 | will expand to: 498 | 499 | del temp\file1.tmp temp\file 2.tmp 500 | 501 | which is probably not what you want. Braces work inside quotes, though: 502 | 503 | del "temp\file{1, 2}.tmp" 504 | 505 | becomes: 506 | 507 | del "temp\file1.tmp" "temp\file 2.tmp" 508 | 509 | Braces can be nested, and the expansion will also recognise commas, semi- 510 | colons and plus signs as separators and "<|>&" as terminators: 511 | 512 | tc file1;temp{2,3},exclude{4,5}>nul 513 | 514 | will become: 515 | 516 | tc file1;temp2;temp3,exclude4,exclude5>nul 517 | 518 | @ 519 | - 520 | 521 | When used at the start of a line "@" is a special command line modifier 522 | which asks CMDread to "dosify" the line. CMDread will replace "/" with 523 | "\", leading "-"s with "/" and remove trailing backslashes from the line. 524 | For example, if you type: 525 | 526 | @rd -q /old/directory/ 527 | 528 | CMD.EXE will see: 529 | 530 | rd /q \old\directory 531 | 532 | ^ 533 | - 534 | 535 | As with CMD.EXE, CMDread uses the "^" character as an escape character. 536 | Any character following it will be treated as a literal character, losing 537 | any special significance CMDread would give it. Of particular note is 538 | specifying control keys on the command line, which require two "^" char- 539 | acters to be recognised (e.g.: "lstk ^^a"). However, within strings, back- 540 | slash should be used to escape a quote (`defk ^^p "\"Program Files\""'). 541 | 542 | History 543 | ------- 544 | 545 | The history stores each line that has been entered (provided it meets the 546 | minimum length requirements of "-l"). Moving through the history is a 547 | cyclic operation, so if you come to the end it will continue from the 548 | start. Erasing a line will move the history back to the start. A line is 549 | only stored once, matching case. Searching, however, is always done ig- 550 | noring case. A search is successful when all the characters from the beg- 551 | inning of the line up to the cursor are matched. 552 | 553 | DELH - Delete history 554 | 555 | Lines containing text will be removed from the history. This includes the 556 | DELH line itself. 557 | 558 | DELH rem 559 | 560 | will remove every line containing "rem" from the history. 561 | 562 | LSTH - List history 563 | 564 | List every command in the history, the last N commands, the first N com- 565 | mands or every command containing text. The LSTH command itself will only 566 | be included when listing every command. This command treats quotes as lit- 567 | eral characters (although a redirected file name must still be quoted as 568 | usual) and trailing spaces are significant (but a space before ">" or "|" 569 | is removed). 570 | 571 | LSTH 5 list the last five lines 572 | LSTH -5 list the first five lines 573 | LSTH text list the lines containing "text" 574 | LSTH ^5 list the lines containing "5" 575 | LSTH " >quotes "quotes" will contain lines having quote-space 576 | 577 | RSTH - Reset history 578 | 579 | Remove every command in the history. 580 | 581 | File Association 582 | ---------------- 583 | 584 | DEFA - Define association 585 | 586 | Directories and certain extensions can be "directly" executed from the com- 587 | mand line. If the first argument on the line ends in either a slash or 588 | backslash (i.e. "/" or "\") it will be treated as a directory and have the 589 | definition of the backslash association inserted and the trailing character 590 | removed: 591 | 592 | defa \ @cd 593 | utils/ 594 | 595 | will become: 596 | 597 | @cd utils 598 | 599 | If the first argument contains an extension the association list will be 600 | searched for that extension and its definition inserted. The list is a 601 | group of extensions (including the dot) followed by the definition. 602 | 603 | defa .txt.doc view 604 | CMDread.txt 605 | 606 | will become: 607 | 608 | view CMDread.txt 609 | 610 | Files without an extension can be associated by a single dot: 611 | 612 | defa .c.asm. tde 613 | 614 | will associate C, assembly and extensionless files to tde. However, remem- 615 | ber to type the dot yourself, as file name completion will not add it. 616 | 617 | A secondary association can be made by appending "=" to the extension: 618 | 619 | defa .txt=.doc= tde 620 | CMDread.txt= 621 | 622 | will become: 623 | 624 | tde CMDread.txt 625 | 626 | DELA - Delete association(s) 627 | 628 | Associations can be removed by using DELA. Supply either a complete list 629 | or individual extensions: 630 | 631 | dela .txt.doc 632 | 633 | will remove the exact list of ".txt.doc", but 634 | 635 | dela .txt .doc 636 | 637 | will remove ".txt" and ".doc" from whatever list contains them. 638 | 639 | LSTA - List association(s) 640 | 641 | List all associations, or just those specified. Each specific extension 642 | will be listed individually, even if it is part of a list. 643 | 644 | lsta .txt .doc ==> defa .txt view 645 | defa .doc view 646 | 647 | RSTA - Remove associations 648 | 649 | Remove every association. 650 | 651 | Symbols 652 | ------- 653 | 654 | DEFS - Define symbol 655 | 656 | Symbols (also known as aliases) replace a word at the beginning of the line 657 | with any sequence of characters. 658 | 659 | defs dw dir /w 660 | dw/b directory 661 | 662 | will become: 663 | 664 | dir /w /b directory 665 | 666 | Redefining a symbol will replace the previous definition; defining a symbol 667 | the same name as a macro will remove the macro definition. 668 | 669 | DELS - Delete symbol(s) 670 | 671 | Remove the specified symbols. 672 | 673 | LSTS - List symbol(s) 674 | 675 | Display all symbols, or just those specified. 676 | 677 | RSTS - Reset symbols 678 | 679 | Remove every symbol. 680 | 681 | Macros 682 | ------ 683 | 684 | DEFM - Define macro 685 | 686 | Multi-line command macros are created by issuing the DEFM macro-name 687 | command. Each line is terminated by hitting Enter, and the macro is term- 688 | inated with the ENDM command. Macro command lines may contain parameters, 689 | which are designated by %N (N is a numeral from 0-9); %N* is shorthand for 690 | parameter N and all parameters after it; %* is shorthand for %1*. Macros 691 | will stop processing parameters at one of "<>|&"; if found, the rest of the 692 | line will be appended after the definition of the first line. 693 | 694 | defm v unzip -c %* | view 695 | v zipfile file_id.diz readme.txt 696 | v zipfile *.txt |tde 697 | 698 | will become: 699 | 700 | unzip -c zipfile file_id.diz readme.txt | view 701 | unzip -c zipfile *.txt | view |tde 702 | 703 | and: 704 | 705 | defm mcd 706 | md %1 707 | cd %1 708 | endm 709 | mcd newdir 710 | mcd dir1 dir2 2>nul 711 | 712 | will become: 713 | 714 | md newdir 715 | cd newdir 716 | md dir1 2>nul 717 | cd dir1 718 | 719 | The name may be left out, in which case the macro will expand on an empty 720 | line. 721 | 722 | defm 723 | time/t 724 | endm 725 | 726 | will show the time by just pressing Enter. 727 | 728 | Redefining a macro will replace the current definition; defining a macro 729 | with the same name as a symbol will delete the symbol. 730 | 731 | DELM - Delete macro(s) 732 | 733 | Remove the specified macros. 734 | 735 | LSTM - List macro(s) 736 | 737 | List all macros, or just those specified. 738 | 739 | RSTM - Reset macros 740 | 741 | Remove every macro. 742 | 743 | 744 | Known Problems 745 | ============== 746 | 747 | Redirecting internal commands will use the current code page, but if a 748 | character cannot be converted, it will not be written (rather than becoming 749 | a question mark). 750 | 751 | A hidden command that has no output will add a blank line if the previous 752 | line is not blank (assuming that to be its output). 753 | 754 | The primary history file will be shared between the 64- and 32-bit 755 | versions (i.e. the first instance of each will both be regarded as primary, 756 | so the one that exits last will overwrite the other). 757 | 758 | The configuration file shown in the status is the one used if CMDread is 759 | installed (via "-i"), which is not necessarily the file specified on the 760 | command line. 761 | 762 | 763 | History 764 | ======= 765 | 766 | Legend: + added, - bug-fixed, * changed. 767 | 768 | v2.12, 10 July, 2013: 769 | * modified option handling (only write to the registry with an explicit -i; 770 | write to a specified history file). 771 | 772 | v2.11, 4 July, 2013: 773 | - fixed storing file names; 774 | - prevent crash from an invalid file (but still garbage in, garbage out); 775 | * increase maximum line length to 2046 bytes. 776 | 777 | v2.10, 24 June, 2013: 778 | * renamed from CMDkey to CMDread (Microsoft already has a Cmdkey); 779 | - fixed file name completion testing for directory on an empty name; 780 | - fixed exe/dll version conflicts (improved future-proofing); 781 | - fixed redefining a keyboard macro; 782 | - fixed listing a single-character keyboard macro; 783 | - remove extra line(s) when wiping a wrapped command; 784 | - fixed interpretation of the custom history file in the config file; 785 | - test if -I/-U fails and show message (need admin rights); 786 | - maintain position when user scrolls window; 787 | - fixed handling of environment variables; 788 | * expand %CD%; 789 | * improved (un)install; 790 | * copy history from primary instance if there's no parent; 791 | * improved file name completion list; 792 | * tweak stats display (remove initial blank line, add underscore setting); 793 | * use %USERPROFILE% as default config/history path (not same as binary); 794 | + 64-bit version; 795 | + Windows 8 support; 796 | + DBCS double-width character support; 797 | + use a prefix character to update history; 798 | + new functions Hidden & HiddenEx to wipe the prompt, as well. 799 | 800 | v2.01, 8 December, 2011: 801 | * fully work with ANSICON. 802 | 803 | v2.00, 22 July to 8 August, 16 September & 15 November, 2011: 804 | - fixed file name completion with leading/trailing dot; 805 | - ensure input is from the console (prevents "set/p 8 | #include "version.h" 9 | 10 | #ifdef _WIN64 11 | #define EDITDLL "edit_amd64.dll" 12 | #else 13 | #define EDITDLL "edit.dll" 14 | #endif 15 | 16 | VS_VERSION_INFO VERSIONINFO 17 | FILEVERSION PVERB 18 | PRODUCTVERSION PVERB 19 | FILEOS VOS_NT 20 | FILETYPE VFT_DLL 21 | { 22 | BLOCK "StringFileInfo" 23 | { 24 | BLOCK "040904B0" 25 | { 26 | VALUE "Comments", "http://cmdkey.adoxa.vze.com/" 27 | VALUE "CompanyName", "Jason Hood" 28 | VALUE "FileDescription", "Command Line Editor" 29 | VALUE "FileVersion", PVERSA 30 | VALUE "InternalName", "edit" 31 | VALUE "LegalCopyright", "Freeware" 32 | VALUE "OriginalFilename", EDITDLL 33 | VALUE "ProductName", "CMDread" 34 | VALUE "ProductVersion", PVERSA 35 | } 36 | } 37 | 38 | BLOCK "VarFileInfo" 39 | { 40 | VALUE "Translation", 0x0409, 0x04B0 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /makefile.gcc: -------------------------------------------------------------------------------- 1 | # MinGW/MinGW-w64 makefile for CMDread. 2 | # 3 | # Tested with: 4 | # * MinGW/gcc 4.7.2; 5 | # * tdm-gcc-4.7.1-2; 6 | # * tdm64-gcc-4.7.1-3; 7 | # * MinGW-builds x64-4.8.1-release-posix-seh-rev1. 8 | 9 | CC = gcc 10 | CFLAGS = -Wall -O2 11 | 12 | #ARCH = 32 13 | #ARCH = 64 14 | #ARCH = multi 15 | 16 | ifndef ARCH 17 | # Use the machine to distinguish between MinGW and MinGW-w64. 18 | ifeq (,$(findstring 64,$(shell gcc -dumpmachine))) 19 | ARCH = 32 20 | else 21 | # It's 64-bit, if it's multi the lib name will be different. 22 | ifeq ($(shell gcc -m32 -print-libgcc-file-name),$(shell gcc -m64 -print-libgcc-file-name)) 23 | ARCH = 64 24 | else 25 | ARCH = multi 26 | endif 27 | # MinGW-w64 wants this to enable wmain. 28 | CFLAGS += -municode 29 | LDFLAGS = -municode 30 | endif 31 | endif 32 | 33 | ifeq ($(ARCH),multi) 34 | all: CMDread32 CMDread64 35 | else 36 | all: CMDread$(ARCH) 37 | endif 38 | 39 | CMDread32: edit.dll CMDread_x86.exe 40 | 41 | CMDread64: edit_amd64.dll CMDread_amd64.exe 42 | 43 | 44 | %.o %_x86.o: %.c 45 | $(CC) -m32 -c $(CFLAGS) $< -o $@ 46 | 47 | %v.o %v_x86.o: %.rc 48 | windres -U _WIN64 -F pe-i386 $< $@ 49 | 50 | %_amd64.o: %.c 51 | $(CC) -m64 -c $(CFLAGS) $< -o $@ 52 | 53 | %v_amd64.o: %.rc 54 | windres -F pe-x86-64 $< $@ 55 | 56 | 57 | edit.dll: edit.o editv.o 58 | $(CC) -m32 $+ -mdll -s -o $@ -lcomdlg32 -Wl,-shared,--out-implib,$(basename $@).a,--image-base,0xCE00000 59 | 60 | CMDread_x86.exe: CMDread_x86.o CMDreadv_x86.o edit.a 61 | $(CC) -m32 $(LDFLAGS) $+ -s -o $@ 62 | 63 | edit_amd64.dll: edit_amd64.o editv_amd64.o 64 | $(CC) -m64 $+ -mdll -s -o $@ -lcomdlg32 -Wl,-shared,--out-implib,$(basename $@).a,--image-base,0xCE000000 65 | 66 | CMDread_amd64.exe: CMDread_amd64.o CMDreadv_amd64.o edit_amd64.a 67 | $(CC) $(LDFLAGS) -m64 $+ -s -o $@ 68 | 69 | 70 | edit.o edit_amd64.o: edit.c CMDread.h version.h 71 | CMDread_x86.o CMDread_amd64.o: CMDread.c CMDread.h version.h 72 | 73 | editv.o editv_amd64.o: edit.rc version.h 74 | CMDreadv_x86.o CMDreadv_amd64.o: CMDread.rc version.h 75 | 76 | 77 | clean: 78 | -cmd /c "del *.o *.a 2>nul" 79 | -------------------------------------------------------------------------------- /makefile.vc: -------------------------------------------------------------------------------- 1 | # VC makefile for CMDread. 2 | # Jason Hood, 6 June, 2013. 3 | # 4 | # Tested with: 5 | # * Visual Studio 6.0 (VC6); 6 | # * Visual C++ 2003 Toolkit (VC7); 7 | # * Platform SDK for Windows Server 2003 R2 (VC8 64-bit); 8 | # * Visual Studio 2008 Express SP1 (VC9); 9 | # * Visual Studio 2010 Professional (VC10). 10 | 11 | !IF "$(CPU)" == "AMD64" || "$(PLATFORM)" == "x64" 12 | ARCH = amd64 13 | EARCH = _$(ARCH) 14 | BASE = 0xCE000000 15 | RFLAGS = /D_WIN64 16 | !ELSE 17 | ARCH = x86 18 | EARCH = 19 | BASE = 0xCE00000 20 | !ENDIF 21 | 22 | # This is required for the 2003 Platform SDK, but not for Visual Studio 2010. 23 | !IF "$(_NMAKE_VER)" == "7.00.8882" 24 | !IF "$(ARCH)" == "amd64" 25 | EXTRA_LIBS = bufferoverflowu.lib 26 | # The 2003 Toolkit doesn't have MSVCRT.LIB, but VC98 does. 27 | !ELSEIF !DEFINED(SHARE) && !DEFINED(MSVCDIR) 28 | SHARE = 29 | !ENDIF 30 | !ENDIF 31 | 32 | # Link with MSVCRT.LIB by default. 33 | !IF !DEFINED(SHARE) 34 | SHARE = /MD 35 | !ENDIF 36 | 37 | # Manifest tool to embed the manifest required by 2008. 38 | MT = mt.exe 39 | 40 | CFLAGS = /nologo /W3 /O2 $(SHARE) /D_CRT_SECURE_NO_WARNINGS 41 | LIBS = advapi32.lib comdlg32.lib shell32.lib user32.lib $(EXTRA_LIBS) 42 | 43 | all: edit$(EARCH).dll CMDread_$(ARCH).exe 44 | 45 | edit$(EARCH).dll: edit$(EARCH).obj edit$(EARCH).res 46 | $(CC) /nologo $(SHARE) /LD /Fe$@ $** $(LIBS) /link /base:$(BASE) /section:.share,s /filealign:512 47 | !IF "$(_NMAKE_VER)" == "9.00.30729.01" 48 | $(MT) /nologo -manifest $@.manifest -outputresource:$@;2 49 | del $@.manifest 50 | !ENDIF 51 | 52 | CMDread_$(ARCH).exe: CMDread_$(ARCH).obj CMDread_$(ARCH).res edit$(EARCH).lib 53 | $(CC) /nologo $(SHARE) /Fe$@ $** $(LIBS) /link /filealign:512 54 | !IF "$(_NMAKE_VER)" == "9.00.30729.01" 55 | $(MT) /nologo -manifest $@.manifest -outputresource:$@;1 56 | del $@.manifest 57 | !ENDIF 58 | 59 | edit$(EARCH).obj: edit.c CMDread.h version.h 60 | $(CC) $(CFLAGS) /c /Fo$@ edit.c 61 | 62 | CMDread_$(ARCH).obj: CMDread.c CMDread.h version.h 63 | $(CC) $(CFLAGS) /c /Fo$@ CMDread.c 64 | 65 | edit$(EARCH).res: edit.rc version.h 66 | $(RC) $(RFLAGS) /fo$@ edit.rc 67 | 68 | CMDread_$(ARCH).res: CMDread.rc version.h 69 | $(RC) $(RFLAGS) /fo$@ CMDread.rc 70 | 71 | clean: 72 | -del *.obj *.res *.lib *.exp 73 | -------------------------------------------------------------------------------- /version.h: -------------------------------------------------------------------------------- 1 | /* 2 | version.h - Version macros for CMDread. 3 | 4 | Jason Hood, 30 July, 2011. 5 | */ 6 | 7 | #define PVERS L"2.12" // string 8 | #define PVERSA "2.12" // ANSI string (windres 2.16.91 didn't like L) 9 | #define PVERX 0x212 // hex 10 | #define PVERB 2,1,2,0 // binary (resource) 11 | --------------------------------------------------------------------------------