<commit_before>//
//  generateSimulatedData.cpp
//  cPWP
//
//  Created by Evan McCartney-Melstad on 12/31/14.
//  Copyright (c) 2014 Evan McCartney-Melstad. All rights reserved.
//

#include "generateSimulatedData.h"
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>

int generateReadsAndMap (int numIndividuals, double mutationRateStepSize, std::string baseErrorRate, std::string libFragmentSize, std::string stdevLibFragmentSize, std::string numReadPairs, std::string readLengths, std::string randomSeed, std::string reference, std::string threads) {
/*// supply 1) number of individuals, 2) the mutation rate steps between them, 3) the base error rate, 4) the average library fragment size, 5) the standard deviation of the library fragment size, 6) the number of read pairs for each individual, 7) read lengths (per read), 8) random number generator seed

    So, for instance, if numIndividuals is 5, and mutationRateSteps is 0.01, then there will be four individuals generated with mutation rate steps of 0.01, 0.02, 0.03, and 0.04 away from the reference genome. Individual 0 is created as being identical to the reference genome (mutation rate parameter set to 0.00).
*/

    std::ofstream bamsFile;
    bamsFile.open("bamlist.txt", std::ios::out | std::ios::app);

    std::cout << "**********\nChecking if processor is available to run wgsim...";
    if (system(NULL)) puts ("Ok");
    else exit (EXIT_FAILURE);

    std::vector<std::string> wgsimCommands;
    int step = 0;
    while(step <= numIndividuals) {
        double mutRate = step * mutationRateStepSize;
        // Covert the mutation rate into a string for the system command
        std::ostringstream mutStrs;
        mutStrs << mutRate;
        std::string mutRateString = mutStrs.str();

        std::cout << "**********\nGenerating sequence reads for individual " << step << " using a mutation rate of " << mutRateString << " from the reference genome\n**********\n";

        // Get the number of the individual as a string
        std::ostringstream stepString;
        stepString << step;
        std::string ind = stepString.str();

        // Generate the output file names as strings
        std::string R1out = "ind" + ind + "_R1.fastq";
        std::string R2out = "ind" + ind + "_R2.fastq";
        std::string polymorphismFile = "ind" + ind + "_polymorphisms.txt";

        // Generate the wgsim command and then run it using a system call
        std::string wgsimCommandToRun = "wgsim -N " + numReadPairs + " -r " + mutRateString + " -R 0.00 -X 0.00 -d " + libFragmentSize + " -s " + stdevLibFragmentSize +  " -1 " + readLengths + " -2 " + readLengths + " -S " + randomSeed + " -e0 " + reference + " " + R1out + " " + R2out + " > " + polymorphismFile; // No indels, no probability of indel extension, no base call error rates
        if (system((wgsimCommandToRun).c_str()) != 0) {
            std::cout << "**********\nFailure running the following command: " << wgsimCommandToRun << "\n**********\n";
            exit(EXIT_FAILURE);
        } else {
            std::cout << "**********\nExecuted the following command: " << wgsimCommandToRun << "\n**********\n";

        }

        // Generate the bwa mem command and then run it using a system call
        std::string bamOut = "ind" + ind + ".bam";
        std::string bwaCommandToRun = "bwa mem -t " + threads + " " + reference + " " + R1out + " " + R2out + " | samtools view -bS - | samtools sort -T temp -o " + bamOut + " -";
        if (system((bwaCommandToRun).c_str()) != 0) {
            std::cout << "**********\nFailure running the following command: " << bwaCommandToRun << "\n**********\n";
            exit(EXIT_FAILURE);
        } else {
            std::cout << "**********\nExecuted the following command: " << bwaCommandToRun << "\n**********\n";
        }
        bamsFile << bamOut << std::endl;
        step++; // Move on to the next individual
    }
    bamsFile.close();
    return 0;
}

/*
 /home/evan/bin/angsd0.613/angsd -bam bamlist272.txt -out 272torts_allCounts_minmapq20minq30 -uniqueOnly 1 -only_proper_pairs 1 -remove_bads 1 -doCounts 1 -nThreads 8 -dumpCounts 4 -doMaf 1 -doMajorMinor 2 -GL 2 -minMapQ 20 -minQ 30 > 272tortsangsdMapQ30_allSites.log 2>&1

*/

/*
 wgsim -N 10000000 -r 0.01 -R 0.00 -X 0.00 -1 100 -2 100 -S11 -e0 lgv2.fasta r1_noerr1percDivergent_10mil.fq r2_noerr1percDivergent_10mil.fq > 1perc_polymorphisms.txt
 wgsim -N 10000000 -r 0.00 -1 100 -2 100 -S11 -e0 lgv2.fasta r1_noerrnomut_10mil.fq r2_noerrnomut_10mil.fq

 bwa mem -t 10 lgv2.fasta r1_noerrnomut_10mil.fq r2_noerrnomut_10mil.fq
 bwa mem -t 10 lgv2.fasta r1_noerr1percDivergent_10mil.fq r2_noerr1percDivergent_10mil.fq

 samtools sort -T noMut -o 1XcoverageNoErrNoMutSorted.bam 1XcoverageNoErrNoMut.bam
 samtools sort -T mut -o 1XcoverageNoErr1PercentDivergentSorted.bam 1XcoverageNoErr1PercDivergent.bam

 echo -e "1XcoverageNoErrNoMutSorted.bam\n1XcoverageNoErr1PercentDivergentSorted.bam" > bamlist2sim.txt

 /home/evan/bin/angsd0.613/angsd -bam bamlist2sim.txt -out 2tortsim1perDiv -uniqueOnly 1 -only_proper_pairs 1 -remove_bads 1 -doCounts 1 -nThreads 8 -dumpCounts 4 -doMaf 1 -doMajorMinor 2 -GL 2 -minMapQ 20 -minQ 30 > 2tortANGSD.log 2>&1 &

*/

/*
 Usage:   wgsim [options] <in.ref.fa> <out.read1.fq> <out.read2.fq>

 Options:
 -e FLOAT      base error rate [0.000]
 -d INT        outer distance between the two ends [500]
 -s INT        standard deviation [50]
 -N INT        number of read pairs [1000000]
 -1 INT        length of the first read [70]
 -2 INT        length of the second read [70]
 -r FLOAT      rate of mutations [0.0010]
 -R FLOAT      fraction of indels [0.15]
 -X FLOAT      probability an indel is extended [0.30]
 -S INT        seed for random generator [-1]
 -h            haplotype mode

 */
<commit_msg>Switched to pirs instead of wgsim<commit_after>//
//  generateSimulatedData.cpp
//  cPWP
//
//  Created by Evan McCartney-Melstad on 12/31/14.
//  Copyright (c) 2014 Evan McCartney-Melstad. All rights reserved.
//

#include "generateSimulatedData.h"
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>

int generateReadsAndMap (int numIndividuals, double mutationRateStepSize, std::string baseErrorRate, std::string libFragmentSize, std::string stdevLibFragmentSize, std::string depth, std::string readLengths, std::string randomSeed, std::string reference, std::string threads) {
/*// supply 1) number of individuals, 2) the mutation rate steps between them, 3) the base error rate, 4) the average library fragment size, 5) the standard deviation of the library fragment size, 6) the number of read pairs for each individual, 7) read lengths (per read), 8) random number generator seed

    So, for instance, if numIndividuals is 5, and mutationRateSteps is 0.01, then there will be four individuals generated with mutation rate steps of 0.01, 0.02, 0.03, and 0.04 away from the reference genome. Individual 0 is created as being identical to the reference genome (mutation rate parameter set to 0.00).
*/

    std::ofstream bamsFile;
    bamsFile.open("bamlist.txt", std::ios::out | std::ios::app);

    std::cout << "**********\nChecking if processor is available to run pirs...";
    if (system(NULL)) puts ("OK");
    else exit (EXIT_FAILURE);

    int pirsInd = 0;
    while(pirsInd < numIndividuals) {
        double mutRate = pirsInd * mutationRateStepSize;
        // Covert the mutation rate into a string for the system command
        std::ostringstream mutStr;
        mutStr << mutRate;
        std::string mutRateString = mutStr.str();

        std::cout << "**********\nGenerating reference genome for individual " << step << " using a mutation rate of " << mutRateString << " from the reference genome\n**********\n";

        // Since we're identifying individuals by the looping variable (an int), we need to convert that to a string to use it in the file names
        std::ostringstream pirsIndSS;
        pirsIndSS << "ind" + pirsInd;
        std::string indName = pirsIndSS.str();
        std::string pirsGenomeSTDOUT = indName + "_genome.stdout";
        std::string pirsGenomeSTDERR = indName + "_genome.stderr";

        // Simulate the other strand of the mutated reference genome. On the first individual it should be identical to the reference (because mutStr = 0 * mutationRateStepSize
        std::string pirsCommandToRun = "pirs diploid -s " + mutStr + " -d 0.00 -v 0.00 -S 1234 -o " + indName + " " + reference + " >" pirsGenomeSTDOUT + " 2>" + pirsGenomeSTDERR;
        if (system((pirsCommandToRun).c_str()) != 0) {
            std::cout << "**********\nFailure running the following command: " << pirsCommandToRun << "\n**********\n";
            exit(EXIT_FAILURE);
        } else {
            std::cout << "**********\nExecuted the following command: " << pirsCommandToRun << "\n**********\n";
        }
        // The following file is output by the pirs diploid command:
        std::string mutatedChromosome = indName + ".snp.fa";

        // After generating the mutated strand for each individual, we simulate reads from both strands for each individual. Parameterize the
        std::string pirsSimSTDOUT = indName + "_reads.stdout";
        std::string pirsSimSTDERR = indName + "_reads.stderr";
        std::string indReadsPrefix = indName + "_reads";
        std::string pirsSimulateCommandToRun = "pirs simulate --diploid " + reference + " " + mutatedChromosome + " -l " << readLengths << " -x " << depth << " -m " << libFragmentSize << " -v " << stdevLibFragmentSize << " --no-substitution-errors --no-indel-errors --no-gc-content-bias -o " + indReadsPrefix + " >" + pirsSimSTDOUT + " 2>" + pirsSimSTDERR;
        if (system((pirsSimulateCommandToRun).c_str()) != 0) {
            std::cout << "**********\nFailure running the following command: " << pirsSimulateCommandToRun << "\n**********\n";
            exit(EXIT_FAILURE);
        } else {
            std::cout << "**********\nExecuted the following command: " << pirsSimulateCommandToRun << "\n**********\n";
        }

        // Generate the bwa mem command and then run it using a system call
        std::string R1 = indReadsPrefix + "_" + readLengths + "_" + libFragmentSize + "_1.fq";
        std::string R2 = indReadsPrefix + "_" + readLengths + "_" + libFragmentSize + "_2.fq";
        std::string bamOut = "ind" + ind + ".bam";
        std::string bwaCommandToRun = "bwa mem -t " + threads + " " + reference + " " + R1 + " " + R2 + " | samtools view -bS - | samtools sort -T temp -o " + bamOut + " -";
        if (system((bwaCommandToRun).c_str()) != 0) {
            std::cout << "**********\nFailure running the following command: " << bwaCommandToRun << "\n**********\n";
            exit(EXIT_FAILURE);
        } else {
            std::cout << "**********\nExecuted the following command: " << bwaCommandToRun << "\n**********\n";
        }
        bamsFile << bamOut << std::endl;

        pirsInd++; // Move on to the next individual
    }

    /* Implementation with wgsim
    std::cout << "**********\nChecking if processor is available to run wgsim...";
    if (system(NULL)) puts ("Ok");
    else exit (EXIT_FAILURE);

    int step = 0;
    while(step <= numIndividuals) {
        double mutRate = step * mutationRateStepSize;
        // Covert the mutation rate into a string for the system command
        std::ostringstream mutStrs;
        mutStrs << mutRate;
        std::string mutRateString = mutStrs.str();

        std::cout << "**********\nGenerating sequence reads for individual " << step << " using a mutation rate of " << mutRateString << " from the reference genome\n**********\n";

        // Get the number of the individual as a string
        std::ostringstream stepString;
        stepString << step;
        std::string ind = stepString.str();

        // Generate the output file names as strings
        std::string R1out = "ind" + ind + "_R1.fastq";
        std::string R2out = "ind" + ind + "_R2.fastq";
        std::string polymorphismFile = "ind" + ind + "_polymorphisms.txt";

        // Generate the wgsim command and then run it using a system call
        std::string wgsimCommandToRun = "wgsim -N " + numReadPairs + " -r " + mutRateString + " -R 0.00 -X 0.00 -d " + libFragmentSize + " -s " + stdevLibFragmentSize +  " -1 " + readLengths + " -2 " + readLengths + " -S " + randomSeed + " -e0 " + reference + " " + R1out + " " + R2out + " > " + polymorphismFile; // No indels, no probability of indel extension, no base call error rates
        if (system((wgsimCommandToRun).c_str()) != 0) {
            std::cout << "**********\nFailure running the following command: " << wgsimCommandToRun << "\n**********\n";
            exit(EXIT_FAILURE);
        } else {
            std::cout << "**********\nExecuted the following command: " << wgsimCommandToRun << "\n**********\n";

        }

        // Generate the bwa mem command and then run it using a system call
        std::string bamOut = "ind" + ind + ".bam";
        std::string bwaCommandToRun = "bwa mem -t " + threads + " " + reference + " " + R1out + " " + R2out + " | samtools view -bS - | samtools sort -T temp -o " + bamOut + " -";
        if (system((bwaCommandToRun).c_str()) != 0) {
            std::cout << "**********\nFailure running the following command: " << bwaCommandToRun << "\n**********\n";
            exit(EXIT_FAILURE);
        } else {
            std::cout << "**********\nExecuted the following command: " << bwaCommandToRun << "\n**********\n";
        }
        bamsFile << bamOut << std::endl;
        step++; // Move on to the next individual
    }
    */
    bamsFile.close();
    return 0;
}

/*
 /home/evan/bin/angsd0.613/angsd -bam bamlist272.txt -out 272torts_allCounts_minmapq20minq30 -uniqueOnly 1 -only_proper_pairs 1 -remove_bads 1 -doCounts 1 -nThreads 8 -dumpCounts 4 -doMaf 1 -doMajorMinor 2 -GL 2 -minMapQ 20 -minQ 30 > 272tortsangsdMapQ30_allSites.log 2>&1

*/

/*
 wgsim -N 10000000 -r 0.01 -R 0.00 -X 0.00 -1 100 -2 100 -S11 -e0 lgv2.fasta r1_noerr1percDivergent_10mil.fq r2_noerr1percDivergent_10mil.fq > 1perc_polymorphisms.txt
 wgsim -N 10000000 -r 0.00 -1 100 -2 100 -S11 -e0 lgv2.fasta r1_noerrnomut_10mil.fq r2_noerrnomut_10mil.fq

 bwa mem -t 10 lgv2.fasta r1_noerrnomut_10mil.fq r2_noerrnomut_10mil.fq
 bwa mem -t 10 lgv2.fasta r1_noerr1percDivergent_10mil.fq r2_noerr1percDivergent_10mil.fq

 samtools sort -T noMut -o 1XcoverageNoErrNoMutSorted.bam 1XcoverageNoErrNoMut.bam
 samtools sort -T mut -o 1XcoverageNoErr1PercentDivergentSorted.bam 1XcoverageNoErr1PercDivergent.bam

 echo -e "1XcoverageNoErrNoMutSorted.bam\n1XcoverageNoErr1PercentDivergentSorted.bam" > bamlist2sim.txt

 /home/evan/bin/angsd0.613/angsd -bam bamlist2sim.txt -out 2tortsim1perDiv -uniqueOnly 1 -only_proper_pairs 1 -remove_bads 1 -doCounts 1 -nThreads 8 -dumpCounts 4 -doMaf 1 -doMajorMinor 2 -GL 2 -minMapQ 20 -minQ 30 > 2tortANGSD.log 2>&1 &

*/

/*
 Usage:   wgsim [options] <in.ref.fa> <out.read1.fq> <out.read2.fq>

 Options:
 -e FLOAT      base error rate [0.000]
 -d INT        outer distance between the two ends [500]
 -s INT        standard deviation [50]
 -N INT        number of read pairs [1000000]
 -1 INT        length of the first read [70]
 -2 INT        length of the second read [70]
 -r FLOAT      rate of mutations [0.0010]
 -R FLOAT      fraction of indels [0.15]
 -X FLOAT      probability an indel is extended [0.30]
 -S INT        seed for random generator [-1]
 -h            haplotype mode

 */

<|endoftext|>


<commit_before>#include <pip/context.hpp>
#include <pip/decl.hpp>
#include <pip/translator.hpp>
#include <pip/resolver.hpp>
#include <pip/evaluator.hpp>
#include <pip/type_checker.hpp>
#include <pip/pcap.hpp>
#include <pip/decode.hpp>

#include <sexpr/syntax.hpp>
#include <sexpr/context.hpp>
#include <sexpr/parser.hpp>

#include <cc/input.hpp>
#include <cc/output.hpp>
#include <cc/diagnostics.hpp>

#include <iostream>

#include <cstring>
#include <cstdint>
#include <netinet/in.h>
#include <climits>
#include <bitset>

template <typename R>
static constexpr R bitmask(unsigned int const onecount)
{
    return static_cast<R>(-(onecount != 0))
        & (static_cast<R>(-1) >> ((sizeof(R) * CHAR_BIT) - onecount));
}

template<typename R>
static constexpr R bitfieldmask(unsigned int const a, unsigned int const b)
{
  return ((static_cast<R>(-1) >> (((sizeof(R) * CHAR_BIT) - 1) - (b)))
	  & ~((1 << (a)) - 1));

}

int
main(int argc, char* argv[])
{
  /// The file use for writing errors.
  cc::output_device error = std::cerr;

  /// Manages error diagnostics.
  cc::diagnostic_manager diags;

  /// Manages in the input source.
  cc::input_manager inputs;

  /// A set of unique symbols.
  cc::symbol_table syms;

  /// The program translation context.
  pip::context cxt(diags, inputs, syms);

  if (argc < 3) {
    std::cerr << "usage: pip <pip-program> <pcap-file>\n";
    return 1;
  }

  // Parse the input file as a pip program. There are a number of
  // stages of translation:
  pip::decl* prog;
  try {
    // Stage 1. Accept the input file.
    const cc::file& input = inputs.add_file(argv[1]);

    // Stage 2: Parse the program as an uninterpreted s-expression.
    sexpr::context sexpr(diags, inputs, syms);
    sexpr::parser parse(sexpr, input);
    sexpr::expr* e = parse();
    e->dump();

    // Stage 3: Match the s-expression into a pip program.
    pip::translator trans(cxt);
    prog = trans(e);
    prog->dump();

    std::cout << "End translation\n";

    // Stage 4: Name lookup. Match identifiers to declarations.
    pip::resolver resolve(cxt);
    resolve(prog);

    // Stage 5: Type checking. Assign a type to each expression.
    pip::type_checker types(cxt, static_cast<pip::program_decl*>(prog));
    types.check();

    // Stage K: Other static analysis?

    // ...

    // Stage K + N: Evaluate the program.
    //
    // TODO: Build a packet stream evaluator that encapsulates this
    // functionality.
    int partial = 0;
    pip::cap::file in(argv[2]);
    pip::cap::packet pkt;
    while (in.get(pkt)) {

      std::cout << "PKT:\n";
      uint8_t* vec = new uint8_t[pkt.size()];
      for(int i = 0; i < pkt.size(); ++i)
      {
      	uint8_t n;
      	std::memcpy(&n, pkt.data() + i, 1);
	vec[i] = n;

      	std::cout << std::hex << (unsigned)vec[i] << '\t';

      	if(i > 1 && i % 8 == 0)
      	  std::cout << '\n';
      }
      // pip::cap::eth_header eth(pkt);
      // const pip::cap::eth_header* eth;
      // eth = (pip::cap::eth_header*)(pkt.data());
      pip::cap::eth_header eth(pkt);
      // eth.print();
      std::cout << "\nETH:\n";
      eth.print(std::cout);
      std::cout << "\nIP:\n";
      pip::cap::ipv4_header ipv4(eth);
      ipv4.print(std::cout);

      std::cout << "\nTCP:\n";
      pip::cap::tcp_header tcp(ipv4);
      tcp.print(std::cout);

      // int bitwidth = 7;
      // uint64_t field = 0;
      // uint64_t value = 94;
      // int field_width = 16;
      // int enjamb = (field_width - (field_width % 8)) / 8;
      // uint64_t mask = bitmask<uint32_t>(bitwidth);

      // // We can enjamb a value into the beginning of an integer
      // // by creating a mask and ORing
      // if(bitwidth < sizeof(value) * 8)
      // {
      // 	mask << ((sizeof(value) * 8) - bitwidth);
      // 	field = (field & ~mask) | (value << (sizeof(value) * 8) - bitwidth);
      // 	uint8_t* exploded_field = new uint8_t[8];

      // 	std::memcpy(exploded_field, &field, 8);

      // 	std::bitset<64> x(field);
      // 	std::cout << "Enjambed number: " << x << '\n';

      // 	std::cout << "Enjambed array: ";
      // 	for(int i = 0; i < enjamb; ++i)
      // 	{
      // 	  std::cout << exploded_field[i] << " ";
      // 	}
      // 	std::cout << '\n';
      // }

      // std::cout << "\nNEW BUF\n";
      // for(int i = 0; i < pkt.size(); ++i)
      // {
      // 	std::cout << std::hex << (unsigned)vec[i] << '\t';

      // 	if(i > 1 && i % 8 == 0)
      // 	  std::cout << '\n';
      // }
      delete[] vec;

      // for(int i = 0; i < 6; ++i)
      // 	std::cout << std::hex << (unsigned)eth->dst_mac[i] << '\t';

      // std::cout << '\n';
      // for(int i = 0; i < 6; ++i)
      // 	std::cout << std::hex << (unsigned)eth->src_mac[i] << '\t';

      // std::cout << '\n';
      //   std::cout << std::hex << (unsigned)eth->ethertype << '\t';

      // for(int i = 0; i < sizeof(eth->payload); ++i) {
      // 	if(i > 1 && i % 8 == 0)
      // 	  std::cout << '\n';
      // 	std::cout << std::hex << (unsigned)eth->payload[i] << '\t';
      // }

      // Ignore incomplete packets.
      if(!pkt.is_complete()) {
        ++partial;
        continue;
      }

      pip::evaluator eval(cxt, nullptr, pkt);

      // TODO: This is where we could turn this into a debugger. Simply
      // allowing the user to invoke the step command would enable them
      // to step through the execution of the packet in the pipeline.
      // eval.run();
    }

    std::cout << "partial packets: " << partial << '\n';
  }
  catch (cc::diagnosable_error& err) {
    diags.emit(err);
  }

  // Emit any errors.
  if (diags.error_count())
    print(diags.get_diagnostics(), inputs, error);
}
<commit_msg>Command line argument for number of physical ports.<commit_after>#include <pip/context.hpp>
#include <pip/decl.hpp>
#include <pip/translator.hpp>
#include <pip/resolver.hpp>
#include <pip/evaluator.hpp>
#include <pip/type_checker.hpp>
#include <pip/pcap.hpp>
#include <pip/decode.hpp>
#include <pip/codegen.hpp>

#include <sexpr/syntax.hpp>
#include <sexpr/context.hpp>
#include <sexpr/parser.hpp>

#include <cc/input.hpp>
#include <cc/output.hpp>
#include <cc/diagnostics.hpp>

#include <iostream>
#include <fstream>

#include <cstring>
#include <cstdint>
#include <netinet/in.h>
#include <climits>
#include <algorithm>

int
main(int argc, char* argv[])
{
  /// The file use for writing errors.
  cc::output_device error = std::cerr;

  /// Manages error diagnostics.
  cc::diagnostic_manager diags;

  /// Manages in the input source.
  cc::input_manager inputs;

  /// A set of unique symbols.
  cc::symbol_table syms;

  /// The program translation context.
  pip::context cxt(diags, inputs, syms);

  if (argc < 3) {
    std::cerr << "usage: pip <pip-program> <pcap-file>\n";
    return 1;
  }

  // Parse the input file as a pip program. There are a number of
  // stages of translation:
  pip::decl* prog;
  try {
    // Stage 1. Accept the input file.
    const cc::file& input = inputs.add_file(argv[1]);

    // If the amount of physical is not defined, it is the maximum 32-bit uinteger.
    std::uint32_t physical_ports = (std::uint32_t)(~0);
    std::vector<std::string> arguments;
    arguments.assign(argv, argv + argc);

    // Otherwise, restrict the set if it is in range.
    auto port_arg_it = std::find(arguments.begin(), arguments.end(), "-p");
    if(port_arg_it == arguments.end())
      port_arg_it = std::find(arguments.begin(), arguments.end(), "--ports");

    if(port_arg_it != arguments.end()) {
      std::string amount_string = *(port_arg_it + 1);
      std::size_t size;
      int amount = std::stoi(amount_string, &size);
      if(amount_string.size() != size)
	throw std::runtime_error("Invalid amount of physical ports.");
      if(amount < 1 || amount > physical_ports)
	throw std::runtime_error("Invalid amount of physical ports.");
      physical_ports = amount;
    }

    // Stage 2: Parse the program as an uninterpreted s-expression.
    sexpr::context sexpr(diags, inputs, syms);
    sexpr::parser parse(sexpr, input);
    sexpr::expr* e = parse();
    e->dump();

    // Stage 3: Match the s-expression into a pip program.
    pip::translator trans(cxt);
    prog = trans(e);
    prog->dump();
    auto program = static_cast<pip::program_decl*>(prog);

    std::cout << "End translation\n";
    std::ofstream outfile("out.pip");
    pip::generator gen(dynamic_cast<pip::program_decl*>(prog));
    outfile << gen.generate();
    outfile.close();

    // Stage 4: Name lookup. Match identifiers to declarations.
    pip::resolver resolve(cxt);
    resolve(prog);

    // // Stage 5: Type checking. Assign a type to each expression.
    // pip::type_checker types(cxt, program);
    // types.check();

    // Stage K: Other static analysis?

    // ...

    // Stage K + N: Evaluate the program.
    //
    // TODO: Build a packet stream evaluator that encapsulates this
    // functionality.
    int partial = 0;
    pip::cap::file in(argv[2]);
    pip::cap::packet pkt;
    while (in.get(pkt)) {

      for(auto d : program->decls) {
	if(auto t = dynamic_cast<pip::table_decl*>(d)) {
	  for(auto r : t->rules)
	    if(auto i = dynamic_cast<pip::int_expr*>(r->key))
	      std::cout << "rule key: " << i->val << '\n';
	}
      }

      pip::evaluator eval(cxt, program, pkt);
      eval.run();

    }
      // TODO: This is where we could turn this into a debugger. Simply
      // allowing the user to invoke the step command would enable them
      // to step through the execution of the packet in the pipeline.
      // eval.run();

    std::cout << "partial packets: " << partial << '\n';
  }
  catch (cc::diagnosable_error& err) {
    diags.emit(err);
  }

  // Emit any errors.
  if (diags.error_count())
    print(diags.get_diagnostics(), inputs, error);
}
<|endoftext|>


<commit_before>/*===================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

===================================================================*/

#ifndef __mitk_ImageToDiffusionImageSource_txx
#define __mitk_ImageToDiffusionImageSource_txx

#include "mitkImageToDiffusionImageSource.h"

#include <itkComposeImageFilter.h>
#include <itkExtractImageFilter.h>

#include "mitkImageTimeSelector.h"

#include <itkVectorImage.h>
#include <itkImage.h>

#include "mitkImageCast.h"

#include "mitkException.h"

template< typename TPixelType>
mitk::ImageToDiffusionImageSource<TPixelType>::ImageToDiffusionImageSource()
  : m_SourceImage(0),
    m_GradientDirections(0)
{

}

template< typename TPixelType>
void mitk::ImageToDiffusionImageSource<TPixelType>
::GenerateOutputInformation()
{
  // sanity checks ( input not null, timesteps corresponds to number of directions specified )
  if( m_GradientDirections->empty() )
  {
    mitkThrow() << "No gradient directions were set. Cannot proceed.";
  }

  if( m_SourceImage.IsNull() )
  {
    mitkThrow() << "No input image was set. Cannot proceed.";
  }

  if( m_GradientDirections->size() != m_SourceImage->GetTimeSteps() )
  {
      mitkThrow() << "Size mismatch between container size " << m_GradientDirections->size() <<
                     "and image volumes."<< m_SourceImage->GetTimeSteps() <<" Cannot proceed.";
  }

  // already pass in the meta-data
  typename OutputType::Pointer metaImage = OutputType::New();

  // set directions and bvalue
  metaImage->SetDirections(this->m_GradientDirections);
  metaImage->SetB_Value(this->m_BValue);

  // set identity as measurement frame
  vnl_matrix_fixed< double, 3, 3 > measurement_frame;
  measurement_frame.set_identity();
  metaImage->SetMeasurementFrame( measurement_frame );

  m_OutputCache = metaImage;
  m_CacheTime.Modified();

}

template< typename TPixelType>
void mitk::ImageToDiffusionImageSource<TPixelType>
::GenerateData()
{
  if ( ( ! m_OutputCache ) || ( this->GetMTime( ) > m_CacheTime.GetMTime( ) ) )
  {
    this->GenerateOutputInformation();
    itkWarningMacro("Cache regenerated!");
  }

  // now cast the mitk image to the vector image and pass in as volume
  typedef itk::Image< TPixelType, 4 > InputItkType;
  typedef itk::Image< TPixelType, 3> SingleVolumeType;
  typedef itk::VectorImage< TPixelType, 3> VectorImageType;

  typedef itk::ComposeImageFilter< SingleVolumeType > ComposeFilterType;
  typename ComposeFilterType::Pointer vec_composer = ComposeFilterType::New();

  mitk::ImageTimeSelector::Pointer t_selector =
      mitk::ImageTimeSelector::New();

  t_selector->SetInput( m_SourceImage );

  for( unsigned int i=0; i< m_SourceImage->GetTimeSteps(); i++)
  {
    t_selector->SetTimeNr(i);
    t_selector->Update();

    typename SingleVolumeType::Pointer singleImageItk;
    mitk::CastToItkImage( t_selector->GetOutput(), singleImageItk );

    vec_composer->SetInput( i, singleImageItk );

  }

  try
  {
    vec_composer->Update();
  }
  catch( const itk::ExceptionObject& e)
  {
    MITK_ERROR << "Caugt exception while updating compose filter: " << e.what();
  }

  m_OutputCache->SetVectorImage( vec_composer->GetOutput() );

  // transfer to the output image
  static_cast<OutputType*>(this->GetOutput())
      ->SetVectorImage(m_OutputCache->GetVectorImage());
  static_cast<OutputType*>(this->GetOutput())
      ->SetB_Value(m_OutputCache->GetB_Value());
  static_cast<OutputType*>(this->GetOutput())
      ->SetDirections(m_OutputCache->GetDirections());
  static_cast<OutputType*>(this->GetOutput())
      ->SetMeasurementFrame(m_OutputCache->GetMeasurementFrame());
  static_cast<OutputType*>(this->GetOutput())
      ->InitializeFromVectorImage();

}

#endif //__mitk_ImageToDiffusionImageSource_txx
<commit_msg>Fixed sanity checks<commit_after>/*===================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

===================================================================*/

#ifndef __mitk_ImageToDiffusionImageSource_txx
#define __mitk_ImageToDiffusionImageSource_txx

#include "mitkImageToDiffusionImageSource.h"

#include <itkComposeImageFilter.h>
#include <itkExtractImageFilter.h>

#include "mitkImageTimeSelector.h"

#include <itkVectorImage.h>
#include <itkImage.h>

#include "mitkImageCast.h"

#include "mitkException.h"

template< typename TPixelType>
mitk::ImageToDiffusionImageSource<TPixelType>::ImageToDiffusionImageSource()
  : m_SourceImage(0),
    m_GradientDirections(0)
{

}

template< typename TPixelType>
void mitk::ImageToDiffusionImageSource<TPixelType>
::GenerateOutputInformation()
{
  // sanity checks ( input not null, timesteps corresponds to number of directions specified )
  if( m_GradientDirections.IsNull() || m_GradientDirections->empty() )
  {
    mitkThrow() << "No gradient directions were set. Cannot proceed.";
  }

  if( m_SourceImage.IsNull() )
  {
    mitkThrow() << "No input image was set. Cannot proceed.";
  }

  if( m_GradientDirections->size() != m_SourceImage->GetTimeSteps() )
  {
      mitkThrow() << "Size mismatch between container size " << m_GradientDirections->size() <<
                     "and image volumes."<< m_SourceImage->GetTimeSteps() <<" Cannot proceed.";
  }

  // already pass in the meta-data
  typename OutputType::Pointer metaImage = OutputType::New();

  // set directions and bvalue
  metaImage->SetDirections(this->m_GradientDirections);
  metaImage->SetB_Value(this->m_BValue);

  // set identity as measurement frame
  vnl_matrix_fixed< double, 3, 3 > measurement_frame;
  measurement_frame.set_identity();
  metaImage->SetMeasurementFrame( measurement_frame );

  m_OutputCache = metaImage;
  m_CacheTime.Modified();

}

template< typename TPixelType>
void mitk::ImageToDiffusionImageSource<TPixelType>
::GenerateData()
{
  if ( ( ! m_OutputCache ) || ( this->GetMTime( ) > m_CacheTime.GetMTime( ) ) )
  {
    this->GenerateOutputInformation();
    itkWarningMacro("Cache regenerated!");
  }

  // now cast the mitk image to the vector image and pass in as volume
  typedef itk::Image< TPixelType, 4 > InputItkType;
  typedef itk::Image< TPixelType, 3> SingleVolumeType;
  typedef itk::VectorImage< TPixelType, 3> VectorImageType;

  typedef itk::ComposeImageFilter< SingleVolumeType > ComposeFilterType;
  typename ComposeFilterType::Pointer vec_composer = ComposeFilterType::New();

  mitk::ImageTimeSelector::Pointer t_selector =
      mitk::ImageTimeSelector::New();

  t_selector->SetInput( m_SourceImage );

  for( unsigned int i=0; i< m_SourceImage->GetTimeSteps(); i++)
  {
    t_selector->SetTimeNr(i);
    t_selector->Update();

    typename SingleVolumeType::Pointer singleImageItk;
    mitk::CastToItkImage( t_selector->GetOutput(), singleImageItk );

    vec_composer->SetInput( i, singleImageItk );

  }

  try
  {
    vec_composer->Update();
  }
  catch( const itk::ExceptionObject& e)
  {
    MITK_ERROR << "Caugt exception while updating compose filter: " << e.what();
  }

  m_OutputCache->SetVectorImage( vec_composer->GetOutput() );

  // transfer to the output image
  static_cast<OutputType*>(this->GetOutput())
      ->SetVectorImage(m_OutputCache->GetVectorImage());
  static_cast<OutputType*>(this->GetOutput())
      ->SetB_Value(m_OutputCache->GetB_Value());
  static_cast<OutputType*>(this->GetOutput())
      ->SetDirections(m_OutputCache->GetDirections());
  static_cast<OutputType*>(this->GetOutput())
      ->SetMeasurementFrame(m_OutputCache->GetMeasurementFrame());
  static_cast<OutputType*>(this->GetOutput())
      ->InitializeFromVectorImage();

}

#endif //__mitk_ImageToDiffusionImageSource_txx
<|endoftext|>


<commit_before>#include <sstream>
#include <stdexcept>

#include "File.h"
#include "KeyValueFile.h"
#include "String.h"
#include "StringView.h"
#include "../debug/Debug.h"

void KeyValueFile::Section::init(std::string &&name)
{
	this->name = std::move(name);
}

const std::string &KeyValueFile::Section::getName() const
{
	return this->name;
}

int KeyValueFile::Section::getPairCount() const
{
	return static_cast<int>(this->pairs.size());
}

const std::pair<std::string, std::string> &KeyValueFile::Section::getPair(int index) const
{
	DebugAssertIndex(this->pairs, index);
	return this->pairs[index];
}

bool KeyValueFile::Section::tryGetValue(const std::string &key, std::string_view &value) const
{
	const std::pair<std::string, std::string> searchPair(key, std::string());
	const auto iter = std::lower_bound(this->pairs.begin(), this->pairs.end(), searchPair,
		[](const auto &pairA, const auto &pairB)
	{
		return pairA.first < pairB.first;
	});

	if (iter != this->pairs.end())
	{
		value = iter->second;
		return true;
	}
	else
	{
		return false;
	}
}

bool KeyValueFile::Section::tryGetBoolean(const std::string &key, bool &value) const
{
	std::string_view str;
	if (!this->tryGetValue(key, str))
	{
		return false;
	}
	else
	{
		// Convert to lowercase for easier comparison.
		const std::string lowerStr = String::toLowercase(std::string(str));

		if (lowerStr == "true")
		{
			value = true;
			return true;
		}
		else if (lowerStr == "false")
		{
			value = false;
			return true;
		}
		else
		{
			return false;
		}
	}
}

bool KeyValueFile::Section::tryGetInteger(const std::string &key, int &value) const
{
	std::string_view str;
	if (!this->tryGetValue(key, str))
	{
		return false;
	}
	else
	{
		try
		{
			size_t index = 0;
			value = std::stoi(std::string(str), &index);
			return index == str.size();
		}
		catch (std::exception)
		{
			return false;
		}
	}
}

bool KeyValueFile::Section::tryGetDouble(const std::string &key, double &value) const
{
	std::string_view str;
	if (!this->tryGetValue(key, str))
	{
		return false;
	}
	else
	{
		try
		{
			size_t index = 0;
			value = std::stod(std::string(str), &index);
			return index == str.size();
		}
		catch (std::exception)
		{
			return false;
		}
	}
}

bool KeyValueFile::Section::tryGetString(const std::string &key, std::string_view &value) const
{
	return this->tryGetValue(key, value);
}

void KeyValueFile::Section::add(std::string &&key, std::string &&value)
{
	this->pairs.push_back(std::make_pair(std::move(key), std::move(value)));
	std::sort(this->pairs.begin(), this->pairs.end(),
		[](const auto &pairA, const auto &pairB)
	{
		const std::string &strA = pairA.first;
		const std::string &strB = pairB.first;
		return strA < strB;
	});
}

void KeyValueFile::Section::clear()
{
	this->pairs.clear();
}

const char KeyValueFile::COMMENT = '#';
const char KeyValueFile::PAIR_SEPARATOR = '=';
const char KeyValueFile::SECTION_FRONT = '[';
const char KeyValueFile::SECTION_BACK = ']';

bool KeyValueFile::init(const char *filename)
{
	const std::string text = File::readAllText(filename);
	std::istringstream iss(text);

	// Check each line for a valid section or key-value pair. Start the line numbers at 1
	// since most users aren't programmers.
	std::string line;
	Section *activeSection = nullptr;
	for (int lineNumber = 1; std::getline(iss, line); lineNumber++)
	{
		// Get a filtered version of the current line so it can be parsed. If the filtered
		// string is empty, then skip to the next line.
		const std::string_view filteredLine = [&line]()
		{
			std::string_view str = line;

			// Skip empty strings.
			if ((str.size() == 0) || ((str.size() == 1) && (str.front() == '\r')))
			{
				return std::string_view();
			}

			// Remove carriage return at the end (if any).
			if (str.back() == '\r')
			{
				str.remove_suffix(1);
			}

			// Extract left-most comment (if any).
			const size_t commentIndex = str.find(KeyValueFile::COMMENT);
			if (commentIndex == 0)
			{
				// Comment covers the entire line.
				return std::string_view();
			}
			else if (commentIndex != std::string_view::npos)
			{
				// Comment is somewhere in the line, so only work with the left substring.
				str = str.substr(0, commentIndex);
			}

			// Trim leading and trailing whitespace (i.e., in case the key has whitespace
			// before it, or a comment had whitespace before it).
			return StringView::trimFront(StringView::trimBack(str));
		}();

		if (filteredLine.empty())
		{
			// It became an empty string after whitespace was removed.
			continue;
		}
		else if (filteredLine.size() < 3)
		{
			// Not long enough to be a section or key-value pair.
			DebugLogError("Syntax error \"" + std::string(filteredLine) + "\" (line " +
				std::to_string(lineNumber) + ") in " + filename + ".");
			return false;
		}

		// See if it's a section line or key-value pair line.
		const size_t sectionFrontIndex = filteredLine.find(KeyValueFile::SECTION_FRONT);
		if (sectionFrontIndex != std::string_view::npos)
		{
			// Section line. There must be a closing character with enough space between it
			// and the front character for at least one section character.
			const size_t sectionBackIndex = filteredLine.find(
				KeyValueFile::SECTION_BACK, sectionFrontIndex);

			if ((sectionBackIndex != std::string_view::npos) &&
				(sectionBackIndex > (sectionFrontIndex + 1)))
			{
				// Get the string that's between the section characters and trim any
				// leading or trailing whitespace.
				std::string_view sectionName = filteredLine.substr(
					sectionFrontIndex + 1, sectionBackIndex - sectionFrontIndex - 1);
				sectionName = StringView::trimFront(StringView::trimBack(sectionName));

				const auto sectionIter = std::find_if(this->sections.begin(), this->sections.end(),
					[&sectionName](const Section &section)
				{
					return section.getName() == sectionName;
				});

				// If the section is new, add it to the section maps.
				if (sectionIter == this->sections.end())
				{
					Section section;
					section.init(std::string(sectionName));
					this->sections.push_back(std::move(section));
					activeSection = &this->sections.back();
				}
				else
				{
					DebugLogError("Section \"" + std::string(sectionName) + "\" (line " +
						std::to_string(lineNumber) + ") already defined in " + filename + ".");
					return false;
				}
			}
			else
			{
				DebugLogError("Invalid section \"" + std::string(filteredLine) + "\" (line " +
					std::to_string(lineNumber) + ") in " + filename + ".");
				return false;
			}
		}
		else if (filteredLine.find(KeyValueFile::PAIR_SEPARATOR) != std::string::npos)
		{
			// Key-value pair line. There must be two tokens: key and value.
			std::array<std::string_view, 2> tokens;
			if (!StringView::splitExpected(filteredLine, KeyValueFile::PAIR_SEPARATOR, tokens))
			{
				DebugLogError("Invalid pair \"" + std::string(filteredLine) + "\" (line " +
					std::to_string(lineNumber) + ") in " + filename + ".");
				return false;
			}

			// Trim trailing whitespace from the key and leading whitespace from the value.
			const std::string_view &key = StringView::trimBack(tokens[0]);
			const std::string_view &value = StringView::trimFront(tokens[1]);

			if (key.size() == 0)
			{
				DebugLogError("Empty key in \"" + std::string(filteredLine) + "\" (line " +
					std::to_string(lineNumber) + ") in " + filename + ".");
				return false;
			}

			// Add the key-value pair to the active section map.
			if (activeSection != nullptr)
			{
				activeSection->add(std::string(key), std::string(value));
			}
			else
			{
				// If no active section map, print a warning and ignore the current pair.
				// All key-value pairs must be in a section.
				DebugLogWarning("Ignoring \"" + std::string(filteredLine) + "\" (line " +
					std::to_string(lineNumber) + "), no active section in " + filename);
			}
		}
		else
		{
			// Filtered line is not a section or key-value pair.
			DebugLogError("Invalid line \"" + line + "\" (line " +
				std::to_string(lineNumber) + ") in " + filename + ".");
			return false;
		}
	}

	// Sort for binary search.
	std::sort(this->sections.begin(), this->sections.end(),
		[](const Section &sectionA, const Section &sectionB)
	{
		return sectionA.getName() < sectionB.getName();
	});

	return true;
}

int KeyValueFile::getSectionCount() const
{
	return static_cast<int>(this->sections.size());
}

const KeyValueFile::Section &KeyValueFile::getSection(int index) const
{
	DebugAssertIndex(this->sections, index);
	return this->sections[index];
}

const KeyValueFile::Section *KeyValueFile::getSectionByName(const std::string &name) const
{
	const auto iter = std::lower_bound(this->sections.begin(), this->sections.end(), name,
		[](const Section &section, const std::string &str)
	{
		return section.getName() < str;
	});

	return (iter != this->sections.end()) ? &(*iter) : nullptr;
}
<commit_msg>Fixed GCC compile error.<commit_after>#include <algorithm>
#include <sstream>
#include <stdexcept>

#include "File.h"
#include "KeyValueFile.h"
#include "String.h"
#include "StringView.h"
#include "../debug/Debug.h"

void KeyValueFile::Section::init(std::string &&name)
{
	this->name = std::move(name);
}

const std::string &KeyValueFile::Section::getName() const
{
	return this->name;
}

int KeyValueFile::Section::getPairCount() const
{
	return static_cast<int>(this->pairs.size());
}

const std::pair<std::string, std::string> &KeyValueFile::Section::getPair(int index) const
{
	DebugAssertIndex(this->pairs, index);
	return this->pairs[index];
}

bool KeyValueFile::Section::tryGetValue(const std::string &key, std::string_view &value) const
{
	const std::pair<std::string, std::string> searchPair(key, std::string());
	const auto iter = std::lower_bound(this->pairs.begin(), this->pairs.end(), searchPair,
		[](const auto &pairA, const auto &pairB)
	{
		return pairA.first < pairB.first;
	});

	if (iter != this->pairs.end())
	{
		value = iter->second;
		return true;
	}
	else
	{
		return false;
	}
}

bool KeyValueFile::Section::tryGetBoolean(const std::string &key, bool &value) const
{
	std::string_view str;
	if (!this->tryGetValue(key, str))
	{
		return false;
	}
	else
	{
		// Convert to lowercase for easier comparison.
		const std::string lowerStr = String::toLowercase(std::string(str));

		if (lowerStr == "true")
		{
			value = true;
			return true;
		}
		else if (lowerStr == "false")
		{
			value = false;
			return true;
		}
		else
		{
			return false;
		}
	}
}

bool KeyValueFile::Section::tryGetInteger(const std::string &key, int &value) const
{
	std::string_view str;
	if (!this->tryGetValue(key, str))
	{
		return false;
	}
	else
	{
		try
		{
			size_t index = 0;
			value = std::stoi(std::string(str), &index);
			return index == str.size();
		}
		catch (std::exception)
		{
			return false;
		}
	}
}

bool KeyValueFile::Section::tryGetDouble(const std::string &key, double &value) const
{
	std::string_view str;
	if (!this->tryGetValue(key, str))
	{
		return false;
	}
	else
	{
		try
		{
			size_t index = 0;
			value = std::stod(std::string(str), &index);
			return index == str.size();
		}
		catch (std::exception)
		{
			return false;
		}
	}
}

bool KeyValueFile::Section::tryGetString(const std::string &key, std::string_view &value) const
{
	return this->tryGetValue(key, value);
}

void KeyValueFile::Section::add(std::string &&key, std::string &&value)
{
	this->pairs.push_back(std::make_pair(std::move(key), std::move(value)));
	std::sort(this->pairs.begin(), this->pairs.end(),
		[](const auto &pairA, const auto &pairB)
	{
		const std::string &strA = pairA.first;
		const std::string &strB = pairB.first;
		return strA < strB;
	});
}

void KeyValueFile::Section::clear()
{
	this->pairs.clear();
}

const char KeyValueFile::COMMENT = '#';
const char KeyValueFile::PAIR_SEPARATOR = '=';
const char KeyValueFile::SECTION_FRONT = '[';
const char KeyValueFile::SECTION_BACK = ']';

bool KeyValueFile::init(const char *filename)
{
	const std::string text = File::readAllText(filename);
	std::istringstream iss(text);

	// Check each line for a valid section or key-value pair. Start the line numbers at 1
	// since most users aren't programmers.
	std::string line;
	Section *activeSection = nullptr;
	for (int lineNumber = 1; std::getline(iss, line); lineNumber++)
	{
		// Get a filtered version of the current line so it can be parsed. If the filtered
		// string is empty, then skip to the next line.
		const std::string_view filteredLine = [&line]()
		{
			std::string_view str = line;

			// Skip empty strings.
			if ((str.size() == 0) || ((str.size() == 1) && (str.front() == '\r')))
			{
				return std::string_view();
			}

			// Remove carriage return at the end (if any).
			if (str.back() == '\r')
			{
				str.remove_suffix(1);
			}

			// Extract left-most comment (if any).
			const size_t commentIndex = str.find(KeyValueFile::COMMENT);
			if (commentIndex == 0)
			{
				// Comment covers the entire line.
				return std::string_view();
			}
			else if (commentIndex != std::string_view::npos)
			{
				// Comment is somewhere in the line, so only work with the left substring.
				str = str.substr(0, commentIndex);
			}

			// Trim leading and trailing whitespace (i.e., in case the key has whitespace
			// before it, or a comment had whitespace before it).
			return StringView::trimFront(StringView::trimBack(str));
		}();

		if (filteredLine.empty())
		{
			// It became an empty string after whitespace was removed.
			continue;
		}
		else if (filteredLine.size() < 3)
		{
			// Not long enough to be a section or key-value pair.
			DebugLogError("Syntax error \"" + std::string(filteredLine) + "\" (line " +
				std::to_string(lineNumber) + ") in " + filename + ".");
			return false;
		}

		// See if it's a section line or key-value pair line.
		const size_t sectionFrontIndex = filteredLine.find(KeyValueFile::SECTION_FRONT);
		if (sectionFrontIndex != std::string_view::npos)
		{
			// Section line. There must be a closing character with enough space between it
			// and the front character for at least one section character.
			const size_t sectionBackIndex = filteredLine.find(
				KeyValueFile::SECTION_BACK, sectionFrontIndex);

			if ((sectionBackIndex != std::string_view::npos) &&
				(sectionBackIndex > (sectionFrontIndex + 1)))
			{
				// Get the string that's between the section characters and trim any
				// leading or trailing whitespace.
				std::string_view sectionName = filteredLine.substr(
					sectionFrontIndex + 1, sectionBackIndex - sectionFrontIndex - 1);
				sectionName = StringView::trimFront(StringView::trimBack(sectionName));

				const auto sectionIter = std::find_if(this->sections.begin(), this->sections.end(),
					[&sectionName](const Section &section)
				{
					return section.getName() == sectionName;
				});

				// If the section is new, add it to the section maps.
				if (sectionIter == this->sections.end())
				{
					Section section;
					section.init(std::string(sectionName));
					this->sections.push_back(std::move(section));
					activeSection = &this->sections.back();
				}
				else
				{
					DebugLogError("Section \"" + std::string(sectionName) + "\" (line " +
						std::to_string(lineNumber) + ") already defined in " + filename + ".");
					return false;
				}
			}
			else
			{
				DebugLogError("Invalid section \"" + std::string(filteredLine) + "\" (line " +
					std::to_string(lineNumber) + ") in " + filename + ".");
				return false;
			}
		}
		else if (filteredLine.find(KeyValueFile::PAIR_SEPARATOR) != std::string::npos)
		{
			// Key-value pair line. There must be two tokens: key and value.
			std::array<std::string_view, 2> tokens;
			if (!StringView::splitExpected(filteredLine, KeyValueFile::PAIR_SEPARATOR, tokens))
			{
				DebugLogError("Invalid pair \"" + std::string(filteredLine) + "\" (line " +
					std::to_string(lineNumber) + ") in " + filename + ".");
				return false;
			}

			// Trim trailing whitespace from the key and leading whitespace from the value.
			const std::string_view &key = StringView::trimBack(tokens[0]);
			const std::string_view &value = StringView::trimFront(tokens[1]);

			if (key.size() == 0)
			{
				DebugLogError("Empty key in \"" + std::string(filteredLine) + "\" (line " +
					std::to_string(lineNumber) + ") in " + filename + ".");
				return false;
			}

			// Add the key-value pair to the active section map.
			if (activeSection != nullptr)
			{
				activeSection->add(std::string(key), std::string(value));
			}
			else
			{
				// If no active section map, print a warning and ignore the current pair.
				// All key-value pairs must be in a section.
				DebugLogWarning("Ignoring \"" + std::string(filteredLine) + "\" (line " +
					std::to_string(lineNumber) + "), no active section in " + filename);
			}
		}
		else
		{
			// Filtered line is not a section or key-value pair.
			DebugLogError("Invalid line \"" + line + "\" (line " +
				std::to_string(lineNumber) + ") in " + filename + ".");
			return false;
		}
	}

	// Sort for binary search.
	std::sort(this->sections.begin(), this->sections.end(),
		[](const Section &sectionA, const Section &sectionB)
	{
		return sectionA.getName() < sectionB.getName();
	});

	return true;
}

int KeyValueFile::getSectionCount() const
{
	return static_cast<int>(this->sections.size());
}

const KeyValueFile::Section &KeyValueFile::getSection(int index) const
{
	DebugAssertIndex(this->sections, index);
	return this->sections[index];
}

const KeyValueFile::Section *KeyValueFile::getSectionByName(const std::string &name) const
{
	const auto iter = std::lower_bound(this->sections.begin(), this->sections.end(), name,
		[](const Section &section, const std::string &str)
	{
		return section.getName() < str;
	});

	return (iter != this->sections.end()) ? &(*iter) : nullptr;
}
<|endoftext|>


<commit_before>/*
 *  nextpnr -- Next Generation Place and Route
 *
 *  Copyright (C) 2018  Miodrag Milanovic <miodrag@symbioticeda.com>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include "yosystab.h"
#include <QGridLayout>
#include <QMessageBox>

NEXTPNR_NAMESPACE_BEGIN

YosysTab::YosysTab(QString folder, QWidget *parent) : QWidget(parent)
{
    QFont f("unexistent");
    f.setStyleHint(QFont::Monospace);

    console = new QPlainTextEdit();
    console->setMinimumHeight(100);
    console->setReadOnly(true);
    console->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
    console->setFont(f);

    console->setContextMenuPolicy(Qt::CustomContextMenu);
    QAction *clearAction = new QAction("Clear &buffer", this);
    clearAction->setStatusTip("Clears display buffer");
    connect(clearAction, SIGNAL(triggered()), this, SLOT(clearBuffer()));
    contextMenu = console->createStandardContextMenu();
    contextMenu->addSeparator();
    contextMenu->addAction(clearAction);
    connect(console, SIGNAL(customContextMenuRequested(const QPoint)), this, SLOT(showContextMenu(const QPoint)));

    lineEdit = new YosysLineEditor();
    lineEdit->setMinimumHeight(30);
    lineEdit->setMaximumHeight(30);
    lineEdit->setFont(f);
    lineEdit->setFocus();
    lineEdit->setEnabled(false);
    lineEdit->setPlaceholderText("yosys>");
    connect(lineEdit, SIGNAL(textLineInserted(QString)), this, SLOT(editLineReturnPressed(QString)));

    QGridLayout *mainLayout = new QGridLayout();
    mainLayout->addWidget(console, 0, 0);
    mainLayout->addWidget(lineEdit, 1, 0);
    setLayout(mainLayout);

    process = new QProcess();
    connect(process, SIGNAL(readyReadStandardError()), this, SLOT(onReadyReadStandardError()));
    connect(process, SIGNAL(readyReadStandardOutput()), this, SLOT(onReadyReadStandardOutput()));
    connect(process, &QProcess::started, this, [this] { lineEdit->setEnabled(true); });
/*
#if QT_VERSION < QT_VERSION_CHECK(5, 6, 0)
    connect(process, &QProcess::error, this, [this](QProcess::ProcessError error) {
#else
    connect(process, &QProcess::errorOccurred, this, [this](QProcess::ProcessError error) {
#endif
        if (error == QProcess::FailedToStart) {
            QMessageBox::critical(
                    this, QString::fromUtf8("Yosys cannot be started!"),
                    QString::fromUtf8("
Please make sure you have Yosys installed and available in path
"));
            Q_EMIT deleteLater();
        }
    });
*/
    process->setWorkingDirectory(folder);
    process->start("yosys");
}

YosysTab::~YosysTab()
{
    process->terminate();
    process->waitForFinished(1000); // in ms
    process->kill();
    process->close();
}

void YosysTab::displayString(QString text)
{
    QTextCursor cursor = console->textCursor();
    cursor.movePosition(QTextCursor::End);
    cursor.insertText(text);
    cursor.movePosition(QTextCursor::End);
    console->setTextCursor(cursor);
}

void YosysTab::onReadyReadStandardOutput() { displayString(process->readAllStandardOutput()); }
void YosysTab::onReadyReadStandardError() { displayString(process->readAllStandardError()); }

void YosysTab::editLineReturnPressed(QString text) { process->write(text.toLatin1() + "\n"); }

void YosysTab::showContextMenu(const QPoint &pt) { contextMenu->exec(mapToGlobal(pt)); }

void YosysTab::clearBuffer() { console->clear(); }

NEXTPNR_NAMESPACE_END
<commit_msg>proper fix<commit_after>/*
 *  nextpnr -- Next Generation Place and Route
 *
 *  Copyright (C) 2018  Miodrag Milanovic <miodrag@symbioticeda.com>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include "yosystab.h"
#include <QGridLayout>
#include <QMessageBox>

NEXTPNR_NAMESPACE_BEGIN

YosysTab::YosysTab(QString folder, QWidget *parent) : QWidget(parent)
{
    QFont f("unexistent");
    f.setStyleHint(QFont::Monospace);

    console = new QPlainTextEdit();
    console->setMinimumHeight(100);
    console->setReadOnly(true);
    console->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
    console->setFont(f);

    console->setContextMenuPolicy(Qt::CustomContextMenu);
    QAction *clearAction = new QAction("Clear &buffer", this);
    clearAction->setStatusTip("Clears display buffer");
    connect(clearAction, SIGNAL(triggered()), this, SLOT(clearBuffer()));
    contextMenu = console->createStandardContextMenu();
    contextMenu->addSeparator();
    contextMenu->addAction(clearAction);
    connect(console, SIGNAL(customContextMenuRequested(const QPoint)), this, SLOT(showContextMenu(const QPoint)));

    lineEdit = new YosysLineEditor();
    lineEdit->setMinimumHeight(30);
    lineEdit->setMaximumHeight(30);
    lineEdit->setFont(f);
    lineEdit->setFocus();
    lineEdit->setEnabled(false);
    lineEdit->setPlaceholderText("yosys>");
    connect(lineEdit, SIGNAL(textLineInserted(QString)), this, SLOT(editLineReturnPressed(QString)));

    QGridLayout *mainLayout = new QGridLayout();
    mainLayout->addWidget(console, 0, 0);
    mainLayout->addWidget(lineEdit, 1, 0);
    setLayout(mainLayout);

    process = new QProcess();
    connect(process, SIGNAL(readyReadStandardError()), this, SLOT(onReadyReadStandardError()));
    connect(process, SIGNAL(readyReadStandardOutput()), this, SLOT(onReadyReadStandardOutput()));
    connect(process, &QProcess::started, this, [this] { lineEdit->setEnabled(true); });

#if QT_VERSION < QT_VERSION_CHECK(5, 6, 0)
    connect(process, static_cast<void (QProcess::*)(QProcess::ProcessError)>(&QProcess::error), this, [this](QProcess::ProcessError error) {
#else
    connect(process, &QProcess::errorOccurred, this, [this](QProcess::ProcessError error) {
#endif
        if (error == QProcess::FailedToStart) {
            QMessageBox::critical(
                    this, QString::fromUtf8("Yosys cannot be started!"),
                    QString::fromUtf8("
Please make sure you have Yosys installed and available in path
"));
            Q_EMIT deleteLater();
        }
    });
    process->setWorkingDirectory(folder);
    process->start("yosys");
}

YosysTab::~YosysTab()
{
    process->terminate();
    process->waitForFinished(1000); // in ms
    process->kill();
    process->close();
}

void YosysTab::displayString(QString text)
{
    QTextCursor cursor = console->textCursor();
    cursor.movePosition(QTextCursor::End);
    cursor.insertText(text);
    cursor.movePosition(QTextCursor::End);
    console->setTextCursor(cursor);
}

void YosysTab::onReadyReadStandardOutput() { displayString(process->readAllStandardOutput()); }
void YosysTab::onReadyReadStandardError() { displayString(process->readAllStandardError()); }

void YosysTab::editLineReturnPressed(QString text) { process->write(text.toLatin1() + "\n"); }

void YosysTab::showContextMenu(const QPoint &pt) { contextMenu->exec(mapToGlobal(pt)); }

void YosysTab::clearBuffer() { console->clear(); }

NEXTPNR_NAMESPACE_END
<|endoftext|>


<commit_before>//
// Aspia Project
// Copyright (C) 2020 Dmitry Chapyshev <dmitry@aspia.ru>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//

#include "base/logging.h"

#include "base/debug.h"
#include "base/system_time.h"
#include "base/strings/unicode.h"

#include <fstream>
#include <iomanip>
#include <mutex>
#include <ostream>
#include <thread>

#if defined(OS_WIN)
#include <Windows.h>
#include <Psapi.h>
#endif // defined(OS_WIN)

#if defined(OS_LINUX)
#include <linux/limits.h>
#include <unistd.h>
#endif // defined(OS_LINUX)

#if defined(OS_MAC)
#include <mach-o/dyld.h>
#include <sys/syslimits.h>
#endif // defined(OS_MAC)

namespace base {

namespace {

LoggingSeverity g_min_log_level = LS_INFO;
LoggingDestination g_logging_destination = LOG_DEFAULT;

std::filesystem::path g_log_file_path;
std::ofstream g_log_file;
std::mutex g_log_file_lock;

const char* severityName(LoggingSeverity severity)
{
    static const char* const kLogSeverityNames[] =
    {
        "INFO", "WARNING", "ERROR", "FATAL"
    };

    static_assert(LS_NUMBER == std::size(kLogSeverityNames));

    if (severity >= 0 && severity < LS_NUMBER)
        return kLogSeverityNames[severity];

    return "UNKNOWN";
}

void removeOldFiles(const std::filesystem::path& path,
                    const std::filesystem::file_time_type& current_time,
                    int max_file_age)
{
    std::filesystem::file_time_type time = current_time - std::chrono::hours(24 * max_file_age);

    std::error_code ignored_code;
    for (const auto& item : std::filesystem::directory_iterator(path, ignored_code))
    {
        if (item.is_directory())
            continue;

        if (item.last_write_time() < time)
            std::filesystem::remove(item.path(), ignored_code);
    }
}

std::filesystem::path defaultLogFileDir()
{
    std::error_code error_code;

    std::filesystem::path path = std::filesystem::temp_directory_path(error_code);
    if (error_code)
        return std::filesystem::path();

    path.append("aspia");
    return path;
}

bool initLoggingImpl(const LoggingSettings& settings, const std::filesystem::path& file_name)
{
    std::scoped_lock lock(g_log_file_lock);
    g_log_file.close();

    g_logging_destination = settings.destination;

    if (!(g_logging_destination & LOG_TO_FILE))
        return true;

    std::filesystem::path file_dir = settings.log_dir;

    if (file_dir.empty())
        file_dir = defaultLogFileDir();

    if (file_dir.empty())
        return false;

    std::error_code error_code;
    if (!std::filesystem::exists(file_dir, error_code))
    {
        if (error_code)
            return false;

        if (!std::filesystem::create_directories(file_dir, error_code))
            return false;
    }

    SystemTime time = SystemTime::now();

    std::ostringstream file_name_stream;
    file_name_stream << file_name.c_str() << '-'
                     << std::setfill('0')
                     << std::setw(4) << time.year()
                     << std::setw(2) << time.month()
                     << std::setw(2) << time.day()
                     << '-'
                     << std::setw(2) << time.hour()
                     << std::setw(2) << time.minute()
                     << std::setw(2) << time.second()
                     << '.'
                     << std::setw(3) << time.millisecond()
                     << ".log";

    std::filesystem::path file_path(file_dir);
    file_path.append(file_name_stream.str());

    g_log_file.open(file_path);
    if (!g_log_file.is_open())
        return false;

    std::filesystem::file_time_type file_time =
        std::filesystem::last_write_time(file_path, error_code);
    if (!error_code)
        removeOldFiles(file_dir, file_time, settings.max_log_age);

    g_log_file_path = std::move(file_path);
    return true;
}

} // namespace

// This is never instantiated, it's just used for EAT_STREAM_PARAMETERS to have
// an object of the correct type on the LHS of the unused part of the ternary
// operator.
std::ostream* g_swallow_stream;

LoggingSettings::LoggingSettings()
    : destination(LOG_DEFAULT),
      min_log_level(LS_INFO),
      max_log_age(7)
{
    // Nothing
}

bool initLogging(const LoggingSettings& settings)
{
    std::filesystem::path exec_file_path;
#if defined(OS_WIN)
    wchar_t buffer[MAX_PATH] = { 0 };
    GetModuleFileNameExW(GetCurrentProcess(), nullptr, buffer, std::size(buffer));
    exec_file_path = buffer;
#elif defined(OS_LINUX)
    char buffer[PATH_MAX] = { 0 };
    readlink("/proc/self/exe", buffer, std::size(buffer));
    exec_file_path = buffer;
#elif defined(OS_MAC)
    char buffer[PATH_MAX] = { 0 };
    uint32_t buffer_size = std::size(buffer);
    _NSGetExecutablePath(buffer, &buffer_size);
    exec_file_path = buffer;
#else
#error Not implemented
#endif

    std::filesystem::path file_name = exec_file_path.filename();
    file_name.replace_extension();

    if (!initLoggingImpl(settings, file_name))
        return false;

    LOG(LS_INFO) << "Executable file: " << exec_file_path.c_str();
    LOG(LS_INFO) << "Logging file: " << g_log_file_path;
    LOG(LS_INFO) << "Debugger present: " << (isDebuggerPresent() ? "Yes" : "No");

#if defined(NDEBUG)
    LOG(LS_INFO) << "Debug build: No";
#else
    LOG(LS_INFO) << "Debug build: Yes";
#endif // defined(NDEBUG)

    LOG(LS_INFO) << "Logging started";
    return true;
}

void shutdownLogging()
{
    LOG(LS_INFO) << "Logging finished";

    std::scoped_lock lock(g_log_file_lock);
    g_log_file.close();
}

bool shouldCreateLogMessage(LoggingSeverity severity)
{
    if (severity < g_min_log_level)
        return false;

    // Return true here unless we know ~LogMessage won't do anything. Note that
    // ~LogMessage writes to stderr if severity_ >= kAlwaysPrintErrorLevel, even
    // when g_logging_destination is LOG_NONE.
    return g_logging_destination != LOG_NONE || severity >= LS_ERROR;
}

void makeCheckOpValueString(std::ostream* os, std::nullptr_t /* p */)
{
    (*os) << "nullptr";
}

std::string* makeCheckOpString(const int& v1, const int& v2, const char* names)
{
    return makeCheckOpString<int, int>(v1, v2, names);
}

std::string* makeCheckOpString(const unsigned long& v1, const unsigned long& v2, const char* names)
{
    return makeCheckOpString<unsigned long, unsigned long>(v1, v2, names);
}

std::string* makeCheckOpString(const unsigned int& v1, const unsigned int& v2, const char* names)
{
    return makeCheckOpString<unsigned int, unsigned int>(v1, v2, names);
}

std::string* makeCheckOpString(const unsigned long long& v1,
                               const unsigned long long& v2,
                               const char* names)
{
    return makeCheckOpString<unsigned long long, unsigned long long>(v1, v2, names);
}

std::string* makeCheckOpString(const unsigned long& v1, const unsigned int& v2, const char* names)
{
    return makeCheckOpString<unsigned long, int>(v1, v2, names);
}

std::string* makeCheckOpString(const unsigned int& v1, const unsigned long& v2, const char* names)
{
    return makeCheckOpString<unsigned int, unsigned long>(v1, v2, names);
}

std::string* makeCheckOpString(const std::string& v1, const std::string& v2, const char* names)
{
    return makeCheckOpString<std::string, std::string>(v1, v2, names);
}

LogMessage::LogMessage(std::string_view file, int line, LoggingSeverity severity)
    : severity_(severity)
{
    init(file, line);
}

LogMessage::LogMessage(std::string_view file, int line, const char* condition)
    : severity_(LS_FATAL)
{
    init(file, line);
    stream_ << "Check failed: " << condition << ". ";
}

LogMessage::LogMessage(std::string_view file, int line, std::string* result)
    : severity_(LS_FATAL)
{
    std::unique_ptr<std::string> result_deleter(result);
    init(file, line);
    stream_ << "Check failed: " << *result;
}

LogMessage::LogMessage(std::string_view file,
                       int line,
                       LoggingSeverity severity,
                       std::string* result)
    : severity_(severity)
{
    std::unique_ptr<std::string> result_deleter(result);
    init(file, line);
    stream_ << "Check failed: " << *result;
}

LogMessage::~LogMessage()
{
    stream_ << std::endl;

    std::string message(stream_.str());

    if ((g_logging_destination & LOG_TO_SYSTEM_DEBUG_LOG) != 0)
    {
        debugPrint(message.data());

        fwrite(message.data(), message.size(), 1, stderr);
        fflush(stderr);
    }
    else if (severity_ >= LS_ERROR)
    {
        // When we're only outputting to a log file, above a certain log level, we
        // should still output to stderr so that we can better detect and diagnose
        // problems with unit tests, especially on the buildbots.
        fwrite(message.data(), message.size(), 1, stderr);
        fflush(stderr);
    }

    // Write to log file.
    if ((g_logging_destination & LOG_TO_FILE) != 0)
    {
        std::scoped_lock lock(g_log_file_lock);
        g_log_file.write(message.c_str(), message.size());
        g_log_file.flush();
    }

    if (severity_ == LS_FATAL)
    {
        // Crash the process.
        debugBreak();
    }
}

// Writes the common header info to the stream.
void LogMessage::init(std::string_view file, int line)
{
    size_t last_slash_pos = file.find_last_of("\\/");
    if (last_slash_pos != std::string_view::npos)
        file.remove_prefix(last_slash_pos + 1);

    SystemTime time = SystemTime::now();

    stream_ << std::setfill('0')
            << std::setw(2) << time.hour()        << ':'
            << std::setw(2) << time.minute()      << ':'
            << std::setw(2) << time.second()      << '.'
            << std::setw(3) << time.millisecond() << ' '
            << std::this_thread::get_id()         << ' '
            << severityName(severity_)            << ' '
            << file.data() << ":" << line << "] ";

    message_start_ = stream_.str().length();
}

ErrorLogMessage::ErrorLogMessage(std::string_view file,
                                 int line,
                                 LoggingSeverity severity,
                                 SystemError error)
    : error_(error),
      log_message_(file, line, severity)
{
    // Nothing
}

ErrorLogMessage::~ErrorLogMessage()
{
    stream() << ": " << error_.toString();
}

void logErrorNotReached(const char* file, int line)
{
    LogMessage(file, line, LS_ERROR).stream() << "NOTREACHED() hit.";
}

} // namespace base

namespace std {

#if defined(OS_WIN)
std::ostream& operator<<(std::ostream& out, const std::wstring& wstr)
{
    return out << base::utf8FromWide(wstr);
}

std::ostream& std::operator<<(std::ostream& out, const wchar_t* wstr)
{
    return out << (wstr ? base::utf8FromWide(wstr) : "nullptr");
}
#endif // defined(OS_WIN)

std::ostream& operator<<(std::ostream& out, const char16_t* ustr)
{
    return out << (ustr ? base::utf8FromUtf16(ustr) : "nullptr");
}

std::ostream& operator<<(std::ostream& out, const std::u16string& ustr)
{
    return out << base::utf8FromUtf16(ustr);
}

} // namespace std
<commit_msg>Added endian info to log.<commit_after>//
// Aspia Project
// Copyright (C) 2020 Dmitry Chapyshev <dmitry@aspia.ru>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//

#include "base/logging.h"

#include "base/debug.h"
#include "base/endian_util.h"
#include "base/system_time.h"
#include "base/strings/unicode.h"

#include <fstream>
#include <iomanip>
#include <mutex>
#include <ostream>
#include <thread>

#if defined(OS_WIN)
#include <Windows.h>
#include <Psapi.h>
#endif // defined(OS_WIN)

#if defined(OS_LINUX)
#include <linux/limits.h>
#include <unistd.h>
#endif // defined(OS_LINUX)

#if defined(OS_MAC)
#include <mach-o/dyld.h>
#include <sys/syslimits.h>
#endif // defined(OS_MAC)

namespace base {

namespace {

LoggingSeverity g_min_log_level = LS_INFO;
LoggingDestination g_logging_destination = LOG_DEFAULT;

std::filesystem::path g_log_file_path;
std::ofstream g_log_file;
std::mutex g_log_file_lock;

const char* severityName(LoggingSeverity severity)
{
    static const char* const kLogSeverityNames[] =
    {
        "INFO", "WARNING", "ERROR", "FATAL"
    };

    static_assert(LS_NUMBER == std::size(kLogSeverityNames));

    if (severity >= 0 && severity < LS_NUMBER)
        return kLogSeverityNames[severity];

    return "UNKNOWN";
}

void removeOldFiles(const std::filesystem::path& path,
                    const std::filesystem::file_time_type& current_time,
                    int max_file_age)
{
    std::filesystem::file_time_type time = current_time - std::chrono::hours(24 * max_file_age);

    std::error_code ignored_code;
    for (const auto& item : std::filesystem::directory_iterator(path, ignored_code))
    {
        if (item.is_directory())
            continue;

        if (item.last_write_time() < time)
            std::filesystem::remove(item.path(), ignored_code);
    }
}

std::filesystem::path defaultLogFileDir()
{
    std::error_code error_code;

    std::filesystem::path path = std::filesystem::temp_directory_path(error_code);
    if (error_code)
        return std::filesystem::path();

    path.append("aspia");
    return path;
}

bool initLoggingImpl(const LoggingSettings& settings, const std::filesystem::path& file_name)
{
    std::scoped_lock lock(g_log_file_lock);
    g_log_file.close();

    g_logging_destination = settings.destination;

    if (!(g_logging_destination & LOG_TO_FILE))
        return true;

    std::filesystem::path file_dir = settings.log_dir;

    if (file_dir.empty())
        file_dir = defaultLogFileDir();

    if (file_dir.empty())
        return false;

    std::error_code error_code;
    if (!std::filesystem::exists(file_dir, error_code))
    {
        if (error_code)
            return false;

        if (!std::filesystem::create_directories(file_dir, error_code))
            return false;
    }

    SystemTime time = SystemTime::now();

    std::ostringstream file_name_stream;
    file_name_stream << file_name.c_str() << '-'
                     << std::setfill('0')
                     << std::setw(4) << time.year()
                     << std::setw(2) << time.month()
                     << std::setw(2) << time.day()
                     << '-'
                     << std::setw(2) << time.hour()
                     << std::setw(2) << time.minute()
                     << std::setw(2) << time.second()
                     << '.'
                     << std::setw(3) << time.millisecond()
                     << ".log";

    std::filesystem::path file_path(file_dir);
    file_path.append(file_name_stream.str());

    g_log_file.open(file_path);
    if (!g_log_file.is_open())
        return false;

    std::filesystem::file_time_type file_time =
        std::filesystem::last_write_time(file_path, error_code);
    if (!error_code)
        removeOldFiles(file_dir, file_time, settings.max_log_age);

    g_log_file_path = std::move(file_path);
    return true;
}

} // namespace

// This is never instantiated, it's just used for EAT_STREAM_PARAMETERS to have
// an object of the correct type on the LHS of the unused part of the ternary
// operator.
std::ostream* g_swallow_stream;

LoggingSettings::LoggingSettings()
    : destination(LOG_DEFAULT),
      min_log_level(LS_INFO),
      max_log_age(7)
{
    // Nothing
}

bool initLogging(const LoggingSettings& settings)
{
    std::filesystem::path exec_file_path;
#if defined(OS_WIN)
    wchar_t buffer[MAX_PATH] = { 0 };
    GetModuleFileNameExW(GetCurrentProcess(), nullptr, buffer, std::size(buffer));
    exec_file_path = buffer;
#elif defined(OS_LINUX)
    char buffer[PATH_MAX] = { 0 };
    readlink("/proc/self/exe", buffer, std::size(buffer));
    exec_file_path = buffer;
#elif defined(OS_MAC)
    char buffer[PATH_MAX] = { 0 };
    uint32_t buffer_size = std::size(buffer);
    _NSGetExecutablePath(buffer, &buffer_size);
    exec_file_path = buffer;
#else
#error Not implemented
#endif

    std::filesystem::path file_name = exec_file_path.filename();
    file_name.replace_extension();

    if (!initLoggingImpl(settings, file_name))
        return false;

    LOG(LS_INFO) << "Executable file: " << exec_file_path.c_str();
    LOG(LS_INFO) << "Logging file: " << g_log_file_path;
    LOG(LS_INFO) << "Debugger present: " << (isDebuggerPresent() ? "Yes" : "No");

#if defined(NDEBUG)
    LOG(LS_INFO) << "Debug build: No";
#else
    LOG(LS_INFO) << "Debug build: Yes";
#endif // defined(NDEBUG)

    LOG(LS_INFO) << "Little endian: " << (EndianUtil::isLittle() ? "Yes" : "No");
    LOG(LS_INFO) << "Logging started";
    return true;
}

void shutdownLogging()
{
    LOG(LS_INFO) << "Logging finished";

    std::scoped_lock lock(g_log_file_lock);
    g_log_file.close();
}

bool shouldCreateLogMessage(LoggingSeverity severity)
{
    if (severity < g_min_log_level)
        return false;

    // Return true here unless we know ~LogMessage won't do anything. Note that
    // ~LogMessage writes to stderr if severity_ >= kAlwaysPrintErrorLevel, even
    // when g_logging_destination is LOG_NONE.
    return g_logging_destination != LOG_NONE || severity >= LS_ERROR;
}

void makeCheckOpValueString(std::ostream* os, std::nullptr_t /* p */)
{
    (*os) << "nullptr";
}

std::string* makeCheckOpString(const int& v1, const int& v2, const char* names)
{
    return makeCheckOpString<int, int>(v1, v2, names);
}

std::string* makeCheckOpString(const unsigned long& v1, const unsigned long& v2, const char* names)
{
    return makeCheckOpString<unsigned long, unsigned long>(v1, v2, names);
}

std::string* makeCheckOpString(const unsigned int& v1, const unsigned int& v2, const char* names)
{
    return makeCheckOpString<unsigned int, unsigned int>(v1, v2, names);
}

std::string* makeCheckOpString(const unsigned long long& v1,
                               const unsigned long long& v2,
                               const char* names)
{
    return makeCheckOpString<unsigned long long, unsigned long long>(v1, v2, names);
}

std::string* makeCheckOpString(const unsigned long& v1, const unsigned int& v2, const char* names)
{
    return makeCheckOpString<unsigned long, int>(v1, v2, names);
}

std::string* makeCheckOpString(const unsigned int& v1, const unsigned long& v2, const char* names)
{
    return makeCheckOpString<unsigned int, unsigned long>(v1, v2, names);
}

std::string* makeCheckOpString(const std::string& v1, const std::string& v2, const char* names)
{
    return makeCheckOpString<std::string, std::string>(v1, v2, names);
}

LogMessage::LogMessage(std::string_view file, int line, LoggingSeverity severity)
    : severity_(severity)
{
    init(file, line);
}

LogMessage::LogMessage(std::string_view file, int line, const char* condition)
    : severity_(LS_FATAL)
{
    init(file, line);
    stream_ << "Check failed: " << condition << ". ";
}

LogMessage::LogMessage(std::string_view file, int line, std::string* result)
    : severity_(LS_FATAL)
{
    std::unique_ptr<std::string> result_deleter(result);
    init(file, line);
    stream_ << "Check failed: " << *result;
}

LogMessage::LogMessage(std::string_view file,
                       int line,
                       LoggingSeverity severity,
                       std::string* result)
    : severity_(severity)
{
    std::unique_ptr<std::string> result_deleter(result);
    init(file, line);
    stream_ << "Check failed: " << *result;
}

LogMessage::~LogMessage()
{
    stream_ << std::endl;

    std::string message(stream_.str());

    if ((g_logging_destination & LOG_TO_SYSTEM_DEBUG_LOG) != 0)
    {
        debugPrint(message.data());

        fwrite(message.data(), message.size(), 1, stderr);
        fflush(stderr);
    }
    else if (severity_ >= LS_ERROR)
    {
        // When we're only outputting to a log file, above a certain log level, we
        // should still output to stderr so that we can better detect and diagnose
        // problems with unit tests, especially on the buildbots.
        fwrite(message.data(), message.size(), 1, stderr);
        fflush(stderr);
    }

    // Write to log file.
    if ((g_logging_destination & LOG_TO_FILE) != 0)
    {
        std::scoped_lock lock(g_log_file_lock);
        g_log_file.write(message.c_str(), message.size());
        g_log_file.flush();
    }

    if (severity_ == LS_FATAL)
    {
        // Crash the process.
        debugBreak();
    }
}

// Writes the common header info to the stream.
void LogMessage::init(std::string_view file, int line)
{
    size_t last_slash_pos = file.find_last_of("\\/");
    if (last_slash_pos != std::string_view::npos)
        file.remove_prefix(last_slash_pos + 1);

    SystemTime time = SystemTime::now();

    stream_ << std::setfill('0')
            << std::setw(2) << time.hour()        << ':'
            << std::setw(2) << time.minute()      << ':'
            << std::setw(2) << time.second()      << '.'
            << std::setw(3) << time.millisecond() << ' '
            << std::this_thread::get_id()         << ' '
            << severityName(severity_)            << ' '
            << file.data() << ":" << line << "] ";

    message_start_ = stream_.str().length();
}

ErrorLogMessage::ErrorLogMessage(std::string_view file,
                                 int line,
                                 LoggingSeverity severity,
                                 SystemError error)
    : error_(error),
      log_message_(file, line, severity)
{
    // Nothing
}

ErrorLogMessage::~ErrorLogMessage()
{
    stream() << ": " << error_.toString();
}

void logErrorNotReached(const char* file, int line)
{
    LogMessage(file, line, LS_ERROR).stream() << "NOTREACHED() hit.";
}

} // namespace base

namespace std {

#if defined(OS_WIN)
std::ostream& operator<<(std::ostream& out, const std::wstring& wstr)
{
    return out << base::utf8FromWide(wstr);
}

std::ostream& std::operator<<(std::ostream& out, const wchar_t* wstr)
{
    return out << (wstr ? base::utf8FromWide(wstr) : "nullptr");
}
#endif // defined(OS_WIN)

std::ostream& operator<<(std::ostream& out, const char16_t* ustr)
{
    return out << (ustr ? base::utf8FromUtf16(ustr) : "nullptr");
}

std::ostream& operator<<(std::ostream& out, const std::u16string& ustr)
{
    return out << base::utf8FromUtf16(ustr);
}

} // namespace std
<|endoftext|>