672 lines
13 KiB
C++
Raw Normal View History

/*
2008-08-21 04:58:19 +00:00
* $Id: Awk.cpp 341 2008-08-20 10:58:19Z baconevi $
2009-02-17 02:11:31 +00:00
*
Copyright 2006-2009 Chung, Hyung-Hwan.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
2008-12-21 21:35:07 +00:00
#include <qse/awk/StdAwk.hpp>
#include <qse/cmn/str.h>
2009-06-04 15:50:32 +00:00
#include <qse/cmn/stdio.h>
#include <qse/cmn/main.h>
#include <stdlib.h>
#include <math.h>
#if defined(_WIN32)
2009-01-19 08:32:51 +00:00
# include <windows.h>
#else
2009-01-19 08:32:51 +00:00
# include <unistd.h>
# include <signal.h>
# include <errno.h>
#endif
2009-07-09 07:01:45 +00:00
class MyAwk;
2009-01-19 08:32:51 +00:00
#ifdef _WIN32
static BOOL WINAPI stop_run (DWORD ctrl_type);
#else
static void stop_run (int sig);
#endif
2009-02-17 02:11:31 +00:00
static void set_intr_run (void);
static void unset_intr_run (void);
2009-07-09 07:01:45 +00:00
MyAwk* app_awk = QSE_NULL;
2008-01-01 07:16:29 +00:00
static bool verbose = false;
2009-07-09 07:01:45 +00:00
class MyAwk: public QSE::StdAwk
{
public:
2009-07-09 07:01:45 +00:00
MyAwk (): srcInName(QSE_NULL), srcOutName(QSE_NULL)
{
#ifdef _WIN32
2008-12-21 21:35:07 +00:00
heap = QSE_NULL;
#endif
}
2009-07-09 07:01:45 +00:00
~MyAwk ()
{
close ();
}
int open ()
{
#ifdef _WIN32
2008-12-21 21:35:07 +00:00
QSE_ASSERT (heap == QSE_NULL);
heap = ::HeapCreate (0, 1000000, 1000000);
2008-12-21 21:35:07 +00:00
if (heap == QSE_NULL) return -1;
#endif
int n = StdAwk::open ();
2009-07-02 07:14:39 +00:00
if (n <= -1)
{
#ifdef _WIN32
HeapDestroy (heap);
2008-12-21 21:35:07 +00:00
heap = QSE_NULL;
#endif
return -1;
}
2008-12-21 21:35:07 +00:00
idLastSleep = addGlobal (QSE_T("LAST_SLEEP"));
2009-07-02 07:14:39 +00:00
if (idLastSleep <= -1) goto failure;
2008-12-21 21:35:07 +00:00
if (addFunction (QSE_T("sleep"), 1, 1,
2009-07-09 07:01:45 +00:00
(FunctionHandler)&MyAwk::sleep) <= -1) goto failure;
2008-12-21 21:35:07 +00:00
if (addFunction (QSE_T("sumintarray"), 1, 1,
2009-07-09 07:01:45 +00:00
(FunctionHandler)&MyAwk::sumintarray) <= -1) goto failure;
2008-12-21 21:35:07 +00:00
if (addFunction (QSE_T("arrayindices"), 1, 1,
2009-07-09 07:01:45 +00:00
(FunctionHandler)&MyAwk::arrayindices) <= -1) goto failure;
return 0;
failure:
StdAwk::close ();
#ifdef _WIN32
HeapDestroy (heap);
2008-12-21 21:35:07 +00:00
heap = QSE_NULL;
#endif
return -1;
}
void close ()
{
StdAwk::close ();
#ifdef _WIN32
2008-12-21 21:35:07 +00:00
if (heap != QSE_NULL)
{
HeapDestroy (heap);
2008-12-21 21:35:07 +00:00
heap = QSE_NULL;
}
#endif
}
int sleep (Run& run, Return& ret, const Argument* args, size_t nargs,
const char_t* name, size_t len)
{
if (args[0].isIndexed())
{
run.setError (ERR_INVAL);
return -1;
}
long_t x = args[0].toInt();
/*Argument arg;
if (run.getGlobal(idLastSleep, arg) == 0)
2008-12-21 21:35:07 +00:00
qse_printf (QSE_T("GOOD: [%d]\n"), (int)arg.toInt());
else { qse_printf (QSE_T("BAD:\n")); }
*/
2009-07-02 07:14:39 +00:00
if (run.setGlobal (idLastSleep, x) <= -1) return -1;
#ifdef _WIN32
::Sleep ((DWORD)(x * 1000));
return ret.set ((long_t)0);
#else
return ret.set ((long_t)::sleep (x));
#endif
}
int sumintarray (Run& run, Return& ret, const Argument* args, size_t nargs,
const char_t* name, size_t len)
{
long_t x = 0;
if (args[0].isIndexed())
{
Argument idx(run), val(run);
int n = args[0].getFirstIndex (idx);
while (n > 0)
{
size_t len;
const char_t* ptr = idx.toStr(&len);
2009-07-02 07:14:39 +00:00
if (args[0].getIndexed(ptr, len, val) <= -1) return -1;
x += val.toInt ();
n = args[0].getNextIndex (idx);
}
if (n != 0) return -1;
}
else x += args[0].toInt();
return ret.set (x);
}
int arrayindices (Run& run, Return& ret, const Argument* args, size_t nargs,
const char_t* name, size_t len)
{
if (!args[0].isIndexed()) return 0;
Argument idx (run);
long_t i;
int n = args[0].getFirstIndex (idx);
for (i = 0; n > 0; i++)
{
size_t len;
const char_t* ptr = idx.toStr(&len);
n = args[0].getNextIndex (idx);
2009-07-02 07:14:39 +00:00
if (ret.setIndexed (i, ptr, len) <= -1) return -1;
}
if (n != 0) return -1;
return 0;
}
2009-07-09 07:01:45 +00:00
Run* parse (const char_t* in, const char_t* out)
{
srcInName = in;
srcOutName = out;
return StdAwk::parse ();
}
protected:
2009-07-09 07:01:45 +00:00
bool onLoopEnter (Run& run)
{
2009-02-17 02:11:31 +00:00
set_intr_run ();
return true;
}
2009-07-09 07:01:45 +00:00
void onLoopExit (Run& run, const Argument& ret)
{
2009-02-17 02:11:31 +00:00
unset_intr_run ();
2008-01-01 07:16:29 +00:00
if (verbose)
{
size_t len;
const char_t* ptr = ret.toStr (&len);
2008-12-21 21:35:07 +00:00
qse_printf (QSE_T("*** return [%.*s] ***\n"), (int)len, ptr);
2008-01-01 07:16:29 +00:00
}
}
2009-02-17 02:11:31 +00:00
int openSource (Source& io)
{
Source::Mode mode = io.getMode();
2008-12-21 21:35:07 +00:00
FILE* fp = QSE_NULL;
// TODO: use sio instead of stdio
if (mode == Source::READ)
{
2008-12-21 21:35:07 +00:00
if (srcInName == QSE_NULL)
{
io.setHandle (stdin);
return 0;
}
2008-12-21 21:35:07 +00:00
if (srcInName[0] == QSE_T('\0')) fp = stdin;
else fp = qse_fopen (srcInName, QSE_T("r"));
}
else if (mode == Source::WRITE)
{
2008-12-21 21:35:07 +00:00
if (srcOutName == QSE_NULL)
{
io.setHandle (stdout);
return 0;
}
2008-12-21 21:35:07 +00:00
if (srcOutName[0] == QSE_T('\0')) fp = stdout;
else fp = qse_fopen (srcOutName, QSE_T("w"));
}
2008-12-21 21:35:07 +00:00
if (fp == QSE_NULL) return -1;
io.setHandle (fp);
return 1;
}
int closeSource (Source& io)
{
FILE* fp = (FILE*)io.getHandle();
if (fp == stdout || fp == stderr) fflush (fp);
if (fp != stdin && fp != stdout && fp != stderr) fclose (fp);
2008-12-21 21:35:07 +00:00
io.setHandle (QSE_NULL);
return 0;
}
ssize_t readSource (Source& io, char_t* buf, size_t len)
{
FILE* fp = (FILE*)io.getHandle();
ssize_t n = 0;
while (n < (ssize_t)len)
{
2008-12-21 21:35:07 +00:00
qse_cint_t c = qse_fgetc (fp);
if (c == QSE_CHAR_EOF)
2008-01-16 00:36:07 +00:00
{
2008-12-21 21:35:07 +00:00
if (qse_ferror(fp)) n = -1;
2008-01-16 00:36:07 +00:00
break;
}
buf[n++] = c;
2008-12-21 21:35:07 +00:00
if (c == QSE_T('\n')) break;
}
return n;
}
ssize_t writeSource (Source& io, char_t* buf, size_t len)
{
FILE* fp = (FILE*)io.getHandle();
size_t left = len;
while (left > 0)
{
2008-12-21 21:35:07 +00:00
if (*buf == QSE_T('\0'))
{
if (qse_fputc(*buf,fp) == QSE_CHAR_EOF) return -1;
left -= 1; buf += 1;
}
else
{
2008-12-21 21:35:07 +00:00
int chunk = (left > QSE_TYPE_MAX(int))? QSE_TYPE_MAX(int): (int)left;
int n = qse_fprintf (fp, QSE_T("%.*s"), chunk, buf);
if (n < 0 || n > chunk) return -1;
left -= n; buf += n;
}
}
return len;
}
void* allocMem (size_t n) throw ()
{
#ifdef _WIN32
return ::HeapAlloc (heap, 0, n);
#else
return ::malloc (n);
#endif
}
void* reallocMem (void* ptr, size_t n) throw ()
{
#ifdef _WIN32
if (ptr == NULL)
return ::HeapAlloc (heap, 0, n);
else
return ::HeapReAlloc (heap, 0, ptr, n);
#else
return ::realloc (ptr, n);
#endif
}
void freeMem (void* ptr) throw ()
{
#ifdef _WIN32
::HeapFree (heap, 0, ptr);
#else
::free (ptr);
#endif
}
private:
const char_t* srcInName;
const char_t* srcOutName;
int idLastSleep;
#ifdef _WIN32
void* heap;
#endif
};
2009-01-19 08:32:51 +00:00
#ifdef _WIN32
static BOOL WINAPI stop_run (DWORD ctrl_type)
{
if (ctrl_type == CTRL_C_EVENT ||
ctrl_type == CTRL_CLOSE_EVENT)
{
if (app_awk) app_awk->stop ();
return TRUE;
}
return FALSE;
}
#else
2009-02-17 02:11:31 +00:00
static int setsignal (int sig, void(*handler)(int), int restart)
{
struct sigaction sa_int;
sa_int.sa_handler = handler;
sigemptyset (&sa_int.sa_mask);
sa_int.sa_flags = 0;
if (restart)
{
#ifdef SA_RESTART
sa_int.sa_flags |= SA_RESTART;
#endif
}
else
{
#ifdef SA_INTERRUPT
sa_int.sa_flags |= SA_INTERRUPT;
#endif
}
return sigaction (sig, &sa_int, NULL);
}
2009-01-19 08:32:51 +00:00
static void stop_run (int sig)
{
int e = errno;
if (app_awk) app_awk->stop ();
errno = e;
}
#endif
2009-02-17 02:11:31 +00:00
static void set_intr_run (void)
{
#ifdef _WIN32
SetConsoleCtrlHandler (stop_run, TRUE);
#else
/*setsignal (SIGINT, stop_run, 1); TO BE MORE COMPATIBLE WITH WIN32*/
setsignal (SIGINT, stop_run, 0);
#endif
}
static void unset_intr_run (void)
{
#ifdef _WIN32
SetConsoleCtrlHandler (stop_run, FALSE);
#else
setsignal (SIGINT, SIG_DFL, 1);
#endif
}
2009-01-19 08:32:51 +00:00
2008-12-21 21:35:07 +00:00
static void print_error (const qse_char_t* msg)
{
2009-07-09 07:01:45 +00:00
qse_fprintf (QSE_STDERR, QSE_T("ERROR: %s\n"), msg);
}
static struct
{
2008-12-21 21:35:07 +00:00
const qse_char_t* name;
2009-07-09 07:01:45 +00:00
MyAwk::Option opt;
} otab[] =
{
2009-07-09 07:01:45 +00:00
{ QSE_T("implicit"), MyAwk::OPT_IMPLICIT },
{ QSE_T("explicit"), MyAwk::OPT_EXPLICIT },
{ QSE_T("bxor"), MyAwk::OPT_BXOR },
{ QSE_T("shift"), MyAwk::OPT_SHIFT },
{ QSE_T("idiv"), MyAwk::OPT_IDIV },
{ QSE_T("rio"), MyAwk::OPT_RIO },
{ QSE_T("rwpipe"), MyAwk::OPT_RWPIPE },
{ QSE_T("newline"), MyAwk::OPT_NEWLINE },
{ QSE_T("stripspaces"), MyAwk::OPT_STRIPSPACES },
{ QSE_T("nextofile"), MyAwk::OPT_NEXTOFILE },
{ QSE_T("crlf"), MyAwk::OPT_CRLF },
{ QSE_T("reset"), MyAwk::OPT_RESET },
{ QSE_T("maptovar"), MyAwk::OPT_MAPTOVAR },
{ QSE_T("pablock"), MyAwk::OPT_PABLOCK }
};
2008-12-21 21:35:07 +00:00
static void print_usage (const qse_char_t* argv0)
{
2008-12-21 21:35:07 +00:00
const qse_char_t* base;
int j;
2008-12-21 21:35:07 +00:00
base = qse_strrchr(argv0, QSE_T('/'));
if (base == QSE_NULL) base = qse_strrchr(argv0, QSE_T('\\'));
if (base == QSE_NULL) base = argv0; else base++;
qse_printf (QSE_T("Usage: %s [-si file]? [-so file]? [-ci file]* [-co file]* [-w o:n]* \n"), base);
2008-12-21 21:35:07 +00:00
qse_printf (QSE_T(" -si file Specify the input source file\n"));
qse_printf (QSE_T(" The source code is read from stdin when it is not specified\n"));
qse_printf (QSE_T(" -so file Specify the output source file\n"));
qse_printf (QSE_T(" The deparsed code is not output when is it not specified\n"));
qse_printf (QSE_T(" -ci file Specify the input console file\n"));
qse_printf (QSE_T(" -co file Specify the output console file\n"));
qse_printf (QSE_T(" -w o:n Specify an old and new word pair\n"));
qse_printf (QSE_T(" o - an original word\n"));
qse_printf (QSE_T(" n - the new word to replace the original\n"));
qse_printf (QSE_T(" -v Print extra messages\n"));
qse_printf (QSE_T("\nYou may specify the following options to change the behavior of the interpreter.\n"));
for (j = 0; j < (int)QSE_COUNTOF(otab); j++)
{
2008-12-21 21:35:07 +00:00
qse_printf (QSE_T(" -%-20s -no%-20s\n"), otab[j].name, otab[j].name);
}
}
2008-12-21 21:35:07 +00:00
static int awk_main (int argc, qse_char_t* argv[])
{
2009-07-09 07:01:45 +00:00
MyAwk awk;
MyAwk::Run* run;
2008-02-09 03:04:38 +00:00
int mode = 0;
2008-12-21 21:35:07 +00:00
const qse_char_t* srcin = QSE_T("");
const qse_char_t* srcout = NULL;
qse_size_t nsrcins = 0;
qse_size_t nsrcouts = 0;
2009-07-02 07:14:39 +00:00
if (awk.open() <= -1)
{
2009-07-09 07:01:45 +00:00
print_error (awk.getErrorMessage());
return -1;
}
// ARGV[0]
if (awk.addArgument (QSE_T("awk05")) <= -1)
{
print_error (awk.getErrorMessage());
awk.close ();
return -1;
}
for (int i = 1; i < argc; i++)
{
if (mode == 0)
{
2008-12-21 21:35:07 +00:00
if (qse_strcmp(argv[i], QSE_T("-si")) == 0) mode = 1;
else if (qse_strcmp(argv[i], QSE_T("-so")) == 0) mode = 2;
else if (qse_strcmp(argv[i], QSE_T("-ci")) == 0) mode = 3;
else if (qse_strcmp(argv[i], QSE_T("-co")) == 0) mode = 4;
else if (qse_strcmp(argv[i], QSE_T("-w")) == 0) mode = 5;
2008-12-21 21:35:07 +00:00
else if (qse_strcmp(argv[i], QSE_T("-v")) == 0)
2008-01-01 07:16:29 +00:00
{
verbose = true;
}
else
{
2008-12-21 21:35:07 +00:00
if (argv[i][0] == QSE_T('-'))
{
int j;
2008-12-21 21:35:07 +00:00
if (argv[i][1] == QSE_T('n') && argv[i][2] == QSE_T('o'))
{
for (j = 0; j < (int)QSE_COUNTOF(otab); j++)
{
2008-12-21 21:35:07 +00:00
if (qse_strcmp(&argv[i][3], otab[j].name) == 0)
{
awk.setOption (awk.getOption() & ~otab[j].opt);
goto ok_valid;
}
}
}
else
{
for (j = 0; j < (int)QSE_COUNTOF(otab); j++)
{
2008-12-21 21:35:07 +00:00
if (qse_strcmp(&argv[i][1], otab[j].name) == 0)
{
awk.setOption (awk.getOption() | otab[j].opt);
goto ok_valid;
}
}
}
}
print_usage (argv[0]);
return -1;
ok_valid:
;
}
}
else
{
2008-12-21 21:35:07 +00:00
if (argv[i][0] == QSE_T('-'))
{
print_usage (argv[0]);
return -1;
}
if (mode == 1) // source input
{
if (nsrcins != 0)
{
print_usage (argv[0]);
return -1;
}
srcin = argv[i];
nsrcins++;
mode = 0;
}
else if (mode == 2) // source output
{
if (nsrcouts != 0)
{
print_usage (argv[0]);
return -1;
}
srcout = argv[i];
nsrcouts++;
mode = 0;
}
else if (mode == 3) // console input
{
if (awk.addArgument (argv[i]) <= -1)
{
2008-12-21 21:35:07 +00:00
print_error (QSE_T("too many console inputs"));
return -1;
}
mode = 0;
}
else if (mode == 4) // console output
{
2009-07-02 07:14:39 +00:00
if (awk.addConsoleOutput (argv[i]) <= -1)
{
2008-12-21 21:35:07 +00:00
print_error (QSE_T("too many console outputs"));
return -1;
}
mode = 0;
}
else if (mode == 5) // word replacement
{
2008-12-21 21:35:07 +00:00
const qse_char_t* p;
qse_size_t l;
2008-12-21 21:35:07 +00:00
p = qse_strchr(argv[i], QSE_T(':'));
if (p == QSE_NULL)
{
print_usage (argv[0]);
return -1;
}
2008-12-21 21:35:07 +00:00
l = qse_strlen (argv[i]);
awk.setWord (
argv[i], p - argv[i],
p + 1, l - (p - argv[i] + 1));
mode = 0;
}
}
}
if (mode != 0)
{
print_usage (argv[0]);
awk.close ();
return -1;
}
2009-07-09 07:01:45 +00:00
run = awk.parse (srcin, srcout);
if (run == QSE_NULL)
{
2008-12-21 21:35:07 +00:00
qse_fprintf (stderr, QSE_T("cannot parse: LINE[%d] %s\n"),
awk.getErrorLine(), awk.getErrorMessage());
awk.close ();
return -1;
}
awk.enableRunCallback ();
2009-02-17 02:11:31 +00:00
app_awk = &awk;
2009-07-02 07:14:39 +00:00
if (awk.loop () <= -1)
{
2008-12-21 21:35:07 +00:00
qse_fprintf (stderr, QSE_T("cannot run: LINE[%d] %s\n"),
awk.getErrorLine(), awk.getErrorMessage());
awk.close ();
return -1;
}
2009-07-09 07:01:45 +00:00
#if 0
MyAwk::Return args[2];
args[0].setRun (run);
args[1].setRun (run);
if (awk.call (QSE_T("add"), args, 2) <= -1)
{
qse_fprintf (stderr, QSE_T("cannot run: LINE[%d] %s\n"),
awk.getErrorLine(), awk.getErrorMessage());
awk.close ();
}
#endif
2009-02-17 02:11:31 +00:00
app_awk = QSE_NULL;
awk.close ();
return 0;
}
int qse_main (int argc, qse_achar_t* argv[])
{
return qse_runmain (argc,argv,awk_main);
}