Commit 74f440ea authored by Dave Pickles's avatar Dave Pickles Committed by Alexandre Julliard

Added Wine command-line interpreter.

parent 5cec819d
......@@ -5300,6 +5300,7 @@ programs/progman/Makefile
programs/regtest/Makefile
programs/regapi/Makefile
programs/view/Makefile
programs/wcmd/Makefile
programs/winhelp/Makefile
programs/winver/Makefile
rc/Makefile
......@@ -5469,6 +5470,7 @@ programs/progman/Makefile
programs/regtest/Makefile
programs/regapi/Makefile
programs/view/Makefile
programs/wcmd/Makefile
programs/winhelp/Makefile
programs/winver/Makefile
rc/Makefile
......
......@@ -802,6 +802,7 @@ programs/progman/Makefile
programs/regtest/Makefile
programs/regapi/Makefile
programs/view/Makefile
programs/wcmd/Makefile
programs/winhelp/Makefile
programs/winver/Makefile
rc/Makefile
......
......@@ -8,6 +8,7 @@ SUBDIRS = \
regapi \
regtest \
view \
wcmd \
winhelp \
winver
......
Makefile
wcmd
wcmdrc.s
v0.10 - 2 June 1999
Additional help text and error codes.
v0.09 - 5 May 1999
Directory byte counts and disk free space are reported with commas and in 64-bit.
File sizes have commas but are computed in 32 bits.
Handling of DIR /S on non-current path corrected.
DEL with wildcard or directory name works correctly.
v0.08 - 21 Mar 1999
Invoke an AUTOEXEC.BAT file if it exists in the root directory of the startup drive.
v0.07 - 8 Mar 1999
Can now be compiled as a WineLib app (conditional code added).
Additional help text.
Icon added to resources (the Wine-glass).
v0.06 - 23 Feb 1999
Help text moved into resource file to allow localisation.
Simple batch files (without parameters) can be executed.
v0.05 - 17 Feb 1999
Fixed problem with DIR command & long, complex relative paths.
DIR /S and /P implemented.
Date and time in PROMPT localised.
More work on batch files (they are echoed to screen but not executed).
v0.04 - 7 Feb 1999
Command-line qualifiers /c /q /k implemented (as NT's CMD.EXE).
ECHO command implemented, though echo mode is not honoured.
Environment variables in commands (eg %envvar%) expanded.
REN and COPY added, but no wildcard support or relative paths.
Filenames in quotes now handled.
PAUSE command.
Preliminary coding for batch files.
v0.03 - 5 Feb 1999
Added relative path and alternate drive support to DIR, also free disk space
(32-bit only!).
v0.02 - 27 Jan 1999
Added change-drive code.
v0.01 - 25 Jan 1999
Initial version.
DEFS = -DWINELIB
TOPSRCDIR = @top_srcdir@
TOPOBJDIR = ../..
SRCDIR = @srcdir@
VPATH = @srcdir@
MODULE = none
PROGRAMS = wcmd
ALL_LIBS = $(WINELIB) $(X_LIBS) $(XLIB) $(LIBS)
RCFLAGS = -w32 -h
WRCEXTRA = -A -t -p $*
C_SRCS = \
batch.c \
builtins.c \
directory.c \
wcmdmain.c
RC_SRCS = \
wcmdrc.rc
all: check_wrc $(PROGRAMS)
depend:: $(RC_SRCS:.rc=.h)
@MAKE_RULES@
#this line is needed to prevent winestub.o being linked
WINESTUB =
wcmd: $(OBJS)
$(CC) -o wcmd $(OBJS) $(LDOPTIONS) $(ALL_LIBS)
install: dummy
$(INSTALL_PROGRAM) wcmd $(bindir)/wcmd
uninstall: dummy
$(RM) $(bindir)/wcmd
$(RC_SRCS:.rc=.s): $(WRC)
dummy:
### Dependencies:
WCMD - A Command-Line Interface for WINE
Copyright (C) 1999 D Pickles (davep@nugate.demon.co.uk)
Open Source software published under the Wine Licence and Warranty.
This is an Alpha version and is very much "work in progress".
WHAT'S INCLUDED
- Sources
- A Makefile for compiling with LibWine. Build Wine with "-enable-dll" first.
- A Makefile for Borland C++ (needs editing for directories).
WHAT'S MISSING
- Redirection, shell parameters and pipes
- Command-line qualifiers for most builtin commands
- MOVE command (plus the batch-only ones)
- Wildcards and relative paths in COPY and RENAME
- Set functionality in DATE, TIME, ATTRIB, SET, LABEL
- Full internationalisation of the text (and commands?).
WHAT DOESN'T WORK
- At present it is not possible to launch Windows GDI programs from the command
line. This is result of the way the CreateProcess() API call is implemented in
Wine, and will be fixed in a later Wine release.
- The ATTRIB command reports all files having their Archive flag set, and the
READONLY setting depends on the Unix file permissions. All other flags are
always clear. The Wine attributes API calls map to the Unix stat() function
which cannot handle the other attributes available in DOS.
- Date/timestamps of files in the DIR listing are shown using the current
locale. As there is AFAIK no way to set the locale, they will always appear in
US format.
- Line editing and command recall doesn't work due to missing functionality in
Wine.
- File sizes in the DIR function are all given in 32 bits, though totals and
free space are computed to 64 bits.
- DIR/S fails if there is no matching file in the starting directory, ie
"DIR C:\TEMP\*.c /S" doesn't work if there is no file matching *.c in C:\TEMP
but one does exist in a lower directory.
- Copy, rename, move, need the source and destination to be specified fully
with an absolute or relative path but no wildcards or partial filenames.
- Simple batch files work, ie a list of commands as they would be typed. However
invoking a batch file from within another invokes the CALL function, control
returns to the calling batch file when the subfile exits.
WINE OR WIN32 BINARY?
Wcmd can be built as a Wine binary, or (using a Win32 compiler) as a Win32 .EXE
image. The Wine binary is simpler to invoke from the U**x command line or from
a GUI such as KDE, however it is not possible to invoke a second shell using the
"WCMD /C filename" syntax. Conversely a Win32 application can be invoked from a
Win32 GUI such as Program Manager but that needs starting under Wine first.
/*
* WCMD - Wine-compatible command line interface - batch interface.
*
* (C) 1999 D A Pickles
*
*/
#include "wcmd.h"
void WCMD_batch_command (HANDLE h, char *command);
char *WCMD_parameter (char *s, int n);
BOOL WCMD_go_to (HANDLE h, char *label);
extern HANDLE STDin, STDout;
extern char nyi[];
extern char newline[];
extern char version_string[];
extern int echo_mode;
extern char quals[MAX_PATH], param1[MAX_PATH], param2[MAX_PATH];
/****************************************************************************
* WCMD_batch
*
* Open and execute a batch file.
* On entry *command includes the complete command line beginning with the name
* of the batch file (if a CALL command was entered the CALL has been removed).
* *file is the name of the file, which might not exist and may not have the
* .BAT suffix on.
*
* We need to handle recursion correctly, since one batch program might call another.
*/
void WCMD_batch (char *file, char *command) {
HANDLE h;
char string[MAX_PATH];
int n;
strcpy (string, file);
CharLower (string);
if (strstr (string, ".bat") == NULL) strcat (string, ".bat");
h = CreateFile (string, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (h == INVALID_HANDLE_VALUE) {
WCMD_output ("File %s not found\n", string);
return;
}
/*
* Work through the file line by line. Specific batch commands are processed here,
* the rest are handled by the main command processor.
*/
while (WCMD_fgets (string, sizeof(string), h)) {
n = strlen (string);
if (string[n-1] == '\n') string[n-1] = '\0';
if (string[n-2] == '\r') string[n-2] = '\0'; /* Under Windoze we get CRLF! */
WCMD_batch_command (h, string);
}
CloseHandle (h);
}
/****************************************************************************
* WCMD_batch_command
*
* Execute one line from a batch file.
*/
void WCMD_batch_command (HANDLE h, char *command) {
DWORD status;
char cmd[1024];
if (echo_mode && (command[0] != '@')) WCMD_output ("%s", command);
status = ExpandEnvironmentStrings (command, cmd, sizeof(cmd));
if (!status) {
WCMD_print_error ();
return;
}
WCMD_process_command (cmd);
}
/****************************************************************************
* WCMD_go_to
*
* Batch file jump instruction. Not the most efficient algorithm ;-)
* Returns FALSE if the specified label cannot be found - the file pointer is
* then at EOF.
*/
BOOL WCMD_go_to (HANDLE h, char *label) {
char string[MAX_PATH];
SetFilePointer (h, 0, NULL, FILE_BEGIN);
while (WCMD_fgets (string, sizeof(string), h)) {
if ((string[0] == ':') && (strcmp (&string[1], label) == 0)) return TRUE;
}
return FALSE;
}
/*******************************************************************
* WCMD_parameter - extract a parameter from a command line.
*
* Returns the 'n'th space-delimited parameter on the command line.
* Parameter is in static storage overwritten on the next call.
* Parameters in quotes are handled.
*/
char *WCMD_parameter (char *s, int n) {
int i = -1;
static char param[MAX_PATH];
char *p;
p = param;
while (TRUE) {
switch (*s) {
case ' ':
s++;
break;
case '"':
s++;
while ((*s != '\0') && (*s != '"')) {
*p++ = *s++;
}
if (i == n) {
*p = '\0';
return param;
}
else {
param[0] = '\0';
i++;
}
if (*s == '"') s++;
break;
case '\0':
return param;
default:
while ((*s != '\0') && (*s != ' ')) {
*p++ = *s++;
}
if (i == n) {
*p = '\0';
return param;
}
else {
param[0] = '\0';
i++;
}
}
}
}
/****************************************************************************
* WCMD_fgets
*
* Get one line from a batch file. We can't use the native f* functions because
* of the filename syntax differences between DOS and Unix.
*/
char *WCMD_fgets (char *s, int n, HANDLE h) {
DWORD bytes;
BOOL status;
char *p;
p = s;
do {
status = ReadFile (h, s, 1, &bytes, NULL);
if ((status == 0) || (bytes == 0)) return NULL;
if (*s == '\n') bytes = 0;
*++s = '\0';
n--;
} while ((bytes == 1) && (n > 1));
return p;
}
/*
* WCMD - Wine-compatible command line interface - Directory functions.
*
* (C) 1999 D A Pickles
*
* On entry, global variables quals, param1, param2 contain
* the qualifiers (uppercased and concatenated) and parameters entered, with
* environment-variable and batch parameter substitution already done.
*/
/*
* FIXME:
* - 32-bit limit on individual file sizes (directories and free space are 64-bit)
* - DIR /S fails if the starting directory is not the current default.
*/
#include "wcmd.h"
int WCMD_dir_sort (const void *a, const void *b);
void WCMD_list_directory (char *path, int level);
char * WCMD_filesize64 (__int64 n);
char * WCMD_filesize32 (int n);
char * WCMD_strrev (char *buff);
extern HANDLE STDin, STDout;
extern char nyi[];
extern char newline[];
extern char version_string[];
extern char anykey[];
extern int echo_mode;
extern char quals[MAX_PATH], param1[MAX_PATH], param2[MAX_PATH];
int file_total, dir_total, line_count, page_mode, recurse;
__int64 byte_total;
/*****************************************************************************
* WCMD_directory
*
* List a file directory.
* FIXME: /S switch only works for the current directory
*
*/
void WCMD_directory () {
char path[MAX_PATH], drive[8];
int status;
__int64 free_space;
DWORD spc, bps, fc, capacity;
line_count = 5;
page_mode = (strstr(quals, "/P") != NULL);
recurse = (strstr(quals, "/S") != NULL);
if (param1[0] == '\0') strcpy (param1, ".");
GetFullPathName (param1, sizeof(path), path, NULL);
lstrcpyn (drive, path, 3);
status = WCMD_volume (0, drive);
if (!status) {
return;
}
WCMD_list_directory (path, 0);
lstrcpyn (drive, path, 4);
GetDiskFreeSpace (drive, &spc, &bps, &fc, &capacity);
free_space = bps * spc * fc;
WCMD_output (" %18s bytes free\n\n", WCMD_filesize64 (free_space));
if (recurse) {
WCMD_output ("Total files listed:\n%8d files%25s bytes\n%8d directories\n\n",
file_total, WCMD_filesize64 (byte_total), dir_total);
}
}
/*****************************************************************************
* WCMD_list_directory
*
* List a single file directory. This function (and those below it) can be called
* recursively when the /S switch is used.
*
* FIXME: Assumes individual files are less than 2**32 bytes.
* FIXME: Entries sorted by name only. Should we support DIRCMD??
* FIXME: Assumes 24-line display for the /P qualifier.
* FIXME: Other command qualifiers not supported.
* FIXME: DIR /S FILENAME fails if at least one matching file is not found in the top level.
*/
void WCMD_list_directory (char *search_path, int level) {
char string[1024], datestring[32], timestring[32];
char mem_err[] = "Memory Allocation Error";
char *p;
DWORD count;
WIN32_FIND_DATA *fd;
FILETIME ft;
SYSTEMTIME st;
HANDLE hff;
int status, dir_count, file_count, entry_count, i;
__int64 byte_count;
dir_count = 0;
file_count = 0;
entry_count = 0;
byte_count = 0;
/*
* If the path supplied does not include a wildcard, and the endpoint of the
* path references a directory, we need to list the *contents* of that
* directory not the directory file itself.
*/
if ((strchr(search_path, '*') == NULL) && (strchr(search_path, '%') == NULL)) {
status = GetFileAttributes (search_path);
if ((status != -1) && (status & FILE_ATTRIBUTE_DIRECTORY)) {
if (search_path[strlen(search_path)-1] == '\\') {
strcat (search_path, "*");
}
else {
strcat (search_path, "\\*");
}
}
}
fd = malloc (sizeof(WIN32_FIND_DATA));
hff = FindFirstFile (search_path, fd);
if (hff == INVALID_HANDLE_VALUE) {
WCMD_output ("File Not Found\n");
free (fd);
return;
}
do {
entry_count++;
fd = realloc (fd, (entry_count+1)*sizeof(WIN32_FIND_DATA));
if (fd == NULL) {
FindClose (hff);
WCMD_output (mem_err);
return;
}
} while (FindNextFile(hff, (fd+entry_count)) != 0);
FindClose (hff);
qsort (fd, entry_count, sizeof(WIN32_FIND_DATA), WCMD_dir_sort);
if (level != 0) WCMD_output ("\n\n");
WCMD_output ("Directory of %s\n\n", search_path);
if (page_mode) {
line_count += 2;
if (line_count > 23) {
line_count = 0;
WCMD_output (anykey);
ReadFile (STDin, string, sizeof(string), &count, NULL);
}
}
for (i=0; i<entry_count; i++) {
FileTimeToLocalFileTime (&(fd+i)->ftLastWriteTime, &ft);
FileTimeToSystemTime (&ft, &st);
GetDateFormat (0, DATE_SHORTDATE, &st, NULL, datestring,
sizeof(datestring));
GetTimeFormat (0, TIME_NOSECONDS, &st,
NULL, timestring, sizeof(timestring));
if ((fd+i)->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
dir_count++;
WCMD_output ("%8s %8s <DIR> %s\n",
datestring, timestring, (fd+i)->cFileName);
}
else {
file_count++;
byte_count += (fd+i)->nFileSizeLow;
WCMD_output ("%8s %8s %10s %s\n",
datestring, timestring,
WCMD_filesize32((fd+i)->nFileSizeLow), (fd+i)->cFileName);
}
if (page_mode) {
if (++line_count > 23) {
line_count = 0;
WCMD_output (anykey);
ReadFile (STDin, string, sizeof(string), &count, NULL);
}
}
}
if (file_count == 1) {
WCMD_output (" 1 file %25s bytes\n", WCMD_filesize64 (byte_count));
}
else {
WCMD_output ("%8d files %24s bytes\n", file_count, WCMD_filesize64 (byte_count));
}
if (page_mode) {
if (++line_count > 23) {
line_count = 0;
WCMD_output (anykey);
ReadFile (STDin, string, sizeof(string), &count, NULL);
}
}
byte_total = byte_total + byte_count;
file_total = file_total + file_count;
dir_total = dir_total + dir_count;
if (dir_count == 1) WCMD_output ("1 directory ");
else WCMD_output ("%8d directories", dir_count);
if (page_mode) {
if (++line_count > 23) {
line_count = 0;
WCMD_output (anykey);
ReadFile (STDin, string, sizeof(string), &count, NULL);
}
}
for (i=0; i<entry_count; i++) {
if ((recurse) &&
((fd+i)->cFileName[0] != '.') &&
((fd+i)->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
// GetFullPathName ((fd+i)->cFileName, sizeof(string), string, NULL);
p = strrchr (search_path, '\\');
lstrcpyn (string, search_path, (p-search_path+2));
lstrcat (string, (fd+i)->cFileName);
lstrcat (string, p);
WCMD_list_directory (string, 1);
}
}
free (fd);
return;
}
/*****************************************************************************
* WCMD_filesize64
*
* Convert a 64-bit number into a character string, with commas every three digits.
* Result is returned in a static string overwritten with each call.
* FIXME: There must be a better algorithm!
*/
char * WCMD_filesize64 (__int64 n) {
__int64 q;
int r, i;
char *p;
static char buff[32];
p = buff;
i = -3;
do {
if ((++i)%3 == 1) *p++ = ',';
q = n / 10;
r = n - (q * 10);
*p++ = r + '0';
*p = '\0';
n = q;
} while (n != 0);
WCMD_strrev (buff);
return buff;
}
/*****************************************************************************
* WCMD_filesize32
*
* Convert a 32-bit number into a character string, with commas every three digits.
* Result is returned in a static string overwritten with each call.
* FIXME: There must be a better algorithm!
*/
char * WCMD_filesize32 (int n) {
int r, i;
char *p, *q;
static char buff1[16], buff2[16];
wsprintf (buff1, "%i", n);
r = lstrlen (buff1);
WCMD_strrev (buff1);
p = buff1;
q = buff2;
for (i=0; i<r; i++) {
if ((i-2)%3 == 1) *q++ = ',';
*q++ = *p++;
}
*q = '\0';
WCMD_strrev (buff2);
return buff2;
}
/*****************************************************************************
* WCMD_strrev
*
* Reverse a character string in-place (strrev() is not available under unixen :-( ).
*/
char * WCMD_strrev (char *buff) {
int r, i;
char b;
r = lstrlen (buff);
for (i=0; i<r/2; i++) {
b = buff[i];
buff[i] = buff[r-i-1];
buff[r-i-1] = b;
}
return (buff);
}
int WCMD_dir_sort (const void *a, const void *b) {
return (lstrcmpi(((WIN32_FIND_DATA *)a)->cFileName,
((WIN32_FIND_DATA *)b)->cFileName));
}
#-----------------------------------------------------------------------------
VERSION = BCB.01
#-----------------------------------------------------------------------------
!ifndef BCB
BCB = $(MAKEDIR)\..
!endif
PROJECT = wcmd.exe
OBJFILES = builtins.obj wcmdmain.obj directory.obj batch.obj
RESFILES = wcmdrc.rc
RESDEPEN = $(RESFILES)
LIBFILES =
DEFFILE =
#-----------------------------------------------------------------------------
CFLAG1 = -c
CFLAG2 = -H=C:\BC\PROJECTS\CMD.CSM -nC:\BC\PROJECTS\CMD \
-IC:\BC\INCLUDE;C:\BC\PROJECTS\CMD
PFLAGS = -U$(BCB)\lib\obj -jph -m
RFLAGS = -I$(BCB)\include;C:\BC\INCLUDE;C:\BC\PROJECTS\CMD
LFLAGS = -ap -Tpe -c -x -L$(BCB)\lib;$(BCB)\lib\obj;C:\BC\LIB
IFLAGS = -i
LINKER = tlink32
#-----------------------------------------------------------------------------
ALLOBJ = c0x32.obj $(OBJFILES)
ALLRES = $(RESFILES)
ALLLIB = $(LIBFILES) noeh32.lib import32.lib cw32mt.lib
# ---------------------------------------------------------------------------
.autodepend
$(PROJECT): $(OBJFILES) $(RESDEPEN) $(DEFFILE)
$(BCB)\BIN\$(LINKER) @&&!
$(LFLAGS) +
$(ALLOBJ), +
$(PROJECT),, +
$(ALLLIB), +
$(DEFFILE), +
$(ALLRES)
!
.cpp.obj:
$(BCB)\BIN\bcc32 $(CFLAG1) $(CFLAG2) -o$* $*
.c.obj:
$(BCB)\BIN\bcc32 $(CFLAG1) $(CFLAG2) -o$* $**
.rc.res:
$(BCB)\BIN\brcc32 $(RFLAGS) $<
#-----------------------------------------------------------------------------
/*
* WCMD - Wine-compatible command line interface.
*
* (C) 1999 D A Pickles
*/
#define IDI_ICON1 1
#include <windows.h>
#ifndef RC_INVOKED
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <ctype.h>
#ifdef WINELIB
#include <winbase.h>
#include <wincon.h>
#endif /* !WINELIB */
void WCMD_batch (char *, char *);
void WCMD_call (void);
void WCMD_change_tty (void);
void WCMD_clear_screen (void);
void WCMD_copy (void);
void WCMD_create_dir (void);
void WCMD_delete (int recurse);
void WCMD_directory (void);
void WCMD_echo (char *);
void WCMD_for (void);
void WCMD_give_help (char *command);
void WCMD_if (void);
void WCMD_move (void);
void WCMD_output (char *format, ...);
void WCMD_parse (char *s, char *q, char *p1, char *p2);
void WCMD_pause (void);
void WCMD_print_error (void);
void WCMD_process_command (char *command);
void WCMD_remove_dir (void);
void WCMD_rename (void);
void WCMD_run_program (char *command);
void WCMD_setshow_attrib (void);
void WCMD_setshow_date (void);
void WCMD_setshow_default (void);
void WCMD_setshow_env (char *command);
void WCMD_setshow_path (void);
void WCMD_setshow_prompt (void);
void WCMD_setshow_time (void);
void WCMD_shift (void);
void WCMD_show_prompt (void);
void WCMD_type (void);
void WCMD_verify (void);
void WCMD_version (void);
int WCMD_volume (int mode, char *command);
char *WCMD_fgets (char *s, int n, HANDLE stream);
char *WCMD_strtrim_leading_spaces (char *string);
void WCMD_strtrim_trailing_spaces (char *string);
#endif /* !RC_INVOKED */
/*
* Serial nos of builtin commands. These constants must be in step with
* the list of strings defined in WCMD.C, and WCMD_EXIT *must* always be
* the last one.
*
* Yes it *would* be nice to use an enumeration here, but the Resource
* Compiler won't accept resource IDs from enumerations :-(
*/
#define WCMD_ATTRIB 0
#define WCMD_CALL 1
#define WCMD_CD 2
#define WCMD_CHDIR 3
#define WCMD_CLS 4
#define WCMD_COPY 5
#define WCMD_CTTY 6
#define WCMD_DATE 7
#define WCMD_DEL 8
#define WCMD_DIR 9
#define WCMD_ECHO 10
#define WCMD_ERASE 11
#define WCMD_FOR 12
#define WCMD_GOTO 13
#define WCMD_HELP 14
#define WCMD_IF 15
#define WCMD_LABEL 16
#define WCMD_MD 17
#define WCMD_MKDIR 18
#define WCMD_MOVE 19
#define WCMD_PATH 20
#define WCMD_PAUSE 21
#define WCMD_PROMPT 22
#define WCMD_REM 23
#define WCMD_REN 24
#define WCMD_RENAME 25
#define WCMD_RD 26
#define WCMD_RMDIR 27
#define WCMD_SET 28
#define WCMD_SHIFT 29
#define WCMD_TIME 30
#define WCMD_TYPE 31
#define WCMD_VERIFY 32
#define WCMD_VER 33
#define WCMD_VOL 34
#define WCMD_EXIT 35
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment