1114 lines
32 KiB
C
Executable File
1114 lines
32 KiB
C
Executable File
/*
|
|
* UltraDefrag - a powerful defragmentation tool for Windows NT.
|
|
* Copyright (c) 2007-2011 by Dmitri Arkhangelski (dmitriar@gmail.com).
|
|
* Copyright (c) 2010-2011 by Stefan Pendl (stefanpe@users.sourceforge.net).
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
/*
|
|
* UltraDefrag boot time (native) interface - native commands implementation.
|
|
*/
|
|
|
|
#include "defrag_native.h"
|
|
|
|
/**
|
|
* @brief Defines whether @echo is on or not.
|
|
*/
|
|
int echo_flag = DEFAULT_ECHO_FLAG;
|
|
|
|
/*
|
|
* boot-off command modifies registry,
|
|
* but shutdown\reboot commands releases its effect,
|
|
* because all registry modifications becomes lost.
|
|
* Therefore we track pending boot-off request through
|
|
* a special pending-boot-off file in windows directory.
|
|
*/
|
|
int pending_boot_off = 0;
|
|
|
|
/**
|
|
* @brief Defines whether command is executed
|
|
* in scripting mode or in interactive mode.
|
|
*/
|
|
int scripting_mode = 1;
|
|
|
|
/* help screen */
|
|
char *help_message[] = {
|
|
"Interactive mode commands:",
|
|
"",
|
|
" boot-off - disable boot time defragger",
|
|
" boot-on - enable boot time defragger",
|
|
" call - execute the boot script or the specified",
|
|
" command script",
|
|
" echo - enable/disable command line display,",
|
|
" display a message or the display status",
|
|
" exit - continue Windows boot",
|
|
" help - display this help screen",
|
|
" hexview - display the contents of the specified",
|
|
" file in a HEX viewer layout",
|
|
" history - display the list of typed commands",
|
|
" man - list or display available manual pages",
|
|
" pause - halt the execution for the specified timeout",
|
|
" or till a key is pressed",
|
|
" reboot - reboot the PC",
|
|
" set - list, set or clear environment variables",
|
|
" shutdown - halt the PC",
|
|
" type - display the contents of the boot script or",
|
|
" the specified file",
|
|
" udefrag - list, analyze, defrag or optimize disks",
|
|
"",
|
|
" For detailed information execute 'man {command}'.",
|
|
"",
|
|
NULL
|
|
};
|
|
|
|
typedef int (*cmd_handler_proc)(int argc,short **argv,short **envp);
|
|
typedef struct {
|
|
short *cmd_name;
|
|
cmd_handler_proc cmd_handler;
|
|
} cmd_table_entry;
|
|
|
|
/* forward declarations */
|
|
extern cmd_table_entry cmd_table[];
|
|
static int boot_off_handler(int argc,short **argv,short **envp);
|
|
static int type_handler(int argc,short **argv,short **envp);
|
|
|
|
static int man_listing_terminator(void *user_defined_parameter)
|
|
{
|
|
KBD_RECORD kbd_rec;
|
|
|
|
/* give user a chance to break a likely hanged man listing */
|
|
if(winx_kb_read(&kbd_rec,0) == 0){
|
|
/* check for escape key hits */
|
|
if(kbd_rec.wVirtualScanCode == 0x1)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int list_installed_man_pages(int argc,short **argv,short **envp)
|
|
{
|
|
char windir[MAX_PATH + 1];
|
|
char path[MAX_PATH + 1];
|
|
short wpath[MAX_PATH + 1];
|
|
winx_file_info *file, *filelist;
|
|
WINX_FILE *f;
|
|
int i, column;
|
|
int max_columns = (MAX_LINE_WIDTH - 1) / 15;
|
|
|
|
if(argc < 1)
|
|
return (-1);
|
|
|
|
/* get %winir% path */
|
|
if(winx_get_windows_directory(windir,MAX_PATH) < 0){
|
|
winx_printf("\n%ws: cannot get %%windir%% path\n\n",argv[0]);
|
|
return (-1);
|
|
}
|
|
|
|
/* try to get list of installed man pages through winx_ftw call */
|
|
_snwprintf(wpath,MAX_PATH,L"%hs\\UltraDefrag\\man",windir);
|
|
wpath[MAX_PATH] = 0;
|
|
filelist = winx_ftw(wpath,0,NULL,NULL,man_listing_terminator,NULL);
|
|
if(filelist){
|
|
winx_printf("Available Manual Pages:\n");
|
|
for(file = filelist->prev, column = 0; file; file = file->prev){
|
|
/* display man files only */
|
|
_wcslwr(file->name);
|
|
if(wcsstr(file->name,L".man")){
|
|
winx_printf("%-15ws",file->name);
|
|
column ++;
|
|
if(column >= max_columns){
|
|
winx_printf("\n");
|
|
column = 0;
|
|
}
|
|
}
|
|
if(file->prev == filelist->prev) break;
|
|
}
|
|
winx_printf("\n");
|
|
winx_ftw_release(filelist);
|
|
} else {
|
|
winx_printf("Available Command Manuals:\n");
|
|
/* cycle through names of existing commands */
|
|
for(i = 0, column = 0; cmd_table[i].cmd_handler != NULL; i++){
|
|
/* build path to the manual page */
|
|
_snprintf(path,MAX_PATH,"%s\\UltraDefrag\\man\\%ws.man",windir,cmd_table[i].cmd_name);
|
|
path[MAX_PATH] = 0;
|
|
/* check for the page existence */
|
|
f = winx_fopen(path,"r");
|
|
if(f != NULL){
|
|
winx_fclose(f);
|
|
/* display man page filename on the screen */
|
|
_snprintf(path,MAX_PATH,"%ws.man",cmd_table[i].cmd_name);
|
|
path[MAX_PATH] = 0;
|
|
winx_printf("%-15s",path);
|
|
column ++;
|
|
if(column >= max_columns){
|
|
winx_printf("\n");
|
|
column = 0;
|
|
}
|
|
}
|
|
}
|
|
winx_printf("%-15s\n","variables.man");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief man command handler.
|
|
*/
|
|
static int man_handler(int argc,short **argv,short **envp)
|
|
{
|
|
short *type_argv[2];
|
|
char path[MAX_PATH + 1];
|
|
short wpath[MAX_PATH + 1];
|
|
size_t native_prefix_length;
|
|
|
|
if(argc < 1)
|
|
return (-1);
|
|
|
|
if(argc < 2){
|
|
/* installed man pages listing is requested */
|
|
return list_installed_man_pages(argc,argv,envp);
|
|
}
|
|
|
|
/* build path to requested manual page */
|
|
if(winx_get_windows_directory(path,MAX_PATH) < 0){
|
|
winx_printf("\n%ws: cannot get %%windir%% path\n\n",argv[0]);
|
|
return (-1);
|
|
}
|
|
_snwprintf(wpath,MAX_PATH,L"%hs\\UltraDefrag\\man\\%ws.man",path,argv[1]);
|
|
wpath[MAX_PATH] = 0;
|
|
|
|
/* build argv for type command handler */
|
|
type_argv[0] = L"man";
|
|
/* skip native prefix in path */
|
|
native_prefix_length = wcslen(L"\\??\\");
|
|
if(wcslen(wpath) >= native_prefix_length)
|
|
type_argv[1] = wpath + native_prefix_length;
|
|
else
|
|
type_argv[1] = wpath;
|
|
return type_handler(2,type_argv,envp);
|
|
}
|
|
|
|
/**
|
|
* @brief help command handler.
|
|
*/
|
|
static int help_handler(int argc,short **argv,short **envp)
|
|
{
|
|
if(argc < 1)
|
|
return (-1);
|
|
|
|
/* check for individual command's help */
|
|
if(argc > 1)
|
|
return man_handler(argc,argv,envp);
|
|
|
|
return winx_print_array_of_strings(help_message,
|
|
MAX_LINE_WIDTH,MAX_DISPLAY_ROWS,
|
|
DEFAULT_PAGING_PROMPT_TO_HIT_ANY_KEY,
|
|
scripting_mode ? 0 : 1);
|
|
}
|
|
|
|
/**
|
|
* @brief history command handler.
|
|
*/
|
|
static int history_handler(int argc,short **argv,short **envp)
|
|
{
|
|
winx_history_entry *entry;
|
|
char **strings;
|
|
int i, result;
|
|
|
|
if(argc < 1)
|
|
return (-1);
|
|
|
|
if(scripting_mode || history.head == NULL)
|
|
return 0;
|
|
|
|
/* convert list of strings to array */
|
|
strings = winx_heap_alloc((history.n_entries + 3) * sizeof(char *));
|
|
if(strings == NULL){
|
|
winx_printf("\n%ws: cannot allocate %u bytes of memory\n\n",
|
|
argv[0],(history.n_entries + 3) * sizeof(char *));
|
|
return (-1);
|
|
}
|
|
strings[0] = "Typed commands history:";
|
|
strings[1] = "";
|
|
i = 2;
|
|
for(entry = history.head; i < history.n_entries; entry = entry->next){
|
|
if(entry->string){
|
|
strings[i] = entry->string;
|
|
i++;
|
|
}
|
|
if(entry->next == history.head) break;
|
|
}
|
|
strings[i] = NULL;
|
|
|
|
winx_printf("\n");
|
|
|
|
result = winx_print_array_of_strings(strings,
|
|
MAX_LINE_WIDTH,MAX_DISPLAY_ROWS,
|
|
DEFAULT_PAGING_PROMPT_TO_HIT_ANY_KEY,1);
|
|
|
|
winx_heap_free(strings);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @brief echo command handler.
|
|
*/
|
|
static int echo_handler(int argc,short **argv,short **envp)
|
|
{
|
|
int i;
|
|
|
|
if(argc < 1)
|
|
return (-1);
|
|
|
|
/* check whether an empty line is requested */
|
|
if(!wcscmp(argv[0],L"echo.")){
|
|
winx_printf("\n");
|
|
return 0;
|
|
}
|
|
|
|
/* check whether echo status is requested */
|
|
if(argc < 2){
|
|
if(echo_flag)
|
|
winx_printf("echo is on\n");
|
|
else
|
|
winx_printf("echo is off\n");
|
|
return 0;
|
|
}
|
|
|
|
/* handle on and off keys */
|
|
if(argc == 2){
|
|
if(!wcscmp(argv[1],L"on")){
|
|
echo_flag = 1;
|
|
return 0;
|
|
} else if(!wcscmp(argv[1],L"off")){
|
|
echo_flag = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* handle echo command */
|
|
for(i = 1; i < argc; i++){
|
|
winx_printf("%ws",argv[i]);
|
|
if(i != argc - 1)
|
|
winx_printf(" ");
|
|
}
|
|
winx_printf("\n");
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief type command handler.
|
|
*/
|
|
static int type_handler(int argc,short **argv,short **envp)
|
|
{
|
|
char path[MAX_PATH];
|
|
short *filename;
|
|
int i, length;
|
|
size_t filesize;
|
|
unsigned char *buffer, *second_buffer;
|
|
int unicode_detected;
|
|
char *strings[] = { NULL, NULL };
|
|
int result;
|
|
|
|
if(argc < 1)
|
|
return (-1);
|
|
|
|
/* display boot time script if filename is missing */
|
|
if(argc < 2){
|
|
if(winx_get_windows_directory(path,MAX_PATH) < 0){
|
|
winx_printf("\n%ws: cannot get %%windir%% path\n\n",argv[0]);
|
|
return (-1);
|
|
}
|
|
(void)strncat(path,"\\system32\\ud-boot-time.cmd",
|
|
MAX_PATH - strlen(path) - 1);
|
|
} else {
|
|
length = 0;
|
|
for(i = 1; i < argc; i++)
|
|
length += wcslen(argv[i]) + 1;
|
|
filename = winx_heap_alloc(length * sizeof(short));
|
|
if(filename == NULL){
|
|
winx_printf("\n%ws: cannot allocate %u bytes of memory\n\n",
|
|
argv[0],length * sizeof(short));
|
|
return (-1);
|
|
}
|
|
filename[0] = 0;
|
|
for(i = 1; i < argc; i++){
|
|
wcscat(filename,argv[i]);
|
|
if(i != argc - 1)
|
|
wcscat(filename,L" ");
|
|
}
|
|
(void)_snprintf(path,MAX_PATH - 1,"\\??\\%ws",filename);
|
|
path[MAX_PATH - 1] = 0;
|
|
winx_heap_free(filename);
|
|
}
|
|
(void)filename;
|
|
|
|
/* read file contents entirely */
|
|
buffer = winx_get_file_contents(path,&filesize);
|
|
if(buffer == NULL)
|
|
return 0; /* file is empty or some error */
|
|
|
|
/* terminate buffer by two zeros */
|
|
buffer[filesize] = buffer[filesize + 1] = 0;
|
|
|
|
/* check for UTF-16 signature which exists in files edited in Notepad */
|
|
unicode_detected = 0;
|
|
if(filesize >= sizeof(short)){
|
|
if(buffer[0] == 0xFF && buffer[1] == 0xFE)
|
|
unicode_detected = 1;
|
|
}
|
|
|
|
/* print file contents */
|
|
if(unicode_detected){
|
|
second_buffer = winx_heap_alloc(filesize + 1);
|
|
if(second_buffer == NULL){
|
|
winx_printf("\n%ws: cannot allocate %u bytes of memory\n\n",
|
|
argv[0],filesize + 1);
|
|
winx_release_file_contents(buffer);
|
|
return (-1);
|
|
}
|
|
(void)_snprintf(second_buffer,filesize + 1,"%ws",(short *)(buffer + 2));
|
|
second_buffer[filesize] = 0;
|
|
strings[0] = second_buffer;
|
|
result = winx_print_array_of_strings(strings,MAX_LINE_WIDTH,
|
|
MAX_DISPLAY_ROWS,DEFAULT_PAGING_PROMPT_TO_HIT_ANY_KEY,
|
|
scripting_mode ? 0 : 1);
|
|
winx_heap_free(second_buffer);
|
|
} else {
|
|
strings[0] = buffer;
|
|
result = winx_print_array_of_strings(strings,MAX_LINE_WIDTH,
|
|
MAX_DISPLAY_ROWS,DEFAULT_PAGING_PROMPT_TO_HIT_ANY_KEY,
|
|
scripting_mode ? 0 : 1);
|
|
}
|
|
|
|
/* cleanup */
|
|
winx_release_file_contents(buffer);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @brief hexview command handler.
|
|
*/
|
|
static int hexview_handler(int argc,short **argv,short **envp)
|
|
{
|
|
char path[MAX_PATH];
|
|
short *filename;
|
|
int i, length;
|
|
WINX_FILE *f;
|
|
ULONGLONG size;
|
|
size_t filesize;
|
|
size_t bytes_to_read, bytes_to_print, n, j, k, m;
|
|
#define SCREEN_BUFFER_SIZE (8 * (MAX_DISPLAY_ROWS - 5))
|
|
unsigned char buffer[SCREEN_BUFFER_SIZE];
|
|
int result;
|
|
char *offset;
|
|
KBD_RECORD kbd_rec;
|
|
int escape_detected = 0;
|
|
int break_detected = 0;
|
|
char esq[] = {'\a', '\b', '\f', '\n', '\r', '\t', '\v', 0};
|
|
int esq_found = 0;
|
|
|
|
if(argc < 1)
|
|
return (-1);
|
|
|
|
if(argc < 2){
|
|
winx_printf("\n%ws: file name must be specified\n\n",argv[0]);
|
|
return (-1);
|
|
}
|
|
|
|
/* build the native path to the file */
|
|
length = 0;
|
|
for(i = 1; i < argc; i++)
|
|
length += wcslen(argv[i]) + 1;
|
|
filename = winx_heap_alloc(length * sizeof(short));
|
|
if(filename == NULL){
|
|
winx_printf("\n%ws: cannot allocate %u bytes of memory\n\n",
|
|
argv[0],length * sizeof(short));
|
|
return (-1);
|
|
}
|
|
filename[0] = 0;
|
|
for(i = 1; i < argc; i++){
|
|
wcscat(filename,argv[i]);
|
|
if(i != argc - 1)
|
|
wcscat(filename,L" ");
|
|
}
|
|
(void)_snprintf(path,MAX_PATH - 1,"\\??\\%ws",filename);
|
|
path[MAX_PATH - 1] = 0;
|
|
winx_heap_free(filename);
|
|
|
|
/* open the file */
|
|
f = winx_fopen(path,"r");
|
|
if(f == NULL){
|
|
winx_printf("\n%ws: cannot open %s\n\n",argv[0],path);
|
|
return (-1);
|
|
}
|
|
size = winx_fsize(f);
|
|
if(size == 0){
|
|
winx_fclose(f);
|
|
return 0; /* nothing to display */
|
|
}
|
|
#ifndef _WIN64
|
|
if(size > 0xFFFFFFFF){
|
|
winx_printf("\n%ws: files larger than ~4Gb aren\'t supported\n\n",argv[0]);
|
|
winx_fclose(f);
|
|
return (-1);
|
|
}
|
|
#endif
|
|
filesize = (size_t)size;
|
|
|
|
/* read the file by portions needed to fill a single screen */
|
|
offset = 0x0;
|
|
while(filesize){
|
|
bytes_to_read = min(SCREEN_BUFFER_SIZE,filesize);
|
|
result = winx_fread(buffer,sizeof(char),bytes_to_read,f);
|
|
if(result != bytes_to_read && winx_fsize(f) == size){
|
|
winx_printf("\n%ws: cannot read %s\n\n",argv[0],path);
|
|
winx_fclose(f);
|
|
return (-1);
|
|
}
|
|
/* fill a screen */
|
|
bytes_to_print = bytes_to_read;
|
|
j = 0;
|
|
while(bytes_to_print){
|
|
n = min(8, bytes_to_print);
|
|
winx_printf("%p: ",offset);
|
|
for(k = 0; k < n; k++)
|
|
winx_printf("%02x ",(UINT)buffer[j+k]);
|
|
for(; k < 8; k++)
|
|
winx_printf(" ");
|
|
winx_printf("| ");
|
|
for(k = 0; k < n; k++){
|
|
/* replace escape sequences and 0x0 codes by spaces */
|
|
esq_found = 0;
|
|
for(m = 0; esq[m]; m++){
|
|
if(esq[m] == buffer[j+k]){
|
|
esq_found = 1;
|
|
break;
|
|
}
|
|
}
|
|
if(esq_found || buffer[j+k] == 0x0)
|
|
winx_printf(" ");
|
|
else
|
|
winx_printf("%c",buffer[j+k]);
|
|
}
|
|
for(; k < 8; k++)
|
|
winx_printf(" ");
|
|
winx_printf("\n");
|
|
offset += n;
|
|
j += n;
|
|
bytes_to_print -= n;
|
|
}
|
|
/* go to the next portion of data */
|
|
filesize -= bytes_to_read;
|
|
if(filesize && !scripting_mode){
|
|
/* display prompt to hit any key in interactive mode */
|
|
winx_printf("\n%s\n\n",DEFAULT_PAGING_PROMPT_TO_HIT_ANY_KEY);
|
|
/* wait for any key */
|
|
if(winx_kb_read(&kbd_rec,INFINITE) < 0){
|
|
break; /* break in case of errors */
|
|
}
|
|
/* check for escape */
|
|
if(kbd_rec.wVirtualScanCode == 0x1){
|
|
escape_detected = 1;
|
|
} else if(kbd_rec.wVirtualScanCode == 0x1d){
|
|
/* distinguish between control keys and break key */
|
|
if(!(kbd_rec.dwControlKeyState & LEFT_CTRL_PRESSED) && \
|
|
!(kbd_rec.dwControlKeyState & RIGHT_CTRL_PRESSED)){
|
|
break_detected = 1;
|
|
}
|
|
}
|
|
if(escape_detected || break_detected)
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* cleanup */
|
|
winx_fclose(f);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Displays list of all environment variables.
|
|
*/
|
|
static int list_environment_variables(int argc,short **argv,short **envp)
|
|
{
|
|
char **strings;
|
|
int i, j, n, length;
|
|
int result;
|
|
int filter_strings = 0;
|
|
|
|
if(argc < 1 || envp == NULL)
|
|
return (-1);
|
|
|
|
if(envp[0] == NULL)
|
|
return 0; /* nothing to print */
|
|
|
|
if(argc > 1)
|
|
filter_strings = 1;
|
|
|
|
/* convert envp to array of ANSI strings */
|
|
for(n = 0; envp[n] != NULL; n++) {}
|
|
strings = winx_heap_alloc((n + 1) * sizeof(char *));
|
|
if(strings == NULL){
|
|
winx_printf("\n%ws: cannot allocate %u bytes of memory\n\n",
|
|
argv[0],(n + 1) * sizeof(char *));
|
|
return (-1);
|
|
}
|
|
RtlZeroMemory((void *)strings,(n + 1) * sizeof(char *));
|
|
for(i = 0, j = 0; i < n; i++){
|
|
if(filter_strings && winx_wcsistr(envp[i],argv[1]) != (wchar_t *)envp[i])
|
|
continue;
|
|
length = wcslen(envp[i]);
|
|
strings[j] = winx_heap_alloc((length + 1) * sizeof(char));
|
|
if(strings[j] == NULL){
|
|
winx_printf("\n%ws: cannot allocate %u bytes of memory\n\n",
|
|
argv[0],(length + 1) * sizeof(char));
|
|
goto fail;
|
|
}
|
|
(void)_snprintf(strings[j],length + 1,"%ws",envp[i]);
|
|
strings[j][length] = 0;
|
|
j++;
|
|
}
|
|
/* print strings */
|
|
result = winx_print_array_of_strings(strings,MAX_LINE_WIDTH,
|
|
MAX_DISPLAY_ROWS,DEFAULT_PAGING_PROMPT_TO_HIT_ANY_KEY,
|
|
scripting_mode ? 0 : 1);
|
|
/* cleanup */
|
|
for(i = 0; i < n; i++){
|
|
if(strings[i])
|
|
winx_heap_free(strings[i]);
|
|
}
|
|
winx_heap_free(strings);
|
|
return result;
|
|
|
|
fail:
|
|
for(i = 0; i < n; i++){
|
|
if(strings[i])
|
|
winx_heap_free(strings[i]);
|
|
}
|
|
winx_heap_free(strings);
|
|
return (-1);
|
|
}
|
|
|
|
/**
|
|
* @brief set command handler.
|
|
*/
|
|
static int set_handler(int argc,short **argv,short **envp)
|
|
{
|
|
int name_length = 0, value_length = 0;
|
|
short *name = NULL, *value = NULL;
|
|
int i, j, n, result;
|
|
|
|
if(argc < 1)
|
|
return (-1);
|
|
|
|
/*
|
|
* Check whether environment variables
|
|
* listing is requested or not.
|
|
*/
|
|
if(argc < 2){
|
|
/* list all environment variables */
|
|
return list_environment_variables(argc,argv,envp);
|
|
} else {
|
|
/* check whether the first parameter contains '=' character */
|
|
if(!wcschr(argv[1],'=')){
|
|
/*
|
|
* List variables containing argv[1] string
|
|
* in the beginning of their names.
|
|
*/
|
|
return list_environment_variables(argc,argv,envp);
|
|
}
|
|
/* calculate name and value lengths */
|
|
n = wcslen(argv[1]);
|
|
for(i = 0; i < n; i++){
|
|
if(argv[1][i] == '='){
|
|
name_length = i;
|
|
value_length = n - i - 1;
|
|
break;
|
|
}
|
|
}
|
|
/* validate '=' character position */
|
|
if(name_length == 0 || (value_length == 0 && argc >= 3)){
|
|
winx_printf("\n%ws: invalid syntax\n\n",argv[0]);
|
|
return (-1);
|
|
}
|
|
/* append all remaining parts of the value string */
|
|
for(i = 2; i < argc; i++)
|
|
value_length += 1 + wcslen(argv[i]);
|
|
/* allocate memory */
|
|
name = winx_heap_alloc((name_length + 1) * sizeof(short));
|
|
if(name == NULL){
|
|
winx_printf("\n%ws: cannot allocate %u bytes of memory\n",
|
|
argv[0],(name_length + 1) * sizeof(short));
|
|
return (-1);
|
|
}
|
|
if(value_length){
|
|
value = winx_heap_alloc((value_length + 1) * sizeof(short));
|
|
if(value == NULL){
|
|
winx_printf("\n%ws: cannot allocate %u bytes of memory\n",
|
|
argv[0],(value_length + 1) * sizeof(short));
|
|
winx_heap_free(name);
|
|
return (-1);
|
|
}
|
|
}
|
|
/* extract name and value */
|
|
n = wcslen(argv[1]);
|
|
for(i = 0; i < n; i++){
|
|
if(argv[1][i] == '=') break;
|
|
name[i] = argv[1][i];
|
|
}
|
|
name[i] = 0;
|
|
if(value_length){
|
|
for(i++, j = 0; i < n; i++){
|
|
value[j] = argv[1][i];
|
|
j++;
|
|
}
|
|
value[j] = 0;
|
|
for(i = 2; i < argc; i++){
|
|
wcscat(value,L" ");
|
|
wcscat(value,argv[i]);
|
|
}
|
|
}
|
|
if(value_length){
|
|
/* set environment variable */
|
|
result = winx_set_env_variable(name,value);
|
|
winx_heap_free(value);
|
|
} else {
|
|
/* clear environment variable */
|
|
result = winx_set_env_variable(name,NULL);
|
|
}
|
|
/* handle a special case of %UD_LOG_FILE_PATH% */
|
|
if(wcscmp(_wcsupr(name),L"UD_LOG_FILE_PATH") == 0){
|
|
if(udefrag_set_log_file_path() < 0)
|
|
winx_printf("\n%ws: udefrag_set_log_file_path failed\n");
|
|
}
|
|
winx_heap_free(name);
|
|
return result;
|
|
}
|
|
|
|
return 0; /* this point will never be reached */
|
|
}
|
|
|
|
/**
|
|
* @brief pause command handler.
|
|
*/
|
|
static int pause_handler(int argc,short **argv,short **envp)
|
|
{
|
|
int msec;
|
|
|
|
if(argc < 1)
|
|
return (-1);
|
|
|
|
/* check whether "Hit any key to continue..." prompt is requested */
|
|
if(argc < 2){
|
|
winx_printf("%s",PAUSE_MESSAGE);
|
|
(void)winx_kbhit(INFINITE);
|
|
winx_printf("\n\n");
|
|
return 0;
|
|
}
|
|
|
|
/* pause execution for specified time interval */
|
|
msec = (int)_wtol(argv[1]);
|
|
winx_sleep(msec);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Executes pending boot-off command.
|
|
* @return Boolean value indicating whether
|
|
* pending boot-off command was detected or not.
|
|
*/
|
|
int ExecPendingBootOff(void)
|
|
{
|
|
char path[MAX_PATH];
|
|
WINX_FILE *f;
|
|
|
|
if(winx_get_windows_directory(path,MAX_PATH) < 0){
|
|
DebugPrint("ExecPendingBootOff: cannot get %%windir%% path");
|
|
winx_printf("\nExecPendingBootOff: cannot get %%windir%% path\n\n");
|
|
short_dbg_delay();
|
|
return 0;
|
|
}
|
|
(void)strncat(path,"\\pending-boot-off",
|
|
MAX_PATH - strlen(path) - 1);
|
|
|
|
f = winx_fopen(path,"r");
|
|
if(f == NULL) return 0;
|
|
|
|
winx_fclose(f);
|
|
if(boot_off_handler(0,NULL,NULL) < 0){
|
|
short_dbg_delay();
|
|
}
|
|
if(winx_delete_file(path) < 0){
|
|
DebugPrint("ExecPendingBootOff: cannot delete %%windir%%\\pending-boot-off file");
|
|
winx_printf("\nExecPendingBootOff: cannot delete %%windir%%\\pending-boot-off file\n\n");
|
|
short_dbg_delay();
|
|
}
|
|
winx_printf("\nPending boot-off command execution completed.\n");
|
|
return 1;
|
|
}
|
|
|
|
static void SavePendingBootOffState(void)
|
|
{
|
|
char path[MAX_PATH];
|
|
WINX_FILE *f;
|
|
char *comment = "UltraDefrag boot-off command is pending.";
|
|
|
|
if(!pending_boot_off) return;
|
|
|
|
if(winx_get_windows_directory(path,MAX_PATH) < 0){
|
|
DebugPrint("SavePendingBootOffState: cannot get %%windir%% path");
|
|
winx_printf("\nSavePendingBootOffState: cannot get %%windir%% path\n\n");
|
|
short_dbg_delay();
|
|
return;
|
|
}
|
|
(void)strncat(path,"\\pending-boot-off",
|
|
MAX_PATH - strlen(path) - 1);
|
|
f = winx_fopen(path,"w");
|
|
if(f == NULL){
|
|
DebugPrint("%%windir%%\\pending-boot-off file creation failed");
|
|
winx_printf("\n%%windir%%\\pending-boot-off file creation failed\n\n");
|
|
short_dbg_delay();
|
|
return;
|
|
}
|
|
(void)winx_fwrite(comment,sizeof(char),sizeof(comment)/sizeof(char) - 1,f);
|
|
winx_fclose(f);
|
|
}
|
|
|
|
/**
|
|
* @brief boot-on command handler.
|
|
*/
|
|
static int boot_on_handler(int argc,short **argv,short **envp)
|
|
{
|
|
pending_boot_off = 0;
|
|
|
|
if(winx_register_boot_exec_command(L"defrag_native") < 0){
|
|
winx_printf("\nCannot enable the boot time defragmenter.\n\n");
|
|
return (-1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief boot-off command handler.
|
|
*/
|
|
int boot_off_handler(int argc,short **argv,short **envp)
|
|
{
|
|
pending_boot_off = 1;
|
|
|
|
if(winx_unregister_boot_exec_command(L"defrag_native") < 0){
|
|
winx_printf("\nCannot disable the boot time defragmenter.\n\n");
|
|
return (-1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief shutdown command handler.
|
|
*/
|
|
static int shutdown_handler(int argc,short **argv,short **envp)
|
|
{
|
|
winx_printf("Shutdown ...\n");
|
|
SavePendingBootOffState();
|
|
winx_shutdown();
|
|
winx_printf("\nShutdown your computer manually.\n");
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief reboot command handler.
|
|
*/
|
|
static int reboot_handler(int argc,short **argv,short **envp)
|
|
{
|
|
winx_printf("Reboot ...\n");
|
|
SavePendingBootOffState();
|
|
winx_reboot();
|
|
winx_printf("\nReboot your computer manually.\n");
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief exit command handler.
|
|
*/
|
|
int exit_handler(int argc,short **argv,short **envp)
|
|
{
|
|
int exit_code = 0;
|
|
|
|
if(!scripting_mode)
|
|
exit_flag = 1;
|
|
|
|
if(argc > 1)
|
|
exit_code = _wtoi(argv[1]);
|
|
|
|
winx_printf("Good bye ...\n");
|
|
winx_destroy_history(&history);
|
|
winx_exit(exit_code);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief call command handler.
|
|
*/
|
|
static int call_handler(int argc,short **argv,short **envp)
|
|
{
|
|
short *filename;
|
|
int i, length;
|
|
int result;
|
|
int old_scripting_mode;
|
|
|
|
if(argc < 1)
|
|
return (-1);
|
|
|
|
old_scripting_mode = scripting_mode;
|
|
|
|
if(argc < 2){
|
|
result = ProcessScript(NULL);
|
|
} else {
|
|
length = 0;
|
|
for(i = 1; i < argc; i++)
|
|
length += wcslen(argv[i]) + 1;
|
|
filename = winx_heap_alloc(length * sizeof(short));
|
|
if(filename == NULL){
|
|
winx_printf("\n%ws: cannot allocate %u bytes of memory\n\n",
|
|
argv[0],length * sizeof(short));
|
|
return (-1);
|
|
}
|
|
filename[0] = 0;
|
|
for(i = 1; i < argc; i++){
|
|
wcscat(filename,argv[i]);
|
|
if(i != argc - 1)
|
|
wcscat(filename,L" ");
|
|
}
|
|
result = ProcessScript(filename);
|
|
winx_heap_free(filename);
|
|
}
|
|
|
|
scripting_mode = old_scripting_mode;
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @brief test command handler.
|
|
* @details Paste here any code
|
|
* which needs to be tested.
|
|
*/
|
|
static int test_handler(int argc,short **argv,short **envp)
|
|
{
|
|
winx_printf("Hi, I'm here ;-)\n");
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief List of supported commands.
|
|
*/
|
|
cmd_table_entry cmd_table[] = {
|
|
{ L"boot-off", boot_off_handler },
|
|
{ L"boot-on", boot_on_handler },
|
|
{ L"call", call_handler },
|
|
{ L"echo", echo_handler },
|
|
{ L"echo.", echo_handler },
|
|
{ L"exit", exit_handler },
|
|
{ L"help", help_handler },
|
|
{ L"hexview", hexview_handler },
|
|
{ L"history", history_handler },
|
|
{ L"man", man_handler },
|
|
{ L"pause", pause_handler },
|
|
{ L"reboot", reboot_handler },
|
|
{ L"set", set_handler },
|
|
{ L"shutdown", shutdown_handler },
|
|
{ L"test", test_handler },
|
|
{ L"type", type_handler },
|
|
{ L"udefrag", udefrag_handler },
|
|
{ L"", NULL }
|
|
};
|
|
|
|
int first_command = 1;
|
|
|
|
/**
|
|
* @brief Executes the command.
|
|
* @param[in] cmdline the command line.
|
|
* @return Zero for success, negative
|
|
* value otherwise.
|
|
*/
|
|
int parse_command(short *cmdline)
|
|
{
|
|
int i, j, n, argc;
|
|
int at_detected = 0;
|
|
int arg_detected;
|
|
short *cmdline_copy;
|
|
short **argv;
|
|
short **envp;
|
|
short *string;
|
|
int length;
|
|
int result;
|
|
|
|
/* disable default logging before the first command parsing */
|
|
if(first_command){
|
|
winx_disable_dbg_log();
|
|
(void)winx_set_env_variable(L"UD_LOG_FILE_PATH",NULL);
|
|
first_command = 0;
|
|
}
|
|
|
|
/*
|
|
* Cleanup the command line by removing
|
|
* spaces and newlines from the beginning
|
|
* and the end of the string.
|
|
*/
|
|
while(*cmdline == 0x20 || *cmdline == '\t')
|
|
cmdline ++; /* skip leading spaces */
|
|
n = wcslen(cmdline);
|
|
for(i = n - 1; i >= 0; i--){
|
|
if(cmdline[i] != 0x20 && cmdline[i] != '\t' && \
|
|
cmdline[i] != '\n' && cmdline[i] != '\r') break;
|
|
cmdline[i] = 0; /* remove trailing spaces and newlines */
|
|
}
|
|
|
|
/*
|
|
* Skip @ in the beginning of the line.
|
|
*/
|
|
if(cmdline[0] == '@'){
|
|
at_detected = 1;
|
|
cmdline ++;
|
|
}
|
|
|
|
/*
|
|
* Handle empty lines and comments.
|
|
*/
|
|
if(cmdline[0] == 0 || cmdline[0] == ';' || cmdline[0] == '#'){
|
|
if(echo_flag && !at_detected)
|
|
winx_printf("%ws\n",cmdline);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Prepare argc, argv, envp variables.
|
|
* Return immediately if argc == 0.
|
|
*/
|
|
/* a. make a copy of command line */
|
|
n = wcslen(cmdline);
|
|
cmdline_copy = winx_heap_alloc((n + 1) * sizeof(short));
|
|
if(cmdline_copy == NULL){
|
|
winx_printf("\n%ws: cannot allocate %u bytes of memory\n\n",
|
|
cmdline,(n + 1) * sizeof(short));
|
|
return (-1);
|
|
}
|
|
wcscpy(cmdline_copy,cmdline);
|
|
/* b. replace all spaces by zeros */
|
|
argc = 1;
|
|
for(i = 0; i < n; i++){
|
|
if(cmdline_copy[i] == 0x20 || cmdline_copy[i] == '\t'){
|
|
cmdline_copy[i] = 0;
|
|
if(cmdline_copy[i+1] != 0x20 && cmdline_copy[i+1] != '\t')
|
|
argc ++;
|
|
}
|
|
}
|
|
/* c. allocate memory for argv array */
|
|
argv = winx_heap_alloc(sizeof(short *) * argc);
|
|
if(argv == NULL){
|
|
winx_printf("\n%ws: cannot allocate %u bytes of memory\n\n",
|
|
cmdline,sizeof(short *) * argc);
|
|
winx_heap_free(cmdline_copy);
|
|
return (-1);
|
|
}
|
|
/* d. fill argv array */
|
|
j = 0; arg_detected = 0;
|
|
for(i = 0; i < n; i++){
|
|
if(cmdline_copy[i]){
|
|
if(!arg_detected){
|
|
argv[j] = cmdline_copy + i;
|
|
j ++;
|
|
arg_detected = 1;
|
|
}
|
|
} else {
|
|
arg_detected = 0;
|
|
}
|
|
}
|
|
/* e. build environment */
|
|
envp = NULL;
|
|
if(peb){
|
|
if(peb->ProcessParameters){
|
|
if(peb->ProcessParameters->Environment){
|
|
/* build array of unicode strings */
|
|
string = peb->ProcessParameters->Environment;
|
|
for(n = 0; ; n++){
|
|
/* empty line indicates the end of environment */
|
|
if(string[0] == 0) break;
|
|
length = wcslen(string);
|
|
string += length + 1;
|
|
}
|
|
if(n > 0){
|
|
envp = winx_heap_alloc((n + 1) * sizeof(short *));
|
|
if(envp == NULL){
|
|
winx_printf("\n%ws: cannot allocate %u bytes of memory\n\n",
|
|
cmdline,(n + 1) * sizeof(short *));
|
|
} else {
|
|
RtlZeroMemory((void *)envp,(n + 1) * sizeof(short *));
|
|
string = peb->ProcessParameters->Environment;
|
|
for(i = 0; i < n; i++){
|
|
/* empty line indicates the end of environment */
|
|
if(string[0] == 0) break;
|
|
envp[i] = string;
|
|
length = wcslen(string);
|
|
string += length + 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Print command line if echo is on
|
|
* and the name of the command is not
|
|
* preceeding by the at sign.
|
|
* Don't print also if echo command
|
|
* is executed.
|
|
*/
|
|
if(echo_flag && !at_detected && wcscmp(argv[0],L"echo") && \
|
|
wcscmp(argv[0],L"echo.")) winx_printf("%ws\n",cmdline);
|
|
|
|
/*
|
|
* Check whether the command
|
|
* is supported or not.
|
|
*/
|
|
for(i = 0; cmd_table[i].cmd_handler != NULL; i++)
|
|
if(!wcscmp(argv[0],cmd_table[i].cmd_name)) break;
|
|
|
|
/*
|
|
* Handle unknown commands.
|
|
*/
|
|
if(cmd_table[i].cmd_handler == NULL){
|
|
winx_printf("\nUnknown command %ws!\n\n",argv[0]);
|
|
winx_heap_free(argv);
|
|
if(envp)
|
|
winx_heap_free(envp);
|
|
winx_heap_free(cmdline_copy);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Handle the command.
|
|
*/
|
|
result = cmd_table[i].cmd_handler(argc,argv,envp);
|
|
winx_heap_free(argv);
|
|
if(envp)
|
|
winx_heap_free(envp);
|
|
winx_heap_free(cmdline_copy);
|
|
return result;
|
|
}
|