src/AwkHandler.h
author František Kučera <franta-hg@frantovo.cz>
Fri, 10 May 2019 00:45:37 +0200
branchv_0
changeset 22 98acfdc4c20b
parent 21 d46a727b7965
child 23 bc20d499a458
permissions -rw-r--r--
some TODO → FIXME
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
0
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
     1
/**
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
     2
 * Relational pipes
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
     3
 * Copyright © 2019 František Kučera (Frantovo.cz, GlobalCode.info)
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
     4
 *
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
     5
 * This program is free software: you can redistribute it and/or modify
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
     6
 * it under the terms of the GNU General Public License as published by
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
     7
 * the Free Software Foundation, either version 3 of the License, or
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
     8
 * (at your option) any later version.
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
     9
 *
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    10
 * This program is distributed in the hope that it will be useful,
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    13
 * GNU General Public License for more details.
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    14
 *
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    15
 * You should have received a copy of the GNU General Public License
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    16
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    17
 */
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    18
#pragma once
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    19
13
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
    20
#include<functional>
0
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    21
#include <memory>
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    22
#include <string>
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    23
#include <vector>
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    24
#include <iostream>
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    25
#include <sstream>
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    26
#include <locale>
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    27
#include <codecvt>
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    28
#include <regex>
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    29
1
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
    30
#include <unistd.h>
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
    31
#include <wait.h>
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
    32
#include <ext/stdio_filebuf.h>
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
    33
0
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    34
#include <relpipe/reader/typedefs.h>
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    35
#include <relpipe/reader/TypeId.h>
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    36
#include <relpipe/reader/handlers/RelationalReaderStringHandler.h>
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    37
#include <relpipe/reader/handlers/AttributeMetadata.h>
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    38
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    39
#include <relpipe/writer/Factory.h>
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    40
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    41
#include <relpipe/cli/RelpipeCLIException.h>
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    42
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    43
#include "Configuration.h"
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    44
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    45
namespace relpipe {
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    46
namespace tr {
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    47
namespace awk {
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    48
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    49
using namespace std;
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    50
using namespace relpipe;
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    51
using namespace relpipe::reader;
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    52
using namespace relpipe::reader::handlers;
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    53
5
86de8e6ab231 documentation
František Kučera <franta-hg@frantovo.cz>
parents: 4
diff changeset
    54
/**
86de8e6ab231 documentation
František Kučera <franta-hg@frantovo.cz>
parents: 4
diff changeset
    55
 * This transformation consists of three processes connected together using pipes.
86de8e6ab231 documentation
František Kučera <franta-hg@frantovo.cz>
parents: 4
diff changeset
    56
 * 
86de8e6ab231 documentation
František Kučera <franta-hg@frantovo.cz>
parents: 4
diff changeset
    57
 * Processes:
86de8e6ab231 documentation
František Kučera <franta-hg@frantovo.cz>
parents: 4
diff changeset
    58
 *	- Parent: process RelationalReaderStringHandler events (read relational data, usually from STDIN) and pass them in the special text format to the pipe1
86de8e6ab231 documentation
František Kučera <franta-hg@frantovo.cz>
parents: 4
diff changeset
    59
 *  - AWK: external program (/usr/bin/awk), read from the pipe1, writes to the pipe2
86de8e6ab231 documentation
František Kučera <franta-hg@frantovo.cz>
parents: 4
diff changeset
    60
 *  - Writer: reads from the pipe2 and writes to relationalWriter (relational data, usually to STDOUT)
86de8e6ab231 documentation
František Kučera <franta-hg@frantovo.cz>
parents: 4
diff changeset
    61
 */
0
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    62
class AwkHandler : public RelationalReaderStringHandler {
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    63
private:
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    64
	Configuration configuration;
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    65
	writer::RelationalWriter* relationalWriter;
13
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
    66
	std::function<void() > relationalWriterFlush;
7
46db0e6e548b pass name-value pair to AWK
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
    67
	std::wstring_convert<codecvt_utf8<wchar_t>> convertor; // TODO: support also other encodings
0
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
    68
2
580ccb511301 for each attribute
František Kučera <franta-hg@frantovo.cz>
parents: 1
diff changeset
    69
	int awkInputWriterFD = -1;
13
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
    70
	RelationConfiguration* currentRelationConfiguration = nullptr;
7
46db0e6e548b pass name-value pair to AWK
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
    71
	std::vector<AttributeMetadata> currentReaderMetadata;
13
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
    72
	vector<writer::AttributeMetadata> currentWriterMetadata;
7
46db0e6e548b pass name-value pair to AWK
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
    73
	integer_t currentAttributeIndex = 0;
2
580ccb511301 for each attribute
František Kučera <franta-hg@frantovo.cz>
parents: 1
diff changeset
    74
1
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
    75
	void createPipe(int& readerFD, int& writerFD) {
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
    76
		int fds[2];
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
    77
		int result = pipe(fds);
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
    78
		readerFD = fds[0];
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
    79
		writerFD = fds[1];
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
    80
		if (result < 0) throw cli::RelpipeCLIException(L"Unable to create a pipe.", cli::CLI::EXIT_CODE_UNEXPECTED_ERROR); // TODO: better exceptions?
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
    81
	}
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
    82
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
    83
	void redirectFD(int oldfd, int newfd) {
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
    84
		int result = dup2(oldfd, newfd);
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
    85
		if (result < 0) throw cli::RelpipeCLIException(L"Unable redirect FD.", cli::CLI::EXIT_CODE_UNEXPECTED_ERROR); // TODO: better exceptions?
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
    86
	}
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
    87
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
    88
	void closeOrThrow(int fd) {
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
    89
		int error = close(fd);
2
580ccb511301 for each attribute
František Kučera <franta-hg@frantovo.cz>
parents: 1
diff changeset
    90
		if (error) throw cli::RelpipeCLIException(L"Unable to close FD: " + to_wstring(fd) + L" from PID: " + to_wstring(getpid()), cli::CLI::EXIT_CODE_UNEXPECTED_ERROR); // TODO: better exceptions?
580ccb511301 for each attribute
František Kučera <franta-hg@frantovo.cz>
parents: 1
diff changeset
    91
	}
580ccb511301 for each attribute
František Kučera <franta-hg@frantovo.cz>
parents: 1
diff changeset
    92
11
f515d14794e0 variable execvp() arguments
František Kučera <franta-hg@frantovo.cz>
parents: 10
diff changeset
    93
	void execp(const std::vector<std::string>& args) {
f515d14794e0 variable execvp() arguments
František Kučera <franta-hg@frantovo.cz>
parents: 10
diff changeset
    94
		const char** a = new const char*[args.size() + 1];
f515d14794e0 variable execvp() arguments
František Kučera <franta-hg@frantovo.cz>
parents: 10
diff changeset
    95
		for (size_t i = 0; i < args.size(); i++) a[i] = args[i].c_str();
f515d14794e0 variable execvp() arguments
František Kučera <franta-hg@frantovo.cz>
parents: 10
diff changeset
    96
		a[args.size()] = nullptr;
f515d14794e0 variable execvp() arguments
František Kučera <franta-hg@frantovo.cz>
parents: 10
diff changeset
    97
f515d14794e0 variable execvp() arguments
František Kučera <franta-hg@frantovo.cz>
parents: 10
diff changeset
    98
		execvp(a[0], (char*const*) a);
f515d14794e0 variable execvp() arguments
František Kučera <franta-hg@frantovo.cz>
parents: 10
diff changeset
    99
f515d14794e0 variable execvp() arguments
František Kučera <franta-hg@frantovo.cz>
parents: 10
diff changeset
   100
		delete[] a;
f515d14794e0 variable execvp() arguments
František Kučera <franta-hg@frantovo.cz>
parents: 10
diff changeset
   101
		throw cli::RelpipeCLIException(L"Unable to do execvp().", cli::CLI::EXIT_CODE_UNEXPECTED_ERROR); // TODO: better exceptions?
f515d14794e0 variable execvp() arguments
František Kučera <franta-hg@frantovo.cz>
parents: 10
diff changeset
   102
	}
f515d14794e0 variable execvp() arguments
František Kučera <franta-hg@frantovo.cz>
parents: 10
diff changeset
   103
18
2ac59242d0cb environment variables
František Kučera <franta-hg@frantovo.cz>
parents: 17
diff changeset
   104
	/* TODO: move to lib-cli when stable and used in other modules */
2ac59242d0cb environment variables
František Kučera <franta-hg@frantovo.cz>
parents: 17
diff changeset
   105
	void setEnv(const char * name, const string_t& value) {
2ac59242d0cb environment variables
František Kučera <franta-hg@frantovo.cz>
parents: 17
diff changeset
   106
		setenv(name, convertor.to_bytes(value).c_str(), true);
2ac59242d0cb environment variables
František Kučera <franta-hg@frantovo.cz>
parents: 17
diff changeset
   107
	}
2ac59242d0cb environment variables
František Kučera <franta-hg@frantovo.cz>
parents: 17
diff changeset
   108
2ac59242d0cb environment variables
František Kučera <franta-hg@frantovo.cz>
parents: 17
diff changeset
   109
	/* TODO: move to lib-cli when stable and used in other modules */
2ac59242d0cb environment variables
František Kučera <franta-hg@frantovo.cz>
parents: 17
diff changeset
   110
	void setEnv(const char * name, std::string& value) {
2ac59242d0cb environment variables
František Kučera <franta-hg@frantovo.cz>
parents: 17
diff changeset
   111
		setenv(name, value.c_str(), true);
2ac59242d0cb environment variables
František Kučera <franta-hg@frantovo.cz>
parents: 17
diff changeset
   112
	}
2ac59242d0cb environment variables
František Kučera <franta-hg@frantovo.cz>
parents: 17
diff changeset
   113
2ac59242d0cb environment variables
František Kučera <franta-hg@frantovo.cz>
parents: 17
diff changeset
   114
	/* TODO: move to lib-cli when stable and used in other modules */
2ac59242d0cb environment variables
František Kučera <franta-hg@frantovo.cz>
parents: 17
diff changeset
   115
	void setEnv(const char * name, const integer_t& value) {
2ac59242d0cb environment variables
František Kučera <franta-hg@frantovo.cz>
parents: 17
diff changeset
   116
		setenv(name, to_string(value).c_str(), true);
2ac59242d0cb environment variables
František Kučera <franta-hg@frantovo.cz>
parents: 17
diff changeset
   117
	}
2ac59242d0cb environment variables
František Kučera <franta-hg@frantovo.cz>
parents: 17
diff changeset
   118
13
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   119
	void addDefinition(std::vector<std::string>& awkCliArgs, DefinitionRecipe& d) {
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   120
		awkCliArgs.push_back("-v");
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   121
		awkCliArgs.push_back(convertor.to_bytes(a2v(d.name) + L"=" + d.value));
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   122
	}
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   123
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   124
	void add(vector<AttributeMetadata>& readerAttributes, vector<writer::AttributeMetadata>& writerAttributes) {
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   125
		for (AttributeMetadata readerAttributes : readerAttributes)
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   126
			writerAttributes.push_back({
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   127
				readerAttributes.getAttributeName(),
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   128
				relationalWriter->toTypeId(readerAttributes.getTypeName())
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   129
			});
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   130
	}
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   131
2
580ccb511301 for each attribute
František Kučera <franta-hg@frantovo.cz>
parents: 1
diff changeset
   132
	void cleanUp() {
580ccb511301 for each attribute
František Kučera <franta-hg@frantovo.cz>
parents: 1
diff changeset
   133
		if (awkInputWriterFD >= 0) {
580ccb511301 for each attribute
František Kučera <franta-hg@frantovo.cz>
parents: 1
diff changeset
   134
			closeOrThrow(awkInputWriterFD);
22
98acfdc4c20b some TODO → FIXME
František Kučera <franta-hg@frantovo.cz>
parents: 21
diff changeset
   135
			// FIXME: check exit codes
2
580ccb511301 for each attribute
František Kučera <franta-hg@frantovo.cz>
parents: 1
diff changeset
   136
			__pid_t waitResult1 = wait(NULL);
580ccb511301 for each attribute
František Kučera <franta-hg@frantovo.cz>
parents: 1
diff changeset
   137
			__pid_t waitResult2 = wait(NULL);
580ccb511301 for each attribute
František Kučera <franta-hg@frantovo.cz>
parents: 1
diff changeset
   138
			awkInputWriterFD = -1;
580ccb511301 for each attribute
František Kučera <franta-hg@frantovo.cz>
parents: 1
diff changeset
   139
		}
7
46db0e6e548b pass name-value pair to AWK
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   140
46db0e6e548b pass name-value pair to AWK
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   141
		currentAttributeIndex = 0;
46db0e6e548b pass name-value pair to AWK
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   142
		currentReaderMetadata.clear();
13
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   143
		currentWriterMetadata.clear();
19
e4558df9ba2d drop – run AWK code but generate no output for given relation
František Kučera <franta-hg@frantovo.cz>
parents: 18
diff changeset
   144
		currentRelationConfiguration = nullptr;
7
46db0e6e548b pass name-value pair to AWK
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   145
	}
46db0e6e548b pass name-value pair to AWK
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   146
46db0e6e548b pass name-value pair to AWK
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   147
	string_t a2v(const string_t& attributeName) {
46db0e6e548b pass name-value pair to AWK
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   148
		// FIXME: escape reserved names; prefix with _ ?
46db0e6e548b pass name-value pair to AWK
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   149
		// cat awkgram.y | awk -v FS='\\{"|",' -v ORS='|' '/static const struct token tokentab/, /\};/ { if (/^\{/) { print $2} }'
46db0e6e548b pass name-value pair to AWK
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   150
		// BEGIN|BEGINFILE|END|ENDFILE|adump|and|asort|asorti|atan2|bindtextdomain|break|case|close|compl|continue|cos|dcgettext|dcngettext|default|delete|do|else|eval|exit|exp|fflush|for|func|function|gensub|getline|gsub|if|in|include|index|int|intdiv0|isarray|length|load|log|lshift|match|mktime|namespace|next|nextfile|or|patsplit|print|printf|rand|return|rshift|sin|split|sprintf|sqrt|srand|stopme|strftime|strtonum|sub|substr|switch|system|systime|tolower|toupper|typeof|while|xor
46db0e6e548b pass name-value pair to AWK
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   151
		return attributeName;
46db0e6e548b pass name-value pair to AWK
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   152
	}
46db0e6e548b pass name-value pair to AWK
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   153
46db0e6e548b pass name-value pair to AWK
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   154
	string_t escapeAwkValue(const string_t& value) {
46db0e6e548b pass name-value pair to AWK
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   155
		// FIXME: escape field and record separators
46db0e6e548b pass name-value pair to AWK
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   156
		return value;
1
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
   157
	}
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
   158
20
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   159
	void processAwkOutput(int awkOutputReaderFD) {
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   160
		locale::global(locale("")); // needed for processing unicode texts, otherwise getline() stopped working on first line with non-ascii characters; TODO: move somewhere else?
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   161
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   162
		__gnu_cxx::stdio_filebuf<wchar_t> awkOutputReaderBuffer(awkOutputReaderFD, std::ios::in);
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   163
		std::wistream awkOutputReader(&awkOutputReaderBuffer);
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   164
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   165
		if (currentRelationConfiguration->drop) {
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   166
			for (wchar_t ch; awkOutputReader.read(&ch, 1).good();); // just eat the lines from the AWK
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   167
		} else {
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   168
			std::wstringstream currentValue;
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   169
			for (wchar_t ch; awkOutputReader.read(&ch, 1).good();) {
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   170
				if (ch == '\t' || ch == '\n') {
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   171
					relationalWriter->writeAttribute(currentValue.str());
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   172
					currentValue.str(L"");
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   173
					currentValue.clear();
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   174
				} else if (ch == '\\') {
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   175
					ch = awkOutputReader.get();
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   176
					if (ch == 't') currentValue << L'\t';
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   177
					else if (ch == 'n') currentValue << L'\n';
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   178
					else if (ch == '\\') currentValue << L'\\';
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   179
					else throw cli::RelpipeCLIException(L"Unknown escape sequence. Only \\t, \\n and \\\\ are supported.", cli::CLI::EXIT_CODE_UNEXPECTED_ERROR); // TODO: better exceptions?
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   180
				} else {
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   181
					currentValue << ch;
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   182
				}
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   183
			}
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   184
		}
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   185
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   186
		closeOrThrow(awkOutputReaderFD);
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   187
	}
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   188
0
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   189
public:
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   190
13
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   191
	/**
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   192
	 * @param relationalWriter
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   193
	 * @param relationalWriterFlush the writer must be flushed before fork() in order to 
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   194
	 * avoid duplicate output (otherwise single relation might be written from two processes); 
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   195
	 * This is a little hack – if it stops working, we should reconnect the pipes 
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   196
	 * and use the writer only from a single process and avoid its effective duplication,
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   197
	 * or use different writers for each relation (or process).
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   198
	 * @param configuration
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   199
	 */
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   200
	AwkHandler(writer::RelationalWriter* relationalWriter, std::function<void() > relationalWriterFlush, Configuration& configuration) : relationalWriter(relationalWriter), relationalWriterFlush(relationalWriterFlush), configuration(configuration) {
0
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   201
	}
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   202
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   203
	void startRelation(string_t name, vector<AttributeMetadata> attributes) override {
2
580ccb511301 for each attribute
František Kučera <franta-hg@frantovo.cz>
parents: 1
diff changeset
   204
		cleanUp();
0
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   205
13
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   206
		for (int i = 0; i < configuration.relationConfigurations.size(); i++) {
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   207
			if (regex_match(name, wregex(configuration.relationConfigurations[i].relation))) {
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   208
				currentRelationConfiguration = &configuration.relationConfigurations[i];
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   209
				break; // it there are multiple matches, only the first configuration is used
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   210
			}
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   211
		}
1
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
   212
21
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   213
		currentReaderMetadata = attributes;
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   214
		// TODO: move to a reusable method (or use same metadata on both reader and writer side?)		
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   215
		if (currentRelationConfiguration && currentRelationConfiguration->writerMetadata.size()) {
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   216
			if (currentRelationConfiguration->inputAttributesPrepend) add(currentReaderMetadata, currentWriterMetadata);
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   217
			currentWriterMetadata.insert(currentWriterMetadata.end(), currentRelationConfiguration->writerMetadata.begin(), currentRelationConfiguration->writerMetadata.end());
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   218
			if (currentRelationConfiguration->inputAttributesAppend) add(currentReaderMetadata, currentWriterMetadata);
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   219
		} else {
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   220
			add(currentReaderMetadata, currentWriterMetadata);
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   221
		}
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   222
13
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   223
		if (currentRelationConfiguration) {
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   224
			int awkInputReaderFD;
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   225
			int awkOutputReaderFD;
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   226
			int awkOutputWriterFD;
1
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
   227
13
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   228
			createPipe(awkInputReaderFD, awkInputWriterFD);
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   229
			createPipe(awkOutputReaderFD, awkOutputWriterFD);
1
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
   230
13
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   231
			relationalWriterFlush();
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   232
			__pid_t awkPid = fork();
11
f515d14794e0 variable execvp() arguments
František Kučera <franta-hg@frantovo.cz>
parents: 10
diff changeset
   233
13
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   234
			if (awkPid < 0) {
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   235
				throw cli::RelpipeCLIException(L"Unable to fork AWK process.", cli::CLI::EXIT_CODE_UNEXPECTED_ERROR); // TODO: better exceptions?
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   236
			} else if (awkPid == 0) {
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   237
				// AWK child process
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   238
				closeOrThrow(awkInputWriterFD);
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   239
				closeOrThrow(awkOutputReaderFD);
12
8844ebce8fb4 pass defined global variables to AWK as CLI arguments
František Kučera <franta-hg@frantovo.cz>
parents: 11
diff changeset
   240
13
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   241
				redirectFD(awkInputReaderFD, STDIN_FILENO);
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   242
				redirectFD(awkOutputWriterFD, STDOUT_FILENO);
11
f515d14794e0 variable execvp() arguments
František Kučera <franta-hg@frantovo.cz>
parents: 10
diff changeset
   243
18
2ac59242d0cb environment variables
František Kučera <franta-hg@frantovo.cz>
parents: 17
diff changeset
   244
				// Environment variables:
2ac59242d0cb environment variables
František Kučera <franta-hg@frantovo.cz>
parents: 17
diff changeset
   245
				setEnv("relationName", name);
2ac59242d0cb environment variables
František Kučera <franta-hg@frantovo.cz>
parents: 17
diff changeset
   246
				setEnv("inputAttributeCount", currentReaderMetadata.size());
2ac59242d0cb environment variables
František Kučera <franta-hg@frantovo.cz>
parents: 17
diff changeset
   247
				setEnv("outputAttributeCount", currentWriterMetadata.size());
2ac59242d0cb environment variables
František Kučera <franta-hg@frantovo.cz>
parents: 17
diff changeset
   248
				// TODO: better names? ENV vs. AWK variables?
2ac59242d0cb environment variables
František Kučera <franta-hg@frantovo.cz>
parents: 17
diff changeset
   249
				for (int i = 0; i < currentReaderMetadata.size(); i++) {
2ac59242d0cb environment variables
František Kučera <franta-hg@frantovo.cz>
parents: 17
diff changeset
   250
					setEnv((std::string("inputAttributeName") + std::to_string(i)).c_str(), currentReaderMetadata[i].getAttributeName());
2ac59242d0cb environment variables
František Kučera <franta-hg@frantovo.cz>
parents: 17
diff changeset
   251
					setEnv("inputAttributeType" + i, currentReaderMetadata[i].getTypeName());
2ac59242d0cb environment variables
František Kučera <franta-hg@frantovo.cz>
parents: 17
diff changeset
   252
				}
2ac59242d0cb environment variables
František Kučera <franta-hg@frantovo.cz>
parents: 17
diff changeset
   253
				for (int i = 0; i < currentWriterMetadata.size(); i++) {
2ac59242d0cb environment variables
František Kučera <franta-hg@frantovo.cz>
parents: 17
diff changeset
   254
					setEnv("outputAttributeName" + i, currentWriterMetadata[i].attributeName);
2ac59242d0cb environment variables
František Kučera <franta-hg@frantovo.cz>
parents: 17
diff changeset
   255
					// setEnv("outputAttributeType" + i, currentWriterMetadata[i].typeId); // TODO: type?
2ac59242d0cb environment variables
František Kučera <franta-hg@frantovo.cz>
parents: 17
diff changeset
   256
				}
2ac59242d0cb environment variables
František Kučera <franta-hg@frantovo.cz>
parents: 17
diff changeset
   257
13
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   258
				// AWK script:
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   259
				std::wstringstream awkScript;
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   260
				awkScript << L"BEGIN {" << std::endl;
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   261
				awkScript << L"FS=\"\\t\";" << std::endl;
18
2ac59242d0cb environment variables
František Kučera <franta-hg@frantovo.cz>
parents: 17
diff changeset
   262
				awkScript << L"OFS=\"\\t\";" << std::endl;
15
ba91a464d2b3 implement --for-each, --before-records, --after-records
František Kučera <franta-hg@frantovo.cz>
parents: 13
diff changeset
   263
				awkScript << currentRelationConfiguration->awkBeforeRecords << std::endl;
13
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   264
				awkScript << L"};" << std::endl;
15
ba91a464d2b3 implement --for-each, --before-records, --after-records
František Kučera <franta-hg@frantovo.cz>
parents: 13
diff changeset
   265
				awkScript << std::endl;
1
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
   266
13
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   267
				awkScript << L"END {" << std::endl;
15
ba91a464d2b3 implement --for-each, --before-records, --after-records
František Kučera <franta-hg@frantovo.cz>
parents: 13
diff changeset
   268
				awkScript << currentRelationConfiguration->awkAfterRecords << std::endl;
13
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   269
				awkScript << L"};" << std::endl;
15
ba91a464d2b3 implement --for-each, --before-records, --after-records
František Kučera <franta-hg@frantovo.cz>
parents: 13
diff changeset
   270
				awkScript << std::endl;
1
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
   271
21
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   272
				awkScript << L"function _escape(value) {" << std::endl;
22
98acfdc4c20b some TODO → FIXME
František Kučera <franta-hg@frantovo.cz>
parents: 21
diff changeset
   273
				// FIXME: escape function
21
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   274
				awkScript << L"return value;" << std::endl;
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   275
				awkScript << L"};" << std::endl;
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   276
				awkScript << std::endl;
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   277
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   278
				awkScript << L"function _unescape(value) {" << std::endl;
22
98acfdc4c20b some TODO → FIXME
František Kučera <franta-hg@frantovo.cz>
parents: 21
diff changeset
   279
				// FIXME: unescape function
21
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   280
				awkScript << L"return value;" << std::endl;
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   281
				awkScript << L"};" << std::endl;
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   282
				awkScript << std::endl;
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   283
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   284
				awkScript << L"function _readVariables() {" << std::endl;
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   285
				for (int i = 0; i < currentReaderMetadata.size(); i++) awkScript << a2v(currentReaderMetadata[i].getAttributeName()) << L"=_unescape($" << (i + 1) << L");" << std::endl;
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   286
				awkScript << L"};" << std::endl;
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   287
				awkScript << std::endl;
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   288
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   289
				awkScript << L"function _writeVariables() {" << std::endl;
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   290
				awkScript << L"NF=" << currentWriterMetadata.size() << ";" << std::endl;
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   291
				for (int i = 0; i < currentWriterMetadata.size(); i++) awkScript << L"$" << (i + 1) << L"=_escape(" << a2v(currentWriterMetadata[i].attributeName) << L");" << std::endl;
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   292
				awkScript << L"};" << std::endl;
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   293
				awkScript << std::endl;
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   294
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   295
				awkScript << L"function record() {" << std::endl;
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   296
				awkScript << L"_writeVariables();" << std::endl;
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   297
				awkScript << L"print;" << std::endl;
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   298
				awkScript << L"};" << std::endl;
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   299
				awkScript << std::endl;
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   300
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   301
				awkScript << L"{ _readVariables();  }" << std::endl; // read line (input attributes) into AWK variables
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   302
				awkScript << L"{ _writeVariables(); }" << std::endl; // write AWK variables to the line (so it matches the output attributes and can be implicitly printed without explicit record() call)
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   303
				awkScript << std::endl;
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   304
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   305
				awkScript << currentRelationConfiguration->awkForEach << std::endl; // user's code – can modify variables, filter results or explicitly call record() (can generate additional records or duplicate them)
1
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
   306
13
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   307
				// CLI arguments:
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   308
				std::vector<std::string> args;
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   309
				args.push_back("awk");
1
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
   310
13
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   311
				for (auto d : configuration.definitions) addDefinition(args, d);
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   312
				for (auto d : currentRelationConfiguration->definitions) addDefinition(args, d);
3
e086ae6a19c3 generate (some) relational output
František Kučera <franta-hg@frantovo.cz>
parents: 2
diff changeset
   313
13
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   314
				args.push_back(convertor.to_bytes(awkScript.str()));
1
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
   315
13
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   316
				// Runs AWK program found on $PATH → user can plug-in a custom implementation or a wrapper, but this can be also bit dangerous (however AWK itself is dangerous).
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   317
				execp(args);
1
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
   318
			} else {
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
   319
				// Parent process
13
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   320
				closeOrThrow(awkInputReaderFD);
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   321
				closeOrThrow(awkOutputWriterFD);
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   322
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   323
				__pid_t writerPid = fork();
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   324
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   325
				if (writerPid < 0) {
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   326
					throw cli::RelpipeCLIException(L"Unable to fork Writer process.", cli::CLI::EXIT_CODE_UNEXPECTED_ERROR); // TODO: better exceptions?
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   327
				} else if (writerPid == 0) {
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   328
					// Writer child process
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   329
					closeOrThrow(awkInputWriterFD);
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   330
19
e4558df9ba2d drop – run AWK code but generate no output for given relation
František Kučera <franta-hg@frantovo.cz>
parents: 18
diff changeset
   331
					if (currentRelationConfiguration->drop) {
e4558df9ba2d drop – run AWK code but generate no output for given relation
František Kučera <franta-hg@frantovo.cz>
parents: 18
diff changeset
   332
						// TODO: omit whole this process and pipe AWK output to /dev/null?
e4558df9ba2d drop – run AWK code but generate no output for given relation
František Kučera <franta-hg@frantovo.cz>
parents: 18
diff changeset
   333
					} else {
21
d46a727b7965 read and write variables, generate real relation
František Kučera <franta-hg@frantovo.cz>
parents: 20
diff changeset
   334
						relationalWriter->startRelation(name, currentWriterMetadata, true);
19
e4558df9ba2d drop – run AWK code but generate no output for given relation
František Kučera <franta-hg@frantovo.cz>
parents: 18
diff changeset
   335
					}
13
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   336
20
f937ad57351f read and un-escape the AWK output, convert to relational attributes
František Kučera <franta-hg@frantovo.cz>
parents: 19
diff changeset
   337
					processAwkOutput(awkOutputReaderFD);
13
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   338
					exit(0);
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   339
				} else {
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   340
					// Parent process
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   341
					closeOrThrow(awkOutputReaderFD);
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   342
				}
1
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
   343
			}
13
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   344
		} else {
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   345
			relationalWriter->startRelation(name, currentWriterMetadata, true);
1
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
   346
		}
05d969cd90d4 fork() processes
František Kučera <franta-hg@frantovo.cz>
parents: 0
diff changeset
   347
0
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   348
	}
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   349
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   350
	void attribute(const string_t& value) override {
13
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   351
		if (currentRelationConfiguration) {
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   352
			currentAttributeIndex++;
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   353
			currentAttributeIndex = currentAttributeIndex % currentReaderMetadata.size();
7
46db0e6e548b pass name-value pair to AWK
František Kučera <franta-hg@frantovo.cz>
parents: 6
diff changeset
   354
17
54716ea44d52 pass only values to the awk process
František Kučera <franta-hg@frantovo.cz>
parents: 15
diff changeset
   355
			std::string awkValue = convertor.to_bytes(escapeAwkValue(value));
54716ea44d52 pass only values to the awk process
František Kučera <franta-hg@frantovo.cz>
parents: 15
diff changeset
   356
			if (currentAttributeIndex == 0) awkValue += "\n";
54716ea44d52 pass only values to the awk process
František Kučera <franta-hg@frantovo.cz>
parents: 15
diff changeset
   357
			else awkValue += "\t";
13
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   358
17
54716ea44d52 pass only values to the awk process
František Kučera <franta-hg@frantovo.cz>
parents: 15
diff changeset
   359
			write(awkInputWriterFD, awkValue.c_str(), awkValue.length());
13
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   360
		} else {
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   361
			relationalWriter->writeAttribute(value);
b74001992ec3 implement --relation option (thus some relations might pass unmodified by AWK), support per-relation variables
František Kučera <franta-hg@frantovo.cz>
parents: 12
diff changeset
   362
		}
0
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   363
	}
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   364
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   365
	void endOfPipe() {
2
580ccb511301 for each attribute
František Kučera <franta-hg@frantovo.cz>
parents: 1
diff changeset
   366
		cleanUp();
0
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   367
	}
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   368
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   369
};
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   370
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   371
}
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   372
}
644fd2ce2580 project skeleton
František Kučera <franta-hg@frantovo.cz>
parents:
diff changeset
   373
}