text:
//- Common Code For All Addons needed just to ease inclusion as separate files in user code ----------------------
#include <imgui.h>
#undef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
#include <imgui_internal.h>
//-----------------------------------------------------------------------------------------------------------------

#include "imguidatechooser.h"
#include <time.h>               // very simple and common plain C header file (it's NOT the c++ <sys/time.h>). If not available it's probably better to implement it yourself rather than modifying this file.
#include <ctype.h>  // toupper()

namespace ImGui {

//struct tm
//{
//  int tm_sec;			/* Seconds.	[0-60] (1 leap second) */
//  int tm_min;			/* Minutes.	[0-59] */
//  int tm_hour;			/* Hours.	[0-23] */
//  int tm_mday;			/* Day.		[1-31] */
//  int tm_mon;			/* Month.	[0-11] */
//  int tm_year;			/* Year	- 1900.  */
//  int tm_wday;			/* Day of week.	[0-6] */
//  int tm_yday;			/* Days in year.[0-365]	*/
//  int tm_isdst;			/* DST.		[-1/0/1]*/

//# ifdef	__USE_BSD
//  long int tm_gmtoff;		/* Seconds east of UTC.  */
//  const char *tm_zone;		/* Timezone abbreviation.  */
//# else
//  long int __tm_gmtoff;		/* Seconds east of UTC.  */
//  const char *__tm_zone;	/* Timezone abbreviation.  */
//# endif
//};

inline static tm GetCurrentDate() {
    time_t now;time(&now);
    return *localtime(&now);
}
// Tip: we modify tm (its fields can even be negative!) and then we call this method to retrieve a valid date
inline static void RecalculateDateDependentFields(tm& date)    {
    date.tm_isdst=-1;   // This tries to detect day time savings too
    time_t tmp = mktime(&date);
    date=*localtime(&tmp);
}
/*inline static size_t DateToString(tm& d,char* buf,size_t bufferSize)  {
    return strftime(buf,bufferSize,
                    //"%a %d %b %Y"
                    "%d/%m/%Y"
                    ,&d);
}*/

inline static tm GetDateZero() {
    tm date;
    memset(&date,0,sizeof(tm));     // Mandatory for emscripten. Please do not remove!
    date.tm_isdst=-1;
    date.tm_sec=0;		//	 Seconds.	[0-60] (1 leap second)
    date.tm_min=0;		//	 Minutes.	[0-59]
    date.tm_hour=0;		//	 Hours.	[0-23]
    date.tm_mday=0;		//	 Day.		[1-31]
    date.tm_mon=0;		//	 Month.	[0-11]
    date.tm_year=0;		//	 Year	- 1900.
    date.tm_wday=0;     //	 Day of week.	[0-6]
    date.tm_yday=0;		//	 Days in year.[0-365]
    return date;
}
void SetDateZero(tm* date) {
    if (!date) return;
    *date = GetDateZero();
    return;
}
void SetDateToday(tm* date)    {
    if (!date) return;
    *date = GetCurrentDate();
    return;
}

bool DateChooser(const char* label, tm& dateOut,const char* dateFormat,bool closeWhenMouseLeavesIt,bool* pSetStartDateToDateOutThisFrame,const char* leftArrow,const char* rightArrow,const char* upArrowString,const char* downArrowString)    {
    ImGuiContext& g = *GImGui;
    ImGuiWindow* window = GetCurrentWindow();

    if (window->SkipItems)
        return false;

    static tm d=GetDateZero();
    if (pSetStartDateToDateOutThisFrame && *pSetStartDateToDateOutThisFrame) {
        *pSetStartDateToDateOutThisFrame=false;
        if (dateOut.tm_mday==0) dateOut = GetCurrentDate();
        d = dateOut;
        // move d at the first day of the month: mandatory fo the algo I'll use below
        d.tm_mday = 1;
        RecalculateDateDependentFields(d);  // now d.tm_wday is correct
    }
    else if (dateOut.tm_mday==0) {
        dateOut = GetCurrentDate();
        d = dateOut;
        // move d at the first day of the month: mandatory fo the algo I'll use below
        d.tm_mday = 1;
        RecalculateDateDependentFields(d);  // now d.tm_wday is correct
    }
    else if (d.tm_mday==0) {
        d = GetCurrentDate();
        // move d at the first day of the month: mandatory fo the algo I'll use below
        d.tm_mday = 1;
        RecalculateDateDependentFields(d);  // now d.tm_wday is correct
    }
    else if (d.tm_mday!=1) {
        // move d at the first day of the month: mandatory fo the algo I'll use below
        d.tm_mday = 1;
        RecalculateDateDependentFields(d);  // now d.tm_wday is correct
    }

    static const int nameBufferSize = 64;
    static char dayNames[7][nameBufferSize]={"","","","","","",""};
    static char monthNames[12][nameBufferSize]={"","","","","","",""};
    static char* pMonthNames[12]={&monthNames[0][0],&monthNames[1][0],&monthNames[2][0],&monthNames[3][0],&monthNames[4][0],&monthNames[5][0],&monthNames[6][0],&monthNames[7][0],&monthNames[8][0],&monthNames[9][0],&monthNames[10][0],&monthNames[11][0]};
    static int maxMonthWidthIndex = -1;
    if (strlen(dayNames[0])==0) {
	tm tmp = GetDateZero();
        for (int i=0;i<7;i++)   {
            tmp.tm_wday=i;
            char* dayName = &dayNames[i][0];
            strftime(dayName,nameBufferSize,"%A",&tmp);
            //fprintf(stderr,"%s\n",dayNames[i]);
            // Warning: This part won't work for multibyte UTF-8 chars:
            // We want to keep the first letter only here, and we want it to be uppercase (because some locales provide it lowercase)
            if (strlen(dayName)==0) {
                static const char fallbacks[7]={'S','M','T','W','T','F','S'};
                dayName[0] = fallbacks[i];dayName[1] = '\0';
            }
            else {
                dayName[0]=toupper(dayName[0]);
                dayName[1]='\0';
            }
            //fprintf(stderr,"%s\n",dayNames[i]);

        }
        float maxMonthWidth = 0;
        for (int i=0;i<12;i++)   {
            tmp.tm_mon=i;
            char* monthName = &monthNames[i][0];
            strftime(monthName,nameBufferSize,"%B",&tmp);
            //fprintf(stderr,"%s\n",monthNames[i]);
            // Warning: This part won't work for multibyte UTF-8 chars:
            // We want the first letter to be uppercase (because some locales provide it lowercase)
            if (strlen(monthName)==0) {
                static const char* fallbacks[12]={"January","February","March","April","May","June","July","August","September","October","November","December"};
                strcpy(monthName,fallbacks[i]);
            }
            else monthName[0]=toupper(monthName[0]);
            //fprintf(stderr,"%s\n",monthNames[i]);
            float mw = ImGui::CalcTextSize(monthName).x;
            if (maxMonthWidth<mw) {
                maxMonthWidth = mw;
                maxMonthWidthIndex = i;
            }
        }
    }

    ImGuiStyle& style = g.Style;
    const ImGuiID id = window->GetID(label);

    const char* arrowLeft= leftArrow ? leftArrow : "<";
    const char* arrowRight= rightArrow ? rightArrow : ">";
    const float arrowLeftWidth  = ImGui::CalcTextSize(arrowLeft).x;
    const float arrowRightWidth = ImGui::CalcTextSize(arrowRight).x;

    static char currentText[128];strftime(currentText,128,dateFormat,&dateOut);
    const ImVec2 label_size = CalcTextSize(label, NULL, true);
    float widthAdder = 0;
#   ifdef IMGUIVARIOUSCONTROLS_H_
    widthAdder+=2.f*style.ItemSpacing.x;
#   endif //IMGUIVARIOUSCONTROLS_H_
    const float widthRequiredByCalendar = 2.f*arrowLeftWidth + 2.f*arrowRightWidth + ImGui::CalcTextSize(monthNames[maxMonthWidthIndex]).x + ImGui::CalcTextSize("9999").x + 50.f + widthAdder;
    const float widthRequiredByTextBox = label_size.x + ImGui::CalcTextSize(currentText).x;// + style.FramePadding.x*2.0f;
    const float w = widthRequiredByCalendar > widthRequiredByTextBox ? widthRequiredByCalendar : widthRequiredByTextBox;//ImGui::CalcItemWidth();

    const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y) + style.FramePadding*2.0f);
    const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(style.ItemInnerSpacing.x + label_size.x,0));
    ItemSize(total_bb, style.FramePadding.y);
    if (!ItemAdd(total_bb, id)) return false;

    const float arrow_size = (g.FontSize + style.FramePadding.x * 2.0f);
    const bool hovered = ItemHoverable(frame_bb, id);

    bool value_changed = false;
    const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f));
    RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
    RenderFrame(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y), frame_bb.Max, GetColorU32(hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button), true, style.FrameRounding);	// FIXME-ROUNDING
    RenderArrow(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y) + style.FramePadding,ImGuiDir_Down);

    //RenderTextClipped(frame_bb.Min + style.FramePadding, currentText, NULL, NULL, value_bb.Max);
    RenderTextClipped(frame_bb.Min + style.FramePadding, value_bb.Max, currentText, NULL, NULL);

    if (label_size.x > 0) RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);

    ImGuiStorage* storage = ImGui::GetStateStorage();
    const bool isOpenStored = storage->GetBool(id,false);
    bool isOpen = isOpenStored;

    ImGui::PushID((int)id);
    //bool menu_toggled = false;
    if (hovered)    {
        //g.HoveredId = id;
        if (g.IO.MouseClicked[0])   isOpen = !isOpen;
        if (isOpen && g.IO.MouseClicked[1])   {
            // reset date when user right clicks the date chooser header whe the dialog is open
            d = GetCurrentDate();
            // move d at the first day of the month: mandatory fo the algo I'll use below
            d.tm_mday = 1;
            RecalculateDateDependentFields(d);  // now d.tm_wday is correct
        }
    }

    if (isOpen)  {

        const int height_in_items = 1 + 1 + 1 + 4;
        const int items_count = height_in_items;

        const ImVec2 backup_pos = ImGui::GetCursorPos();
        const float popup_off_x = 0.0f;
        const float popup_height = (label_size.y + 2*style.ItemSpacing.y) * ImMin(items_count, height_in_items) + (style.FramePadding.y * 3);
        const ImRect popup_rect(ImVec2(frame_bb.Min.x+popup_off_x+ImGui::GetScrollX(), frame_bb.Max.y+ImGui::GetScrollY()), ImVec2(frame_bb.Max.x+popup_off_x + ImGui::GetScrollX(), frame_bb.Max.y + popup_height + ImGui::GetScrollY()));
        ImGui::SetCursorPos(popup_rect.Min - window->Pos);

        // ImGuiWindowFlags_ComboBox is no longer present... we simply remove it and see if it still work...
        const ImGuiWindowFlags flags = /*ImGuiWindowFlags_ComboBox |*/ ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse;// | ImGuiWindowFlags_Modal;    // Adding ImGuiWindowFlags_Modal seems to work too...
        ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
        ImGui::PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_PopupBg]);
        ImGui::BeginChild("#ComboBoxDateChooser", popup_rect.GetSize(), true, flags);
        ImGui::Spacing();

        bool combo_item_active = false;

        static const ImVec4 transparent(1,1,1,0);
        ImGui::PushStyleColor(ImGuiCol_Button,transparent);

        static char yearString[12]="";sprintf(yearString,"%d",1900+d.tm_year);
        //const float monthPartWidth = arrowLeftWidth + arrowRightWidth + ImGui::CalcTextSize(monthNames[d.tm_mon]).x;
        const float yearPartWidth = arrowLeftWidth + arrowRightWidth + ImGui::CalcTextSize(yearString).x + widthAdder*0.5f;

        const float oldWindowRounding = style.WindowRounding;
        style.WindowRounding = 0;

        ImGui::PushID(1234);
        if (ImGui::SmallButton(arrowLeft)) {d.tm_mon-=1;RecalculateDateDependentFields(d);}
        ImGui::SameLine();
//#       undef  IMGUIVARIOUSCONTROLS_H_
#       ifndef IMGUIVARIOUSCONTROLS_H_
        ImGui::Text("%s",monthNames[d.tm_mon]);
#       else //IMGUIVARIOUSCONTROLS_H_
        static ImGui::PopupMenuSimpleParams popupMonthParams;
        ImVec2 menupos(ImGui::GetCursorPos().x+popup_rect.Min.x,ImGui::GetIO().MousePos.y);
        bool previousOpen = popupMonthParams.open;
        popupMonthParams.open|=ImGui::SmallButton(monthNames[d.tm_mon]);
        if (popupMonthParams.open) {
            if (previousOpen!=popupMonthParams.open) ImGui::SetNextWindowPos(menupos);
            const int selectedEntry = ImGui::PopupMenuSimple(popupMonthParams,(const char**)pMonthNames,12,6,false,NULL,upArrowString,downArrowString);
            if (selectedEntry>=0) {d.tm_mon = selectedEntry;RecalculateDateDependentFields(d);}
        }
#       endif //IMGUIVARIOUSCONTROLS_H_
        ImGui::SameLine();
        if (ImGui::SmallButton(arrowRight)) {d.tm_mon+=1;RecalculateDateDependentFields(d);}
        ImGui::PopID();

        ImGui::SameLine(ImGui::GetWindowWidth()-yearPartWidth-ImGui::GetStyle().WindowPadding.x-ImGui::GetStyle().ItemSpacing.x*4.f);

        ImGui::PushID(1235);
        if (ImGui::SmallButton(arrowLeft)) {d.tm_year-=1;if(d.tm_year<0) d.tm_year=0;RecalculateDateDependentFields(d);}
        ImGui::SameLine();

#       ifndef IMGUIVARIOUSCONTROLS_H_
        ImGui::Text("%d",1900+d.tm_year);
#       else //IMGUIVARIOUSCONTROLS_H_
        static ImGui::PopupMenuSimpleParams popupYearParams;
        menupos = ImVec2(ImGui::GetCursorPos().x+popup_rect.Min.x,ImGui::GetIO().MousePos.y);
        previousOpen = popupYearParams.open;
        popupYearParams.open|=ImGui::SmallButton(yearString);
        if (popupYearParams.open) {
            char years[20][5];
            char* pyears[20];
            for (int i=0;i<20;i++) {sprintf(&years[i][0],"%d",1900+d.tm_year-10+i);pyears[i]=&years[i][0];}
            if (previousOpen!=popupYearParams.open) ImGui::SetNextWindowPos(menupos);
            const int selectedEntry = ImGui::PopupMenuSimple(popupYearParams,(const char**)pyears,20,6,false,NULL,upArrowString,downArrowString);
            if (selectedEntry>=0) {if (selectedEntry+d.tm_year-10>=0) {d.tm_year += selectedEntry-10;RecalculateDateDependentFields(d);}}
        }
#       endif //IMGUIVARIOUSCONTROLS_H_
        ImGui::SameLine();
        if (ImGui::SmallButton(arrowRight)) {d.tm_year+=1;RecalculateDateDependentFields(d);}
        ImGui::PopID();

        ImGui::Spacing();

        const static int numDaysPerMonth[12]={31,28,31,30,31,30,31,31,30,31,30,31};
        int maxDayOfCurMonth = numDaysPerMonth[d.tm_mon];   // This could be calculated only when needed (but I guess it's fast in any case...)
        if (maxDayOfCurMonth==28)   {
            const int year = d.tm_year+1900;
            const bool bis = ((year%4)==0) && ((year%100)!=0 || (year%400)==0);
            if (bis) maxDayOfCurMonth=29;
        }
        static char curDayStr[3]="";

        ImGui::PushStyleColor(ImGuiCol_ButtonHovered,style.Colors[ImGuiCol_HeaderHovered]);
        ImGui::PushStyleColor(ImGuiCol_ButtonActive,style.Colors[ImGuiCol_HeaderActive]);

        IM_ASSERT(d.tm_mday==1);    // Otherwise the algo does not work
        // Display items
        for (int dw=0;dw<7;dw++)    {
            ImGui::BeginGroup();
            if (dw==0)  {
                const ImVec4& tc = style.Colors[ImGuiCol_Text];
                const float l = (tc.x+tc.y+tc.z)*0.33334f;
                ImGui::PushStyleColor(ImGuiCol_Text,ImVec4(l*2.f>1?1:l*2.f,l*.5f,l*.5f,tc.w));
            }
            ImGui::Separator();
            ImGui::Text(" %s",dayNames[dw]);
            ImGui::Separator();
            ImGui::Spacing();
            //-----------------------------------------------------------------------
            int curDay = dw-d.tm_wday;      // tm_wday is in [0,6]. For this to work here d must point to the first day of the month: i.e.: d.tm_mday = 1;
            for (int row=0;row<7;row++) {
                int cday=curDay+7*row;
                if (cday>=0 && cday<maxDayOfCurMonth)  {
                    ImGui::PushID(row*10+dw);
                    if (cday<9) sprintf(curDayStr," %d",cday+1);
                    else sprintf(curDayStr,"%d",cday+1);
                    if (ImGui::SmallButton(curDayStr)) {
                        ClearActiveID();
                        //-------------------------
                        value_changed = true;
                        dateOut = d;
                        dateOut.tm_mday = cday+1;
                        RecalculateDateDependentFields(dateOut);
                        d=dateOut;
                        //fprintf(stderr,"Chosen date: %d-%d-%d/n",dateOut.tm_mday,dateOut.tm_mon+1,dateOut.tm_year+1900);
                        combo_item_active = true;
                        isOpen = false;
                    }
                    combo_item_active |= ImGui::IsItemActive();
                    ImGui::PopID();
                }
                else ImGui::Text(" ");
            }
            //-----------------------------------------------------------------------
            ImGui::Separator();
            if (dw==0) ImGui::PopStyleColor();
            ImGui::EndGroup();
            if (dw!=6) ImGui::SameLine(ImGui::GetWindowWidth()-(6-dw)*(ImGui::GetWindowWidth()/7.f));
        }

        style.WindowRounding = oldWindowRounding;
        ImGui::PopStyleColor(2);

        if (closeWhenMouseLeavesIt || (!combo_item_active && g.ActiveId != 0))
        {
            const float distance = g.FontSize*1.75f;//1.3334f;//24;
            //fprintf(stderr,"%1.f",distance);
            ImVec2 pos = ImGui::GetWindowPos();pos.x-=distance;pos.y-=distance;
            ImVec2 size = ImGui::GetWindowSize();size.x+=2.f*distance;size.y+=2.f*distance;
            const ImVec2& mousePos = ImGui::GetIO().MousePos;
            if (mousePos.x<pos.x || mousePos.y<pos.y || mousePos.x>pos.x+size.x || mousePos.y>pos.y+size.y) {
                isOpen = false;
                d = dateOut;
                //fprintf(stderr,"Leaving DateChooser\n");
            }
        }

        ImGui::PopStyleColor();

        ImGui::EndChild();
        ImGui::PopStyleColor();
        ImGui::PopStyleVar();
        ImGui::SetCursorPos(backup_pos);

    }

    ImGui::PopID();

    if (isOpen!=isOpenStored) storage->SetBool(id,isOpen);

    //if (value_changed) d.tm_mday=0;    //reset d
    return value_changed;

}

void TestDateChooser(const char* dateFormat,bool closeWhenMouseLeavesIt,const char* leftArrow,const char* rightArrow,const char* upArrowString,const char* downArrowString)  {
    static tm date=GetDateZero();
    if (DateChooser("DateChooser##id",date,dateFormat,closeWhenMouseLeavesIt,NULL,leftArrow,rightArrow,upArrowString,downArrowString)) {
        // Do spmething with date
    }
}

} // namespace ImGui


text:
<!DOCTYPE html>
<!--
Copyright 2017 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->

<link rel="import" href="/tracing/model/event.html">
<link rel="import" href="/tracing/model/event_registry.html">

<script>
'use strict';

tr.exportTo('tr.model', function() {
  const Event = tr.model.Event;
  const EventRegistry = tr.model.EventRegistry;

  /**
   * A sample containing data about what fraction of a resource
   * (CPU or GPU) is being used at a given point in time.
   */
  class ResourceUsageSample extends Event {

    /**
     * @param {ResourceUsageSeries } series The ResourceUsageSeries that this
     * sample will be a part of.
     * @param {float} start Time of the sample.
     * @param {float} usage Fraction of the resource (CPU or GPU) in use at the
     * time of the sample.
     */
    constructor(series, start, usage) {
      super();

      this.series_ = series;
      this.start_ = start;
      this.usage_ = usage;
    }

    get series() {
      return this.series_;
    }

    get start() {
      return this.start_;
    }

    set start(value) {
      this.start_ = value;
    }

    get usage() {
      return this.usage_;
    }

    set usage(value) {
      this.usage_ = value;
    }

    addBoundsToRange(range) {
      range.addValue(this.start);
    }
  }

  EventRegistry.register(
      ResourceUsageSample,
      {
        name: 'resourceUsageSample',
        pluralName: 'resourceUsageSamples'
      });

  return {
    ResourceUsageSample,
  };
});
</script>

redpajama_set_name: RedPajamaGithub


text:
// Copyright (c) 2012 Ecma International.  All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

/*---
es5id: 15.4.4.22-3-2
description: >
    Array.prototype.reduceRight applied to an Array-like object,
    'length' is 0 (length overridden to false(type conversion))
---*/

        var accessed = false;

        function callbackfn(preVal, curVal, idx, obj) {
            accessed = true;
        }

        var obj = { 0: 9, length: false };

assert.sameValue(Array.prototype.reduceRight.call(obj, callbackfn, 1), 1, 'Array.prototype.reduceRight.call(obj, callbackfn, 1)');
assert.sameValue(accessed, false, 'accessed');


text:
/*
 * @author Joshua Koo http://joshuakoo.com
 */

const TIMELINER_VERSION = "2.0.0-dev";

import { UndoManager, UndoState } from './utils/util_undo.js'
import { Dispatcher } from './utils/util_dispatcher.js'
import { Theme } from './theme.js'
import { LayoutConstants as Settings } from './layout_constants.js';
import { utils } from './utils/utils.js'
import { LayerCabinet } from './views/layer_cabinet.js'
import { TimelinePanel } from './views/panel.js'
import { IconButton } from './ui/icon_button.js'
var style = utils.style
var saveToFile = utils.saveToFile
var openAs = utils.openAs
var STORAGE_PREFIX = utils.STORAGE_PREFIX
import { ScrollBar } from './ui/scrollbar.js'
import { DataStore } from './utils/util_datastore.js'
import { DockingWindow } from './utils/docking_window.js'

var Z_INDEX = 999;

function LayerProp(name) {
	this.name = name;
	this.values = [];

	this._value = 0;

	this._color = '#' + (Math.random() * 0xffffff | 0).toString(16);
	/*
	this.max
	this.min
	this.step
	*/
}

function Timeliner(target) {
	// Dispatcher for coordination
	var dispatcher = new Dispatcher();

	// Data
	var data = new DataStore();
	var layer_store = data.get('layers');
	var layers = layer_store.value;

	window._data = data; // expose it for debugging

	// Undo manager
	var undo_manager = new UndoManager(dispatcher);

	// Views
	var timeline = new TimelinePanel(data, dispatcher);
	var layer_panel = new LayerCabinet(data, dispatcher);

	setTimeout(function() {
		// hack!
		undo_manager.save(new UndoState(data, 'Loaded'), true);
	});

	dispatcher.on('keyframe', function(layer, value) {
		var index = layers.indexOf(layer);

		var t = data.get('ui:currentTime').value;
		var v = utils.findTimeinLayer(layer, t);

		// console.log(v, '...keyframe index', index, utils.format_friendly_seconds(t), typeof(v));
		// console.log('layer', layer, value);

		if (typeof(v) === 'number') {
			layer.values.splice(v, 0, {
				time: t,
				value: value,
				_color: '#' + (Math.random() * 0xffffff | 0).toString(16)
			});

			undo_manager.save(new UndoState(data, 'Add Keyframe'));
		} else {
			console.log('remove from index', v);
			layer.values.splice(v.index, 1);

			undo_manager.save(new UndoState(data, 'Remove Keyframe'));
		}

		repaintAll();

	});

	dispatcher.on('keyframe.move', function(layer, value) {
		undo_manager.save(new UndoState(data, 'Move Keyframe'));
	});

	// dispatcher.fire('value.change', layer, me.value);
	dispatcher.on('value.change', function(layer, value, dont_save) {
		if (layer._mute) return;

		var t = data.get('ui:currentTime').value;
		var v = utils.findTimeinLayer(layer, t);

		// console.log(v, 'value.change', layer, value, utils.format_friendly_seconds(t), typeof(v));
		if (typeof(v) === 'number') {
			layer.values.splice(v, 0, {
				time: t,
				value: value,
				_color: '#' + (Math.random() * 0xffffff | 0).toString(16)
			});
			if (!dont_save) undo_manager.save(new UndoState(data, 'Add value'));
		} else {
			v.object.value = value;
			if (!dont_save) undo_manager.save(new UndoState(data, 'Update value'));
		}

		repaintAll();
	});

	dispatcher.on('action:solo', function(layer, solo) {
		layer._solo = solo;

		console.log(layer, solo);

		// When a track is solo-ed, playback only changes values
		// of that layer.
	});

	dispatcher.on('action:mute', function(layer, mute) {
		layer._mute = mute;

		// When a track is mute, playback does not play
		// frames of those muted layers.

		// also feels like hidden feature in photoshop

		// when values are updated, eg. from slider,
		// no tweens will be created.
		// we can decide also to "lock in" layers
		// no changes to tween will be made etc.
	});

	dispatcher.on('ease', function(layer, ease_type) {
		var t = data.get('ui:currentTime').value;
		var v = utils.timeAtLayer(layer, t);
		// console.log('Ease Change > ', layer, value, v);
		if (v && v.entry) {
			v.entry.tween  = ease_type;
		}

		undo_manager.save(new UndoState(data, 'Add Ease'));

		repaintAll();
	});

	var start_play = null,
		played_from = 0; // requires some more tweaking

	dispatcher.on('controls.toggle_play', function() {
		if (start_play) {
			pausePlaying();
		} else {
			startPlaying();
		}
	});

	dispatcher.on('controls.restart_play', function() {
		if (!start_play) {
			startPlaying();
		}

		setCurrentTime(played_from);
	});

	dispatcher.on('controls.play', startPlaying);
	dispatcher.on('controls.pause', pausePlaying);

	function startPlaying() {
		// played_from = timeline.current_frame;
		start_play = performance.now() - data.get('ui:currentTime').value * 1000;
		layer_panel.setControlStatus(true);
		// dispatcher.fire('controls.status', true);
	}

	function pausePlaying() {
		start_play = null;
		layer_panel.setControlStatus(false);
		// dispatcher.fire('controls.status', false);
	}

	dispatcher.on('controls.stop', function() {
		if (start_play !== null) pausePlaying();
		setCurrentTime(0);
	});

	var currentTimeStore = data.get('ui:currentTime');
	dispatcher.on('time.update', setCurrentTime);

	dispatcher.on('totalTime.update', function(value) {
		// context.totalTime = value;
		// controller.setDuration(value);
		// timeline.repaint();
	});

	/* update scroll viewport */
	dispatcher.on('update.scrollTime', function(v) {
		v = Math.max(0, v);
		data.get('ui:scrollTime').value = v;
		repaintAll();
	});

	function setCurrentTime(value) {
		value = Math.max(0, value);
		currentTimeStore.value = value;

		if (start_play) start_play = performance.now() - value * 1000;
		repaintAll();
		// layer_panel.repaint(s);
	}

	dispatcher.on('target.notify', function(name, value) {
		if (target) target[name] = value;
	});

	dispatcher.on('update.scale', function(v) {
		console.log('range', v);
		data.get('ui:timeScale').value = v;

		timeline.repaint();
	});

	// handle undo / redo
	dispatcher.on('controls.undo', function() {
		var history = undo_manager.undo();
		data.setJSONString(history.state);

		updateState();
	});

	dispatcher.on('controls.redo', function() {
		var history = undo_manager.redo();
		data.setJSONString(history.state);

		updateState();
	});

	/*
		Paint Routines
	*/

	function paint() {
		requestAnimationFrame(paint);

		if (start_play) {
			var t = (performance.now() - start_play) / 1000;
			setCurrentTime(t);

			if (t > data.get('ui:totalTime').value) {
				// simple loop
				start_play = performance.now();
			}
		}

		if (needsResize) {
			div.style.width = Settings.width + 'px';
			div.style.height = Settings.height + 'px';

			restyle(layer_panel.dom, timeline.dom);

			timeline.resize();
			repaintAll();
			needsResize = false;

			dispatcher.fire('resize');
		}

		timeline._paint();
	}

	paint();

	/*
		End Paint Routines
	*/

	function save(name) {
		if (!name) name = 'autosave';

		var json = data.getJSONString();

		try {
			localStorage[STORAGE_PREFIX + name] = json;
			dispatcher.fire('save:done');
		} catch (e) {
			console.log('Cannot save', name, json);
		}
	}

	function saveAs(name) {
		if (!name) name = data.get('name').value;
		name = prompt('Pick a name to save to (localStorage)', name);
		if (name) {
			data.data.name = name;
			save(name);
		}
	}

	function saveSimply() {
		var name = data.get('name').value;
		if (name) {
			save(name);
		} else {
			saveAs(name);
		}
	}

	function exportJSON() {
		var json = data.getJSONString();
		var ret = prompt('Hit OK to download otherwise Copy and Paste JSON', json);

		console.log(JSON.stringify(data.data, null, '\t'));
		if (!ret) return;

		// make json downloadable
		json = data.getJSONString('\t');
		var fileName = 'timeliner-test' + '.json';

		saveToFile(json, fileName);
	}

	function loadJSONString(o) {
		// should catch and check errors here
		var json = JSON.parse(o);
		load(json);
	}

	function load(o) {
		data.setJSON(o);
		//
		if (data.getValue('ui') === undefined) {
			data.setValue('ui', {
				currentTime: 0,
				totalTime: Settings.default_length,
				scrollTime: 0,
				timeScale: Settings.time_scale
			});
		}

		undo_manager.clear();
		undo_manager.save(new UndoState(data, 'Loaded'), true);

		updateState();
	}

	function updateState() {
		layers = layer_store.value; // FIXME: support Arrays
		layer_panel.setState(layer_store);
		timeline.setState(layer_store);

		repaintAll();
	}

	function repaintAll() {
		var content_height = layers.length * Settings.LINE_HEIGHT;
		scrollbar.setLength(Settings.TIMELINE_SCROLL_HEIGHT / content_height);

		layer_panel.repaint();
		timeline.repaint();
	}

	function promptImport() {
		var json = prompt('Paste JSON in here to Load');
		if (!json) return;
		console.log('Loading.. ', json);
		loadJSONString(json);
	}

	function open(title) {
		if (title) {
			loadJSONString(localStorage[STORAGE_PREFIX + title]);
		}
	}

	this.openLocalSave = open;

	dispatcher.on('import', function() {
		promptImport();
	}.bind(this));

	dispatcher.on('new', function() {
		data.blank();
		updateState();
	});

	dispatcher.on('openfile', function() {
		openAs(function(data) {
			// console.log('loaded ' + data);
			loadJSONString(data);
		}, div);
	});

	dispatcher.on('open', open);
	dispatcher.on('export', exportJSON);

	dispatcher.on('save', saveSimply);
	dispatcher.on('save_as', saveAs);

	// Expose API
	this.save = save;
	this.load = load;

	/*
		Start DOM Stuff (should separate file)
	*/

	var div = document.createElement('div');
	style(div, {
		textAlign: 'left',
		lineHeight: '1em',
		position: 'absolute',
		top: '22px'
	});

	var pane = document.createElement('div');

	style(pane, {
		position: 'fixed',
		top: '20px',
		left: '20px',
		margin: 0,
		border: '1px solid ' + Theme.a,
		padding: 0,
		overflow: 'hidden',
		backgroundColor: Theme.a,
		color: Theme.d,
		zIndex: Z_INDEX,
		fontFamily: 'monospace',
		fontSize: '12px'
	});

	var header_styles = {
		position: 'absolute',
		top: '0px',
		width: '100%',
		height: '22px',
		lineHeight: '22px',
		overflow: 'hidden'
	};

	var button_styles = {
		width: '20px',
		height: '20px',
		padding: '2px',
		marginRight: '2px'
	};

	var pane_title = document.createElement('div');
	style(pane_title, header_styles, {
		borderBottom: '1px solid ' + Theme.b,
		textAlign: 'center'
	});

	var title_bar = document.createElement('span');
	pane_title.appendChild(title_bar);

	title_bar.innerHTML = 'Timeliner ' + TIMELINER_VERSION;
	pane_title.appendChild(title_bar);

	var top_right_bar = document.createElement('div');
	style(top_right_bar, header_styles, {
		textAlign: 'right'
	});

	pane_title.appendChild(top_right_bar);

	// resize minimize
	// var resize_small = new IconButton(10, 'resize_small', 'minimize', dispatcher);
	// top_right_bar.appendChild(resize_small.dom);

	// resize full
	var resize_full = new IconButton(10, 'resize_full', 'maximize', dispatcher);
	style(resize_full.dom, button_styles, { marginRight: '2px' });
	top_right_bar.appendChild(resize_full.dom);

	var pane_status = document.createElement('div');

	var footer_styles = {
		position: 'absolute',
		width: '100%',
		height: '22px',
		lineHeight: '22px',
		bottom: '0',
		// padding: '2px',
		background: Theme.a,
		fontSize: '11px'
	};

	style(pane_status, footer_styles, {
		borderTop: '1px solid ' + Theme.b,
	});

	pane.appendChild(div);
	pane.appendChild(pane_status);
	pane.appendChild(pane_title);

	var label_status = document.createElement('span');
	label_status.textContent = 'hello!';
	label_status.style.marginLeft = '10px';

	this.setStatus = function(text) {
		label_status.textContent = text;
	};

	dispatcher.on('state:save', function(description) {
		dispatcher.fire('status', description);
		save('autosave');
	});

	dispatcher.on('status', this.setStatus);

	var bottom_right = document.createElement('div');
	style(bottom_right, footer_styles, {
		textAlign: 'right'
	});

	// var button_save = document.createElement('button');
	// style(button_save, button_styles);
	// button_save.textContent = 'Save';
	// button_save.onclick = function() {
	// 	save();
	// };

	// var button_load = document.createElement('button');
	// style(button_load, button_styles);
	// button_load.textContent = 'Import';
	// button_load.onclick = this.promptLoad;

	// var button_open = document.createElement('button');
	// style(button_open, button_styles);
	// button_open.textContent = 'Open';
	// button_open.onclick = this.promptOpen;

	// bottom_right.appendChild(button_load);
	// bottom_right.appendChild(button_save);
	// bottom_right.appendChild(button_open);

	pane_status.appendChild(label_status);
	pane_status.appendChild(bottom_right);

	/**/
	// zoom in
	var zoom_in = new IconButton(12, 'zoom_in', 'zoom in', dispatcher);
	// zoom out
	var zoom_out = new IconButton(12, 'zoom_out', 'zoom out', dispatcher);
	// settings
	var cog = new IconButton(12, 'cog', 'settings', dispatcher);

	// bottom_right.appendChild(zoom_in.dom);
	// bottom_right.appendChild(zoom_out.dom);
	// bottom_right.appendChild(cog.dom);

	// add layer
	var plus = new IconButton(12, 'plus', 'New Layer', dispatcher);
	plus.onClick(function() {
		var name = prompt('Layer name?');
		addLayer(name);

		undo_manager.save(new UndoState(data, 'Layer added'));

		repaintAll();
	});
	style(plus.dom, button_styles);
	bottom_right.appendChild(plus.dom);

	// trash
	var trash = new IconButton(12, 'trash', 'Delete save', dispatcher);
	trash.onClick(function() {
		var name = data.get('name').value;
		if (name && localStorage[STORAGE_PREFIX + name]) {
			var ok = confirm('Are you sure you wish to delete ' + name + '?');
			if (ok) {
				delete localStorage[STORAGE_PREFIX + name];
				dispatcher.fire('status', name + ' deleted');
				dispatcher.fire('save:done');
			}
		}
	});
	style(trash.dom, button_styles, { marginRight: '2px' });
	bottom_right.appendChild(trash.dom);

	// pane_status.appendChild(document.createTextNode(' | TODO <Dock Full | Dock Botton | Snap Window Edges | zoom in | zoom out | Settings | help>'));

	/*
			End DOM Stuff
	*/

	var ghostpane = document.createElement('div');
	ghostpane.id = 'ghostpane';
	style(ghostpane, {
		background: '#999',
		opacity: 0.2,
		position: 'fixed',
		margin: 0,
		padding: 0,
		zIndex: (Z_INDEX - 1),
		// transition: 'all 0.25s ease-in-out',
		transitionProperty: 'top, left, width, height, opacity',
		transitionDuration: '0.25s',
		transitionTimingFunction: 'ease-in-out'
	});

	//
	// Handle DOM Views
	//

	// Shadow Root
	var root = document.createElement('timeliner');
	document.body.appendChild(root);
	if (root.createShadowRoot) root = root.createShadowRoot();

	window.r = root;

	// var iframe = document.createElement('iframe');
	// document.body.appendChild(iframe);
	// root = iframe.contentDocument.body;

	root.appendChild(pane);
	root.appendChild(ghostpane);

	div.appendChild(layer_panel.dom);
	div.appendChild(timeline.dom);

	var scrollbar = new ScrollBar(200, 10);
	div.appendChild(scrollbar.dom);

	// percentages
	scrollbar.onScroll.do(function(type, scrollTo) {
		switch (type) {
		case 'scrollto':
			layer_panel.scrollTo(scrollTo);
			timeline.scrollTo(scrollTo);
			break;
	//		case 'pageup':
	// 			scrollTop -= pageOffset;
	// 			me.draw();
	// 			me.updateScrollbar();
	// 			break;
	// 		case 'pagedown':
	// 			scrollTop += pageOffset;
	// 			me.draw();
	// 			me.updateScrollbar();
	// 			break;
		}
	});

	// document.addEventListener('keypress', function(e) {
	// 	console.log('kp', e);
	// });
	// document.addEventListener('keyup', function(e) {
	// 	if (undo) console.log('UNDO');

	// 	console.log('kd', e);
	// });

	// TODO: Keyboard Shortcuts
	// Esc - Stop and review to last played from / to the start?
	// Space - play / pause from current position
	// Enter - play all
	// k - keyframe

	document.addEventListener('keydown', function(e) {
		var play = e.keyCode == 32; // space
		var enter = e.keyCode == 13; //
		var undo = e.metaKey && e.keyCode == 91 && !e.shiftKey;

		var active = document.activeElement;
		// console.log( active.nodeName );

		if (active.nodeName.match(/(INPUT|BUTTON|SELECT|TIMELINER)/)) {
			active.blur();
		}

		if (play) {
			dispatcher.fire('controls.toggle_play');
		}
		else if (enter) {
			// FIXME: Return should play from the start or last played from?
			dispatcher.fire('controls.restart_play');
			// dispatcher.fire('controls.undo');
		}
		else if (e.keyCode == 27) {
			// Esc = stop. FIXME: should rewind head to last played from or Last pointed from?
			dispatcher.fire('controls.pause');
		}
		else console.log('keydown', e.keyCode);
	});

	var needsResize = true;

	function resize(width, height) {
		// data.get('ui:bounds').value = {
		// 	width: width,
		// 	height: height
		// };
		// TODO: remove ugly hardcodes
		width -= 4;
		height -= 44;

		Settings.width = width - Settings.LEFT_PANE_WIDTH;
		Settings.height = height;

		Settings.TIMELINE_SCROLL_HEIGHT = height - Settings.MARKER_TRACK_HEIGHT;
		var scrollable_height = Settings.TIMELINE_SCROLL_HEIGHT;

		scrollbar.setHeight(scrollable_height - 2);

		style(scrollbar.dom, {
			top: Settings.MARKER_TRACK_HEIGHT + 'px',
			left: (width - 16) + 'px',
		});

		needsResize = true;
	}

	function restyle(left, right) {
		left.style.cssText = 'position: absolute; left: 0px; top: 0px; height: ' + Settings.height + 'px;';
		style(left, {
			// background: Theme.a,
			overflow: 'hidden'
		});
		left.style.width = Settings.LEFT_PANE_WIDTH + 'px';

		// right.style.cssText = 'position: absolute; top: 0px;';
		right.style.position = 'absolute';
		right.style.top = '0px';
		right.style.left = Settings.LEFT_PANE_WIDTH + 'px';
	}

	function addLayer(name) {
		var layer = new LayerProp(name);

		layers = layer_store.value;
		layers.push(layer);

		layer_panel.setState(layer_store);
	}

	this.addLayer = addLayer;

	this.dispose = function dispose() {

		var domParent = pane.parentElement;
		domParent.removeChild(pane);
		domParent.removeChild(ghostpane);

	};

	this.setTarget = function(t) {
		target = t;
	};

	function getValueRanges(ranges, interval) {
		interval = interval ? interval : 0.15;
		ranges = ranges ? ranges : 2;

		// not optimized!
		var t = data.get('ui:currentTime').value;

		var values = [];

		for (var u = -ranges; u <= ranges; u++) {
			// if (u == 0) continue;
			var o = {};

			for (var l = 0; l < layers.length; l++) {
				var layer = layers[l];
				var m = utils.timeAtLayer(layer, t + u * interval);
				o[layer.name] = m.value;
			}

			values.push(o);

		}

		return values;
	}

	this.getValues = getValueRanges;

	/* Integrate pane into docking window */
	var widget = new DockingWindow(pane, ghostpane)
	widget.allowMove(false);
	widget.resizes.do(resize)

	pane_title.addEventListener('mouseover', function() {
		widget.allowMove(true);
	});

	pane_title.addEventListener('mouseout', function() {
		widget.allowMove(false);
	});
}

window.Timeliner = Timeliner;

export { Timeliner }


text:
layout: page
title: "Install Zeppelin"
description: ""
group: install
---

{% include JB/setup %}

## From binary package

   Download latest binary package from [Download](../download.html).

## Build from source

   Check instructions in [README](https://github.com/apache/incubator-zeppelin/blob/master/README.md) to build from source.

## Configure

Configuration can be done by both environment variable(conf/zeppelin-env.sh) and java properties(conf/zeppelin-site.xml). If both defined, environment vaiable is used.

    zepplin-env.sh
    zepplin-site.xml
    Default value
    Description

    ZEPPELIN_PORT
    zeppelin.server.port
    8080
    Zeppelin server port.

    ZEPPELIN_MEM
    N/A
    -Xmx1024m -XX:MaxPermSize=512m
    JVM mem options

    ZEPPELIN_INTP_MEM
    N/A
    ZEPPELIN_MEM
    JVM mem options for interpreter process

    ZEPPELIN_JAVA_OPTS
    N/A

    JVM Options

    ZEPPELIN_ALLOWED_ORIGINS
    zeppelin.server.allowed.origins
    *
    Allows a way to specify a ',' separated list of allowed origins for rest and websockets. i.e. http://localhost:8080

    ZEPPELIN_SERVER_CONTEXT_PATH
    zeppelin.server.context.path
    /
    Context Path of the Web Application

    ZEPPELIN_SSL
    zeppelin.ssl
    false

    ZEPPELIN_SSL_CLIENT_AUTH
    zeppelin.ssl.client.auth
    false

    ZEPPELIN_SSL_KEYSTORE_PATH
    zeppelin.ssl.keystore.path
    keystore

    ZEPPELIN_SSL_KEYSTORE_TYPE
    zeppelin.ssl.keystore.type
    JKS

    ZEPPELIN_SSL_KEYSTORE_PASSWORD
    zeppelin.ssl.keystore.password

    ZEPPELIN_SSL_KEY_MANAGER_PASSWORD
    zeppelin.ssl.key.manager.password

    ZEPPELIN_SSL_TRUSTSTORE_PATH
    zeppelin.ssl.truststore.path

    ZEPPELIN_SSL_TRUSTSTORE_TYPE
    zeppelin.ssl.truststore.type

    ZEPPELIN_SSL_TRUSTSTORE_PASSWORD
    zeppelin.ssl.truststore.password

    ZEPPELIN_NOTEBOOK_HOMESCREEN
    zeppelin.notebook.homescreen

    Id of notebook to be displayed in homescreen ex) 2A94M5J1Z

    ZEPPELIN_NOTEBOOK_HOMESCREEN_HIDE
    zeppelin.notebook.homescreen.hide
    false
    hide homescreen notebook from list when this value set to "true"

    ZEPPELIN_NOTEBOOK_DIR
    zeppelin.notebook.dir
    notebook
    Where notebook file is saved

    ZEPPELIN_NOTEBOOK_S3_BUCKET
    zeppelin.notebook.s3.bucket
    zeppelin
    Bucket where notebook saved

    ZEPPELIN_NOTEBOOK_S3_USER
    zeppelin.notebook.s3.user
    user
    User in bucket where notebook saved. For example bucket/user/notebook/2A94M5J1Z/note.json

    ZEPPELIN_NOTEBOOK_STORAGE
    zeppelin.notebook.storage
    org.apache.zeppelin.notebook.repo.VFSNotebookRepo
    Comma separated list of notebook storage

    ZEPPELIN_NOTEBOOK_RELOAD_FROM_STORAGE
    zeppelin.notebook.reloadAllNotesFromStorage
    false
    Notebook list and contents will be always loaded from repository if set true. If set false, modified notebooks or new notebooks added on file system level won't be reflected on Zeppelin till user restarts Zeppelin.

    ZEPPELIN_INTERPRETERS
    zeppelin.interpreters
  <description></description>
    org.apache.zeppelin.spark.SparkInterpreter,
org.apache.zeppelin.spark.PySparkInterpreter,
org.apache.zeppelin.spark.SparkSqlInterpreter,
org.apache.zeppelin.spark.DepInterpreter,
org.apache.zeppelin.markdown.Markdown,
org.apache.zeppelin.shell.ShellInterpreter,
org.apache.zeppelin.hive.HiveInterpreter

    ...

    Comma separated interpreter configurations [Class]. First interpreter become a default

    ZEPPELIN_INTERPRETER_DIR
    zeppelin.interpreter.dir
    interpreter
    Zeppelin interpreter directory

You'll also need to configure individual interpreter. Information can be found in 'Interpreter' section in this documentation.

For example [Spark](../interpreter/spark.html).

## Start/Stop
#### Start Zeppelin

```
bin/zeppelin-daemon.sh start
```
After successful start, visit http://localhost:8080 with your web browser.

#### Stop Zeppelin

```
bin/zeppelin-daemon.sh stop
```


text:
package integration

// These are test helpers that:
//
// - Accept *testing.T arguments (see helpers.go)
// - Are used in multiple tests
// - Must not compare test values

import (
	"bufio"
	"bytes"
	"context"
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"testing"
	"time"

	"github.com/docker/machine/libmachine/state"
	"github.com/google/go-cmp/cmp"
	"github.com/shirou/gopsutil/v3/process"
	core "k8s.io/api/core/v1"
	meta "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/minikube/pkg/kapi"
)

// RunResult stores the result of an cmd.Run call
type RunResult struct {
	Stdout   *bytes.Buffer
	Stderr   *bytes.Buffer
	ExitCode int
	Args     []string
}

// Command returns a human readable command string that does not induce eye fatigue
func (rr RunResult) Command() string {
	var sb strings.Builder
	sb.WriteString(strings.TrimPrefix(rr.Args[0], "../../"))
	for _, a := range rr.Args[1:] {
		if strings.Contains(a, " ") {
			sb.WriteString(fmt.Sprintf(` "%s"`, a))
			continue
		}
		sb.WriteString(fmt.Sprintf(" %s", a))
	}
	return sb.String()
}

// indentLines indents every line in a bytes.Buffer and returns it as string
func indentLines(b []byte) string {
	scanner := bufio.NewScanner(bytes.NewReader(b))
	var lines string
	for scanner.Scan() {
		lines = lines + "\t" + scanner.Text() + "\n"
	}
	return lines
}

// Output returns human-readable output for an execution result
func (rr RunResult) Output() string {
	var sb strings.Builder
	if rr.Stdout.Len() > 0 {
		sb.WriteString(fmt.Sprintf("\n-- stdout --\n%s\n-- /stdout --", indentLines(rr.Stdout.Bytes())))
	}
	if rr.Stderr.Len() > 0 {
		sb.WriteString(fmt.Sprintf("\n** stderr ** \n%s\n** /stderr **", indentLines(rr.Stderr.Bytes())))
	}
	return sb.String()
}

// Run is a test helper to log a command being executed ¯\_(ツ)_/¯
func Run(t *testing.T, cmd *exec.Cmd) (*RunResult, error) {
	t.Helper()
	rr := &RunResult{Args: cmd.Args}
	t.Logf("(dbg) Run:  %v", rr.Command())

	var outb, errb bytes.Buffer
	cmd.Stdout, rr.Stdout = &outb, &outb
	cmd.Stderr, rr.Stderr = &errb, &errb
	start := time.Now()
	err := cmd.Run()
	elapsed := time.Since(start)
	if err == nil {
		// Reduce log spam
		if elapsed > (1 * time.Second) {
			t.Logf("(dbg) Done: %v: (%s)", rr.Command(), elapsed)
		}
	} else {
		if exitError, ok := err.(*exec.ExitError); ok {
			rr.ExitCode = exitError.ExitCode()
		}
		t.Logf("(dbg) Non-zero exit: %v: %v (%s)\n%s", rr.Command(), err, elapsed, rr.Output())
	}
	return rr, err
}

// StartSession stores the result of an cmd.Start call
type StartSession struct {
	Stdout *bufio.Reader
	Stderr *bufio.Reader
	cmd    *exec.Cmd
}

// Start starts a process in the background, streaming output
func Start(t *testing.T, cmd *exec.Cmd) (*StartSession, error) {
	t.Helper()
	t.Logf("(dbg) daemon: %v", cmd.Args)

	stdoutPipe, err := cmd.StdoutPipe()
	if err != nil {
		t.Fatalf("stdout pipe failed: %v %v", cmd.Args, err)
	}
	stderrPipe, err := cmd.StderrPipe()
	if err != nil {
		t.Fatalf("stderr pipe failed: %v %v", cmd.Args, err)
	}

	sr := &StartSession{Stdout: bufio.NewReader(stdoutPipe), Stderr: bufio.NewReader(stderrPipe), cmd: cmd}
	return sr, cmd.Start()
}

// Stop stops the started process
func (ss *StartSession) Stop(t *testing.T) {
	t.Helper()
	t.Logf("(dbg) stopping %s ...", ss.cmd.Args)
	if ss.cmd.Process == nil {
		t.Logf("%s has a nil Process. Maybe it's dead? How weird!", ss.cmd.Args)
		return
	}
	killProcessFamily(t, ss.cmd.Process.Pid)
	if t.Failed() {
		if ss.Stdout.Size() > 0 {
			stdout, err := io.ReadAll(ss.Stdout)
			if err != nil {
				t.Logf("read stdout failed: %v", err)
			}
			t.Logf("(dbg) %s stdout:\n%s", ss.cmd.Args, stdout)
		}
		if ss.Stderr.Size() > 0 {
			stderr, err := io.ReadAll(ss.Stderr)
			if err != nil {
				t.Logf("read stderr failed: %v", err)
			}
			t.Logf("(dbg) %s stderr:\n%s", ss.cmd.Args, stderr)
		}
	}
}

// Cleanup cleans up after a test run
func Cleanup(t *testing.T, profile string, cancel context.CancelFunc) {
	// No helper because it makes the call log confusing.
	if *cleanup {
		t.Logf("Cleaning up %q profile ...", profile)
		ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
		defer cancel()
		_, err := Run(t, exec.CommandContext(ctx, Target(), "delete", "-p", profile))
		if err != nil {
			t.Logf("failed cleanup: %v", err)
		}
	} else {
		t.Logf("skipping cleanup of %s (--cleanup=false)", profile)
	}
	cancel()
}

// CleanupWithLogs cleans up after a test run, fetching logs and deleting the profile
func CleanupWithLogs(t *testing.T, profile string, cancel context.CancelFunc) {
	t.Helper()
	if !t.Failed() {
		Cleanup(t, profile, cancel)
		return
	}

	t.Logf("*** %s FAILED at %s", t.Name(), time.Now())
	PostMortemLogs(t, profile)
	Cleanup(t, profile, cancel)
}

// PostMortemLogs shows logs for debugging a failed cluster
func PostMortemLogs(t *testing.T, profile string, multinode ...bool) {
	if !t.Failed() {
		return
	}

	if !*postMortemLogs {
		t.Logf("post-mortem logs disabled, oh well!")
		return
	}

	m := false
	if len(multinode) > 0 {
		m = multinode[0]
	}

	nodes := []string{profile}
	if m {
		nodes = append(nodes, SecondNodeName, ThirdNodeName)
	}

	t.Logf("-----------------------post-mortem--------------------------------")

	for _, n := range nodes {
		machine := profile
		if n != profile {
			machine = fmt.Sprintf("%s-%s", profile, n)
		}
		if DockerDriver() {
			t.Logf("======>  post-mortem[%s]: docker inspect <======", t.Name())
			rr, err := Run(t, exec.Command("docker", "inspect", machine))
			if err != nil {
				t.Logf("failed to get docker inspect: %v", err)
			} else {
				t.Logf("(dbg) %s:\n%s", rr.Command(), rr.Output())
			}
		}

		st := Status(context.Background(), t, Target(), profile, "Host", n)
		if st != state.Running.String() {
			t.Logf("%q host is not running, skipping log retrieval (state=%q)", profile, st)
			return
		}
		t.Logf("<<< %s FAILED: start of post-mortem logs <<<", t.Name())
		t.Logf("======>  post-mortem[%s]: minikube logs <======", t.Name())

		rr, err := Run(t, exec.Command(Target(), "-p", profile, "logs", "-n", "25"))
		if err != nil {
			t.Logf("failed logs error: %v", err)
			return
		}
		t.Logf("%s logs: %s", t.Name(), rr.Output())

		st = Status(context.Background(), t, Target(), profile, "APIServer", n)
		if st != state.Running.String() {
			t.Logf("%q apiserver is not running, skipping kubectl commands (state=%q)", profile, st)
			return
		}

		// Get non-running pods. NOTE: This does not yet contain pods which are "running", but not "ready"
		rr, rerr := Run(t, exec.Command("kubectl", "--context", profile, "get", "po", "-o=jsonpath={.items[*].metadata.name}", "-A", "--field-selector=status.phase!=Running"))
		if rerr != nil {
			t.Logf("%s: %v", rr.Command(), rerr)
			return
		}
		notRunning := strings.Split(rr.Stdout.String(), " ")
		if len(notRunning) == 0 {
			continue
		}
		t.Logf("non-running pods: %s", strings.Join(notRunning, " "))

		t.Logf("======> post-mortem[%s]: describe non-running pods <======", t.Name())

		args := append([]string{"--context", profile, "describe", "pod"}, notRunning...)
		rr, rerr = Run(t, exec.Command("kubectl", args...))
		if rerr != nil {
			t.Logf("%s: %v", rr.Command(), rerr)
			return
		}
		t.Logf("(dbg) %s:\n%s", rr.Command(), rr.Output())
	}

	t.Logf("<<< %s FAILED: end of post-mortem logs <<<", t.Name())
	t.Logf("---------------------/post-mortem---------------------------------")
}

// podStatusMsg returns a human-readable pod status, for generating debug status
func podStatusMsg(pod core.Pod) string {
	var sb strings.Builder
	sb.WriteString(fmt.Sprintf("%q [%s] %s", pod.ObjectMeta.GetName(), pod.ObjectMeta.GetUID(), pod.Status.Phase))
	for i, c := range pod.Status.Conditions {
		if c.Reason != "" {
			if i == 0 {
				sb.WriteString(": ")
			} else {
				sb.WriteString(" / ")
			}
			sb.WriteString(fmt.Sprintf("%s:%s", c.Type, c.Reason))
		}
		if c.Message != "" {
			sb.WriteString(fmt.Sprintf(" (%s)", c.Message))
		}
	}
	return sb.String()
}

// PodWait waits for pods to achieve a running state.
func PodWait(ctx context.Context, t *testing.T, profile string, ns string, selector string, timeout time.Duration) ([]string, error) {
	t.Helper()
	client, err := kapi.Client(profile)
	if err != nil {
		return nil, err
	}

	// For example: kubernetes.io/minikube-addons=gvisor
	listOpts := meta.ListOptions{LabelSelector: selector}
	minUptime := 5 * time.Second
	podStart := time.Time{}
	foundNames := map[string]bool{}
	lastMsg := ""

	start := time.Now()
	t.Logf("(dbg) %s: waiting %s for pods matching %q in namespace %q ...", t.Name(), timeout, selector, ns)
	f := func() (bool, error) {
		pods, err := client.CoreV1().Pods(ns).List(ctx, listOpts)
		if err != nil {
			t.Logf("%s: WARNING: pod list for %q %q returned: %v", t.Name(), ns, selector, err)
			// Don't return the error upwards so that this is retried, in case the apiserver is rescheduled
			podStart = time.Time{}
			return false, nil
		}
		if len(pods.Items) == 0 {
			podStart = time.Time{}
			return false, nil
		}

		for _, pod := range pods.Items {
			foundNames[pod.ObjectMeta.Name] = true
			msg := podStatusMsg(pod)
			// Prevent spamming logs with identical messages
			if msg != lastMsg {
				t.Log(msg)
				lastMsg = msg
			}
			// Successful termination of a short-lived process, will not be restarted
			if pod.Status.Phase == core.PodSucceeded {
				return true, nil
			}
			// Long-running process state
			if pod.Status.Phase != core.PodRunning {
				if !podStart.IsZero() {
					t.Logf("%s: WARNING: %s was running %s ago - may be unstable", t.Name(), selector, time.Since(podStart))
				}
				podStart = time.Time{}
				return false, nil
			}

			if podStart.IsZero() {
				podStart = time.Now()
			}

			if time.Since(podStart) > minUptime {
				return true, nil
			}
		}
		return false, nil
	}

	err = wait.PollImmediate(1*time.Second, timeout, f)
	names := []string{}
	for n := range foundNames {
		names = append(names, n)
	}

	if err == nil {
		t.Logf("(dbg) %s: %s healthy within %s", t.Name(), selector, time.Since(start))
		return names, nil
	}

	t.Logf("***** %s: pod %q failed to start within %s: %v ****", t.Name(), selector, timeout, err)
	showPodLogs(ctx, t, profile, ns, names)
	return names, fmt.Errorf("%s: %v", fmt.Sprintf("%s within %s", selector, timeout), err)
}

// PVCWait waits for persistent volume claim to reach bound state
func PVCWait(ctx context.Context, t *testing.T, profile string, ns string, name string, timeout time.Duration) error {
	t.Helper()

	t.Logf("(dbg) %s: waiting %s for pvc %q in namespace %q ...", t.Name(), timeout, name, ns)

	f := func() (bool, error) {
		ret, err := Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "get", "pvc", name, "-o", "jsonpath={.status.phase}", "-n", ns))
		if err != nil {
			t.Logf("%s: WARNING: PVC get for %q %q returned: %v", t.Name(), ns, name, err)
			return false, nil
		}

		pvc := strings.TrimSpace(ret.Stdout.String())
		if pvc == string(core.ClaimBound) {
			return true, nil
		} else if pvc == string(core.ClaimLost) {
			return true, fmt.Errorf("PVC %q is LOST", name)
		}
		return false, nil
	}

	return wait.PollImmediate(1*time.Second, timeout, f)
}

// VolumeSnapshotWait waits for volume snapshot to be ready to use
func VolumeSnapshotWait(ctx context.Context, t *testing.T, profile string, ns string, name string, timeout time.Duration) error {
	t.Helper()

	t.Logf("(dbg) %s: waiting %s for volume snapshot %q in namespace %q ...", t.Name(), timeout, name, ns)

	f := func() (bool, error) {
		res, err := Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "get", "volumesnapshot", name, "-o", "jsonpath={.status.readyToUse}", "-n", ns))
		if err != nil {
			t.Logf("%s: WARNING: volume snapshot get for %q %q returned: %v", t.Name(), ns, name, err)
			return false, nil
		}

		isReady, err := strconv.ParseBool(strings.TrimSpace(res.Stdout.String()))
		if err != nil {
			t.Logf("%s: WARNING: volume snapshot get for %q %q returned: %v", t.Name(), ns, name, res.Stdout.String())
			return false, nil
		}

		return isReady, nil
	}

	return wait.PollImmediate(1*time.Second, timeout, f)
}

// Status returns a minikube component status as a string
func Status(ctx context.Context, t *testing.T, path string, profile string, key string, node string) string {
	t.Helper()
	// Reminder of useful keys: "Host", "Kubelet", "APIServer"
	rr, err := Run(t, exec.CommandContext(ctx, path, "status", fmt.Sprintf("--format={{.%s}}", key), "-p", profile, "-n", node))
	if err != nil {
		t.Logf("status error: %v (may be ok)", err)
	}
	return strings.TrimSpace(rr.Stdout.String())
}

// showPodLogs logs debug info for pods
func showPodLogs(ctx context.Context, t *testing.T, profile string, ns string, names []string) {
	t.Helper()
	st := Status(context.Background(), t, Target(), profile, "APIServer", profile)
	if st != state.Running.String() {
		t.Logf("%q apiserver is not running, skipping kubectl commands (state=%q)", profile, st)
		return
	}

	t.Logf("%s: showing logs for failed pods as of %s", t.Name(), time.Now())

	for _, name := range names {
		rr, err := Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "describe", "po", name, "-n", ns))
		if err != nil {
			t.Logf("%s: %v", rr.Command(), err)
		} else {
			t.Logf("(dbg) %s:\n%s", rr.Command(), rr.Stdout)
		}

		rr, err = Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "logs", name, "-n", ns))
		if err != nil {
			t.Logf("%s: %v", rr.Command(), err)
		} else {
			t.Logf("(dbg) %s:\n%s", rr.Command(), rr.Stdout)
		}
	}
}

// MaybeParallel sets that the test should run in parallel
func MaybeParallel(t *testing.T) {
	t.Helper()
	// TODO: Allow paralellized tests on "none" that do not require independent clusters
	if NoneDriver() {
		return
	}
	t.Parallel()
}

// killProcessFamily kills a pid and all of its children
func killProcessFamily(t *testing.T, pid int) {
	parent, err := process.NewProcess(int32(pid))
	if err != nil {
		t.Logf("unable to find parent, assuming dead: %v", err)
		return
	}
	procs := []*process.Process{}
	children, err := parent.Children()
	if err == nil {
		procs = append(procs, children...)
	}
	procs = append(procs, parent)

	for _, p := range procs {
		if err := p.Terminate(); err != nil {
			t.Logf("unable to terminate pid %d: %v", p.Pid, err)
			continue
		}
		// Allow process a chance to cleanup before instant death.
		time.Sleep(100 * time.Millisecond)
		if err := p.Kill(); err != nil {
			t.Logf("unable to kill pid %d: %v", p.Pid, err)
			continue
		}
	}
}

// cpTestMinikubePath is where the test file will be located in the Minikube instance
func cpTestMinikubePath() string {
	return "/home/docker/cp-test.txt"
}

// cpTestLocalPath is where the test file located in host os
func cpTestLocalPath() string {
	return filepath.Join(*testdataDir, "cp-test.txt")
}

func cpTestReadText(ctx context.Context, t *testing.T, profile, node, path string) string {
	if node == "" {
		expected, err := os.ReadFile(path)
		if err != nil {
			t.Errorf("failed to read test file 'testdata/cp-test.txt' : %v", err)
		}
		return string(expected)
	}

	sshArgv := []string{"-p", profile, "ssh", "-n", node, fmt.Sprintf("sudo cat %s", path)}
	rr, err := Run(t, exec.CommandContext(ctx, Target(), sshArgv...))
	if ctx.Err() == context.DeadlineExceeded {
		t.Errorf("failed to run command by deadline. exceeded timeout : %s", rr.Command())
	}
	if err != nil {
		t.Errorf("failed to run an cp command. args %q : %v", rr.Command(), err)
	}

	return rr.Stdout.String()
}

func cpTestMergePath(node, path string) string {
	if node == "" {
		return path
	}

	return fmt.Sprintf("%s:%s", node, path)
}

// testCpCmd ensures copy functionality into minikube instance.
func testCpCmd(ctx context.Context, t *testing.T, profile string, srcNode, srcPath, dstNode, dstPath string) {
	cpArgv := []string{"-p", profile, "cp", cpTestMergePath(srcNode, srcPath), cpTestMergePath(dstNode, dstPath)}
	rr, err := Run(t, exec.CommandContext(ctx, Target(), cpArgv...))
	if ctx.Err() == context.DeadlineExceeded {
		t.Errorf("failed to run command by deadline. exceeded timeout : %s", rr.Command())
	}
	if err != nil {
		t.Errorf("failed to run an cp command. args %q : %v", rr.Command(), err)
	}

	expected := cpTestReadText(ctx, t, profile, srcNode, srcPath)
	var copiedText string
	if srcNode == "" && dstNode == "" {
		copiedText = cpTestReadText(ctx, t, profile, profile, dstPath)
	} else {
		copiedText = cpTestReadText(ctx, t, profile, dstNode, dstPath)
	}

	if diff := cmp.Diff(expected, copiedText); diff != "" {
		t.Errorf("/testdata/cp-test.txt content mismatch (-want +got):\n%s", diff)
	}
}

// CopyFile copies the specified source file to the specified destination file.
// Specify true for overwrite to overwrite the destination file if it already exits.
func CopyFile(src, dst string, overwrite bool) error {
	srcFile, err := os.Open(src)
	if err != nil {
		return err
	}
	defer srcFile.Close()

	if !overwrite {
		// check if the file exists, if it does then return an error
		_, err := os.Stat(dst)
		if err != nil && !os.IsNotExist(err) {
			return errors.New("won't overwrite destination file")
		}
	}

	dstFile, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer dstFile.Close()

	_, err = io.Copy(dstFile, srcFile)
	if err != nil {
		return err
	}

	// flush the buffer
	err = dstFile.Sync()
	if err != nil {
		return err
	}

	// copy file permissions
	srcInfo, err := os.Stat(src)
	if err != nil {
		return err
	}

	err = os.Chmod(dst, srcInfo.Mode())
	if err != nil {
		return err
	}

	return nil
}

// CopyDir recursively copies the specified source directory tree to the
// specified destination.  The destination directory must not exist.  Any
// symlinks under src are ignored.
func CopyDir(src, dst string) error {
	src = filepath.Clean(src)
	dst = filepath.Clean(dst)

	// verify that src is a directory
	srcInfo, err := os.Stat(src)
	if err != nil {
		return err
	}
	if !srcInfo.IsDir() {
		return fmt.Errorf("source is not a directory")
	}

	// now verify that dst doesn't exist
	_, err = os.Stat(dst)
	if err != nil && !os.IsNotExist(err) {
		return err
	}
	if err == nil {
		return fmt.Errorf("destination directory already exists")
	}

	err = os.MkdirAll(dst, srcInfo.Mode())
	if err != nil {
		return err
	}

	// get the collection of directory entries under src.
	// for each entry build its corresponding path under dst.
	entries, err := os.ReadDir(src)
	if err != nil {
		return err
	}

	for _, entry := range entries {
		// skip symlinks
		entryInfo, err := entry.Info()
		if err != nil {
			return err
		}
		if entryInfo.Mode()&os.ModeSymlink != 0 {
			continue
		}

		srcPath := filepath.Join(src, entry.Name())
		dstPath := filepath.Join(dst, entry.Name())

		if entry.IsDir() {
			err = CopyDir(srcPath, dstPath)
			if err != nil {
				return err
			}
		} else {
			err = CopyFile(srcPath, dstPath, true)
			if err != nil {
				return err
			}
		}
	}

	return nil
}


text:
const DOMNode  = require('../../../../../lib/dom').DOMNode;
const Toolbar  = require('../../../../../lib/components/Toolbar').Toolbar;
const Button   = require('../../../../../lib/components/input/Button').Button;
const Slider   = require('../../../../../lib/components/input/Slider').Slider;
const tabIndex = require('../../../../tabIndex');

exports.ToolbarBottom = class extends Toolbar {
    constructor(opts) {
        super(opts);
        this._ui          = opts.ui;
        this._parentNode  = opts.parentNode;
        this._soundEditor = opts.soundEditor;
        this.initDOM();
    }

    initDOM() {
        let soundEditor = this._soundEditor;
        this.create(
            this._parentNode,
            {
                className: 'flt max-w resource-options bottom',
                children: [
                    this.addFileSaved(soundEditor),
                    this.addCursorInfo(soundEditor),
                    this.addZoom(soundEditor, tabIndex.SOUND_ZOOM, 0)
                ]
            }
        );
    }
};


text:
title: 21st Lesson Review
layout: post
author: dhilan.nathwani
permalink: /21st-lesson-review/
source-id: 1ih0u6ppIikLpjY6I7--_wEr99aUMyzlzm5ywBs8VvH4
published: true
---
** 21st Lesson Review 15/07/2016**

**Summary:** In our final IT lesson of the year Mr Keen talked to us about propaganda. I already knew a bit about this topic but Mr Keen definitely increased my knowledge of propaganda. He gave us examples of where propaganda is used such as a deodorant ad which had Lewis Hamilton in it. People think that if they use that deodorant they will be good at formula 1, which is not true.

**What I learned:** I learned techniques of propaganda and I learned that propaganda is all around us.

**What I could have improved on:** Because I had a bit of knowledge about propaganda I should have shared it with the class.


text:
create class tb1 ( a list(char(2)));
create class tb2 ( b list(varchar(2)));

insert into tb1 values({'a1'});
insert into tb1 values({'a2'});
insert into tb1 values({'a3'});

insert into tb2 values({'a1'});
insert into tb2 values({'a2'});
insert into tb2 values({'a3'});
insert into tb2 values({'a4'});
insert into tb2 values({'a5'});

select a,b from tb1, tb2 where a subset b order by 1,2;

drop class tb1;
drop class tb2;


text:
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#if defined __linux__ && !defined ANDROID

#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdarg.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <termios.h>
#include <unistd.h>

#include <winpr/crt.h>
#include <winpr/comm.h>
#include <winpr/tchar.h>
#include <winpr/wlog.h>

#include "comm_ioctl.h"

/**
 * Communication Resources:
 * http://msdn.microsoft.com/en-us/library/windows/desktop/aa363196/
 */

#include "comm.h"

static wLog *_Log = NULL;

struct comm_device
{
	LPTSTR name;
	LPTSTR path;
};

typedef struct comm_device COMM_DEVICE;

/* FIXME: get a clever data structure, see also io.h functions */
/* _CommDevices is a NULL-terminated array with a maximun of COMM_DEVICE_MAX COMM_DEVICE */
#define COMM_DEVICE_MAX	128
static COMM_DEVICE **_CommDevices = NULL;
static CRITICAL_SECTION _CommDevicesLock;

static HANDLE_CREATOR *_CommHandleCreator = NULL;

static pthread_once_t _CommInitialized = PTHREAD_ONCE_INIT;

static int CommGetFd(HANDLE handle)
{
	WINPR_COMM *comm = (WINPR_COMM *)handle;

	if (!CommIsHandled(handle))
		return -1;

	return comm->fd;
}

static void _CommInit()
{
	/* NB: error management to be done outside of this function */

	assert(_Log == NULL);
	assert(_CommDevices == NULL);
	assert(_CommHandleCreator == NULL);

	_CommDevices = (COMM_DEVICE**)calloc(COMM_DEVICE_MAX+1, sizeof(COMM_DEVICE*));
	if (!_CommDevices)
		return;

	if (!InitializeCriticalSectionEx(&_CommDevicesLock, 0, 0))
	{
		free(_CommDevices);
		_CommDevices = NULL;
		return;
	}

	_CommHandleCreator = (HANDLE_CREATOR*)malloc(sizeof(HANDLE_CREATOR));
	if (!_CommHandleCreator)
	{
		DeleteCriticalSection(&_CommDevicesLock);
		free(_CommDevices);
		_CommDevices = NULL;
		return;
	}

	_CommHandleCreator->IsHandled = IsCommDevice;
	_CommHandleCreator->CreateFileA = CommCreateFileA;

	if (!RegisterHandleCreator(_CommHandleCreator))
	{
		DeleteCriticalSection(&_CommDevicesLock);
		free(_CommDevices);
		free(_CommHandleCreator);
		_CommDevices = NULL;
		_CommHandleCreator = NULL;
		return;
	}
	_Log = WLog_Get("com.winpr.comm");
	assert(_Log != NULL);
}

/**
 * Returns TRUE when the comm module is correctly intialized, FALSE otherwise
 * with ERROR_DLL_INIT_FAILED set as the last error.
 */
static BOOL CommInitialized()
{
	if (pthread_once(&_CommInitialized, _CommInit) != 0)
	{
		SetLastError(ERROR_DLL_INIT_FAILED);
		return FALSE;
	}

	if (_CommHandleCreator == NULL)
	{
		SetLastError(ERROR_DLL_INIT_FAILED);
		return FALSE;
	}

	return TRUE;
}

void CommLog_Print(int level, char *fmt, ...)
{
	if (!CommInitialized())
		return;

	va_list ap;
	va_start(ap, fmt);
	WLog_PrintVA(_Log, level, fmt, ap);
	va_end(ap);
}

BOOL BuildCommDCBA(LPCSTR lpDef, LPDCB lpDCB)
{
	if (!CommInitialized())
		return FALSE;

	/* TODO: not implemented */

	return TRUE;
}

BOOL BuildCommDCBW(LPCWSTR lpDef, LPDCB lpDCB)
{
	if (!CommInitialized())
		return FALSE;

	/* TODO: not implemented */

	return TRUE;
}

BOOL BuildCommDCBAndTimeoutsA(LPCSTR lpDef, LPDCB lpDCB, LPCOMMTIMEOUTS lpCommTimeouts)
{
	if (!CommInitialized())
		return FALSE;

	/* TODO: not implemented */

	return TRUE;
}

BOOL BuildCommDCBAndTimeoutsW(LPCWSTR lpDef, LPDCB lpDCB, LPCOMMTIMEOUTS lpCommTimeouts)
{
	if (!CommInitialized())
		return FALSE;

	/* TODO: not implemented */

	return TRUE;
}

BOOL CommConfigDialogA(LPCSTR lpszName, HWND hWnd, LPCOMMCONFIG lpCC)
{
	if (!CommInitialized())
		return FALSE;

	/* TODO: not implemented */

	return TRUE;
}

BOOL CommConfigDialogW(LPCWSTR lpszName, HWND hWnd, LPCOMMCONFIG lpCC)
{
	if (!CommInitialized())
		return FALSE;

	/* TODO: not implemented */

	return TRUE;
}

BOOL GetCommConfig(HANDLE hCommDev, LPCOMMCONFIG lpCC, LPDWORD lpdwSize)
{
	WINPR_COMM* pComm = (WINPR_COMM*) hCommDev;

	if (!CommInitialized())
		return FALSE;

	/* TODO: not implemented */

	if (!pComm)
		return FALSE;

	return TRUE;
}

BOOL SetCommConfig(HANDLE hCommDev, LPCOMMCONFIG lpCC, DWORD dwSize)
{
	WINPR_COMM* pComm = (WINPR_COMM*) hCommDev;

	if (!CommInitialized())
		return FALSE;

	/* TODO: not implemented */

	if (!pComm)
		return FALSE;

	return TRUE;
}

BOOL GetCommMask(HANDLE hFile, PDWORD lpEvtMask)
{
	WINPR_COMM* pComm = (WINPR_COMM*) hFile;

	if (!CommInitialized())
		return FALSE;

	/* TODO: not implemented */

	if (!pComm)
		return FALSE;

	return TRUE;
}

BOOL SetCommMask(HANDLE hFile, DWORD dwEvtMask)
{
	WINPR_COMM* pComm = (WINPR_COMM*) hFile;

	if (!CommInitialized())
		return FALSE;

	/* TODO: not implemented */

	if (!pComm)
		return FALSE;

	return TRUE;
}

BOOL GetCommModemStatus(HANDLE hFile, PDWORD lpModemStat)
{
	WINPR_COMM* pComm = (WINPR_COMM*) hFile;

	if (!CommInitialized())
		return FALSE;

	/* TODO: not implemented */

	if (!pComm)
		return FALSE;

	return TRUE;
}

/**
 * ERRORS:
 *   ERROR_DLL_INIT_FAILED
 *   ERROR_INVALID_HANDLE
 */
BOOL GetCommProperties(HANDLE hFile, LPCOMMPROP lpCommProp)
{
	WINPR_COMM* pComm = (WINPR_COMM*) hFile;
	DWORD bytesReturned;

	if (!CommInitialized())
		return FALSE;

	if (!pComm || pComm->Type != HANDLE_TYPE_COMM || !pComm->fd )
	{
		SetLastError(ERROR_INVALID_HANDLE);
		return FALSE;
	}

	if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_PROPERTIES, NULL, 0, lpCommProp, sizeof(COMMPROP), &bytesReturned, NULL))
	{
		CommLog_Print(WLOG_WARN, "GetCommProperties failure.");
		return FALSE;
	}

	return TRUE;
}

/**
 *
 *
 * ERRORS:
 *   ERROR_INVALID_HANDLE
 *   ERROR_INVALID_DATA
 *   ERROR_IO_DEVICE
 *   ERROR_OUTOFMEMORY
 */
BOOL GetCommState(HANDLE hFile, LPDCB lpDCB)
{
	DCB *lpLocalDcb;
	struct termios currentState;
	WINPR_COMM* pComm = (WINPR_COMM*) hFile;
	DWORD bytesReturned;

	if (!CommInitialized())
		return FALSE;

	if (!pComm || pComm->Type != HANDLE_TYPE_COMM || !pComm->fd )
	{
		SetLastError(ERROR_INVALID_HANDLE);
		return FALSE;
	}

	if (!lpDCB)
	{
		SetLastError(ERROR_INVALID_DATA);
		return FALSE;
	}

	if (lpDCB->DCBlength < sizeof(DCB))
	{
		SetLastError(ERROR_INVALID_DATA);
		return FALSE;
	}

	if (tcgetattr(pComm->fd, &currentState) < 0)
	{
		SetLastError(ERROR_IO_DEVICE);
		return FALSE;
	}

	lpLocalDcb = (DCB*)calloc(1, lpDCB->DCBlength);
	if (lpLocalDcb == NULL)
	{
		SetLastError(ERROR_OUTOFMEMORY);
		return FALSE;
	}

	/* error_handle */

	lpLocalDcb->DCBlength = lpDCB->DCBlength;

	SERIAL_BAUD_RATE baudRate;
	if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_BAUD_RATE, NULL, 0, &baudRate, sizeof(SERIAL_BAUD_RATE), &bytesReturned, NULL))
	{
		CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the baud rate.");
		goto error_handle;
	}
	lpLocalDcb->BaudRate = baudRate.BaudRate;

	lpLocalDcb->fBinary = (currentState.c_cflag & ICANON) == 0;
	if (!lpLocalDcb->fBinary)
	{
		CommLog_Print(WLOG_WARN, "Unexpected nonbinary mode, consider to unset the ICANON flag.");
	}

	lpLocalDcb->fParity =  (currentState.c_iflag & INPCK) != 0;

	SERIAL_HANDFLOW handflow;
	if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_HANDFLOW, NULL, 0, &handflow, sizeof(SERIAL_HANDFLOW), &bytesReturned, NULL))
	{
		CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the handflow settings.");
		goto error_handle;
	}

	lpLocalDcb->fOutxCtsFlow = (handflow.ControlHandShake & SERIAL_CTS_HANDSHAKE) != 0;

	lpLocalDcb->fOutxDsrFlow = (handflow.ControlHandShake & SERIAL_DSR_HANDSHAKE) != 0;

	if (handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE)
	{
		lpLocalDcb->fDtrControl = DTR_CONTROL_HANDSHAKE;
	}
	else if (handflow.ControlHandShake & SERIAL_DTR_CONTROL)
	{
		lpLocalDcb->fDtrControl = DTR_CONTROL_ENABLE;
	}
	else
	{
		lpLocalDcb->fDtrControl = DTR_CONTROL_DISABLE;
	}

	lpLocalDcb->fDsrSensitivity = (handflow.ControlHandShake & SERIAL_DSR_SENSITIVITY) != 0;

	lpLocalDcb->fTXContinueOnXoff = (handflow.FlowReplace & SERIAL_XOFF_CONTINUE) != 0;

	lpLocalDcb->fOutX = (handflow.FlowReplace & SERIAL_AUTO_TRANSMIT) != 0;

	lpLocalDcb->fInX = (handflow.FlowReplace & SERIAL_AUTO_RECEIVE) != 0;

	lpLocalDcb->fErrorChar = (handflow.FlowReplace & SERIAL_ERROR_CHAR) != 0;

	lpLocalDcb->fNull = (handflow.FlowReplace & SERIAL_NULL_STRIPPING) != 0;

	if (handflow.FlowReplace & SERIAL_RTS_HANDSHAKE)
	{
		lpLocalDcb->fRtsControl = RTS_CONTROL_HANDSHAKE;
	}
	else if (handflow.FlowReplace & SERIAL_RTS_CONTROL)
	{
		lpLocalDcb->fRtsControl = RTS_CONTROL_ENABLE;
	}
	else
	{
		lpLocalDcb->fRtsControl = RTS_CONTROL_DISABLE;
	}

	// FIXME: how to get the RTS_CONTROL_TOGGLE state? Does it match the UART 16750's Autoflow Control Enabled bit in its Modem Control Register (MCR)

	lpLocalDcb->fAbortOnError = (handflow.ControlHandShake & SERIAL_ERROR_ABORT) != 0;

	/* lpLocalDcb->fDummy2 not used */

	lpLocalDcb->wReserved = 0; /* must be zero */

	lpLocalDcb->XonLim = handflow.XonLimit;

	lpLocalDcb->XoffLim = handflow.XoffLimit;

	SERIAL_LINE_CONTROL lineControl;
	if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_LINE_CONTROL, NULL, 0, &lineControl, sizeof(SERIAL_LINE_CONTROL), &bytesReturned, NULL))
	{
		CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the control settings.");
		goto error_handle;
	}

	lpLocalDcb->ByteSize = lineControl.WordLength;

	lpLocalDcb->Parity = lineControl.Parity;

	lpLocalDcb->StopBits = lineControl.StopBits;

	SERIAL_CHARS serialChars;
	if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_CHARS, NULL, 0, &serialChars, sizeof(SERIAL_CHARS), &bytesReturned, NULL))
	{
		CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the serial chars.");
		goto error_handle;
	}

	lpLocalDcb->XonChar = serialChars.XonChar;

	lpLocalDcb->XoffChar = serialChars.XoffChar;

	lpLocalDcb->ErrorChar = serialChars.ErrorChar;

	lpLocalDcb->EofChar = serialChars.EofChar;

	lpLocalDcb->EvtChar = serialChars.EventChar;

	memcpy(lpDCB, lpLocalDcb, lpDCB->DCBlength);
	free(lpLocalDcb);
	return TRUE;

error_handle:
	free(lpLocalDcb);

	return FALSE;
}

/**
 * @return TRUE on success, FALSE otherwise.
 *
 * As of today, SetCommState() can fail half-way with some settings
 * applied and some others not. SetCommState() returns on the first
 * failure met. FIXME: or is it correct?
 *
 * ERRORS:
 *   ERROR_INVALID_HANDLE
 *   ERROR_IO_DEVICE
 */
BOOL SetCommState(HANDLE hFile, LPDCB lpDCB)
{
	struct termios upcomingTermios;
	WINPR_COMM* pComm = (WINPR_COMM*) hFile;
	DWORD bytesReturned;

	/* FIXME: validate changes according GetCommProperties? */

	if (!CommInitialized())
		return FALSE;

	if (!pComm || pComm->Type != HANDLE_TYPE_COMM || !pComm->fd )
	{
		SetLastError(ERROR_INVALID_HANDLE);
		return FALSE;
	}

	if (!lpDCB)
	{
		SetLastError(ERROR_INVALID_DATA);
		return FALSE;
	}

	/* NB: did the choice to call ioctls first when available and
	   then to setup upcomingTermios. Don't mix both stages. */

	/** ioctl calls stage **/

	SERIAL_BAUD_RATE baudRate;
	baudRate.BaudRate = lpDCB->BaudRate;
	if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_BAUD_RATE, &baudRate, sizeof(SERIAL_BAUD_RATE), NULL, 0, &bytesReturned, NULL))
	{
		CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the baud rate.");
		return FALSE;
	}

	SERIAL_CHARS serialChars;
	if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_CHARS, NULL, 0, &serialChars, sizeof(SERIAL_CHARS), &bytesReturned, NULL)) /* as of today, required for BreakChar */
	{
		CommLog_Print(WLOG_WARN, "SetCommState failure: could not get the initial serial chars.");
		return FALSE;
	}
	serialChars.XonChar = lpDCB->XonChar;
	serialChars.XoffChar = lpDCB->XoffChar;
	serialChars.ErrorChar = lpDCB->ErrorChar;
	serialChars.EofChar = lpDCB->EofChar;
	serialChars.EventChar = lpDCB->EvtChar;
	if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_CHARS, &serialChars, sizeof(SERIAL_CHARS), NULL, 0, &bytesReturned, NULL))
	{
		CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the serial chars.");
		return FALSE;
	}

	SERIAL_LINE_CONTROL lineControl;
	lineControl.StopBits = lpDCB->StopBits;
	lineControl.Parity = lpDCB->Parity;
	lineControl.WordLength = lpDCB->ByteSize;
	if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_LINE_CONTROL, &lineControl, sizeof(SERIAL_LINE_CONTROL), NULL, 0, &bytesReturned, NULL))
	{
		CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the control settings.");
		return FALSE;
	}

	SERIAL_HANDFLOW handflow;
	ZeroMemory(&handflow, sizeof(SERIAL_HANDFLOW));

	if (lpDCB->fOutxCtsFlow)
	{
		handflow.ControlHandShake |= SERIAL_CTS_HANDSHAKE;
	}

	if (lpDCB->fOutxDsrFlow)
	{
		handflow.ControlHandShake |= SERIAL_DSR_HANDSHAKE;
	}

	switch (lpDCB->fDtrControl)
	{
		case SERIAL_DTR_HANDSHAKE:
			handflow.ControlHandShake |= DTR_CONTROL_HANDSHAKE;
			break;

		case SERIAL_DTR_CONTROL:
			handflow.ControlHandShake |= DTR_CONTROL_ENABLE;
			break;

		case DTR_CONTROL_DISABLE:
			/* do nothing since handflow is init-zeroed */
			break;

		default:
			CommLog_Print(WLOG_WARN, "Unexpected fDtrControl value: %d\n", lpDCB->fDtrControl);
			return FALSE;
	}

	if (lpDCB->fDsrSensitivity)
	{
		handflow.ControlHandShake |= SERIAL_DSR_SENSITIVITY;
	}

	if (lpDCB->fTXContinueOnXoff)
	{
		handflow.FlowReplace |= SERIAL_XOFF_CONTINUE;
	}

	if (lpDCB->fOutX)
	{
		handflow.FlowReplace |= SERIAL_AUTO_TRANSMIT;
	}

	if (lpDCB->fInX)
	{
		handflow.FlowReplace |= SERIAL_AUTO_RECEIVE;
	}

	if (lpDCB->fErrorChar)
	{
		handflow.FlowReplace |= SERIAL_ERROR_CHAR;
	}

	if (lpDCB->fNull)
	{
		handflow.FlowReplace |= SERIAL_NULL_STRIPPING;
	}

	switch (lpDCB->fRtsControl)
	{
		case RTS_CONTROL_TOGGLE:
			CommLog_Print(WLOG_WARN, "Unsupported RTS_CONTROL_TOGGLE feature");
			// FIXME: see also GetCommState()
			return FALSE;

		case RTS_CONTROL_HANDSHAKE:
			handflow.FlowReplace |= SERIAL_RTS_HANDSHAKE;
			break;

		case RTS_CONTROL_ENABLE:
			handflow.FlowReplace |= SERIAL_RTS_CONTROL;
			break;

		case RTS_CONTROL_DISABLE:
			/* do nothing since handflow is init-zeroed */
			break;

		default:
			CommLog_Print(WLOG_WARN, "Unexpected fRtsControl value: %d\n", lpDCB->fRtsControl);
			return FALSE;
	}

	if (lpDCB->fAbortOnError)
	{
		handflow.ControlHandShake |= SERIAL_ERROR_ABORT;
	}

	/* lpDCB->fDummy2 not used */

	/* lpLocalDcb->wReserved  ignored */

	handflow.XonLimit = lpDCB->XonLim;

	handflow.XoffLimit = lpDCB->XoffLim;

	if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_HANDFLOW, &handflow, sizeof(SERIAL_HANDFLOW), NULL, 0, &bytesReturned, NULL))
	{
		CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the handflow settings.");
		return FALSE;
	}

	/** upcomingTermios stage **/

	ZeroMemory(&upcomingTermios, sizeof(struct termios));
	if (tcgetattr(pComm->fd, &upcomingTermios) < 0) /* NB: preserves current settings not directly handled by the Communication Functions */
	{
		SetLastError(ERROR_IO_DEVICE);
		return FALSE;
	}

	if (lpDCB->fBinary)
	{
		upcomingTermios.c_lflag &= ~ICANON;
	}
	else
	{
		upcomingTermios.c_lflag |= ICANON;
		CommLog_Print(WLOG_WARN, "Unexpected nonbinary mode, consider to unset the ICANON flag.");
	}

	if (lpDCB->fParity)
	{
		upcomingTermios.c_iflag |= INPCK;
	}
	else
	{
		upcomingTermios.c_iflag &= ~INPCK;
	}

	/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363423%28v=vs.85%29.aspx
	 *
	 * The SetCommState function reconfigures the communications
	 * resource, but it does not affect the internal output and
	 * input buffers of the specified driver. The buffers are not
	 * flushed, and pending read and write operations are not
	 * terminated prematurely.
	 *
	 * TCSANOW matches the best this definition
	 */

	if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
	{
		SetLastError(ERROR_IO_DEVICE);
		return FALSE;
	}

	return TRUE;
}

/**
 * ERRORS:
 *   ERROR_INVALID_HANDLE
 */
BOOL GetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts)
{
	WINPR_COMM* pComm = (WINPR_COMM*) hFile;
	DWORD bytesReturned;

	if (!CommInitialized())
		return FALSE;

	if (!pComm || pComm->Type != HANDLE_TYPE_COMM || !pComm->fd )
	{
		SetLastError(ERROR_INVALID_HANDLE);
		return FALSE;
	}

	/* as of today, SERIAL_TIMEOUTS and COMMTIMEOUTS structures are identical */

	if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_TIMEOUTS, NULL, 0, lpCommTimeouts, sizeof(COMMTIMEOUTS), &bytesReturned, NULL))
	{
		CommLog_Print(WLOG_WARN, "GetCommTimeouts failure.");
		return FALSE;
	}

	return TRUE;
}

/**
 * ERRORS:
 *   ERROR_INVALID_HANDLE
 */
BOOL SetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts)
{
	WINPR_COMM* pComm = (WINPR_COMM*) hFile;
	DWORD bytesReturned;

	if (!CommInitialized())
		return FALSE;

	if (!pComm || pComm->Type != HANDLE_TYPE_COMM || !pComm->fd )
	{
		SetLastError(ERROR_INVALID_HANDLE);
		return FALSE;
	}

	/* as of today, SERIAL_TIMEOUTS and COMMTIMEOUTS structures are identical */

	if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_TIMEOUTS, lpCommTimeouts, sizeof(COMMTIMEOUTS), NULL, 0, &bytesReturned, NULL))
	{
		CommLog_Print(WLOG_WARN, "SetCommTimeouts failure.");
		return FALSE;
	}

	return TRUE;
}

BOOL GetDefaultCommConfigA(LPCSTR lpszName, LPCOMMCONFIG lpCC, LPDWORD lpdwSize)
{
	if (!CommInitialized())
		return FALSE;

	/* TODO: not implemented */

	return TRUE;
}

BOOL GetDefaultCommConfigW(LPCWSTR lpszName, LPCOMMCONFIG lpCC, LPDWORD lpdwSize)
{
	if (!CommInitialized())
		return FALSE;

	/* TODO: not implemented */

	return TRUE;
}

BOOL SetDefaultCommConfigA(LPCSTR lpszName, LPCOMMCONFIG lpCC, DWORD dwSize)
{
	if (!CommInitialized())
		return FALSE;

	/* TODO: not implemented */

	return TRUE;
}

BOOL SetDefaultCommConfigW(LPCWSTR lpszName, LPCOMMCONFIG lpCC, DWORD dwSize)
{
	if (!CommInitialized())
		return FALSE;

	/* TODO: not implemented */

	return TRUE;
}

BOOL SetCommBreak(HANDLE hFile)
{
	WINPR_COMM* pComm = (WINPR_COMM*) hFile;

	if (!CommInitialized())
		return FALSE;

	/* TODO: not implemented */

	if (!pComm)
		return FALSE;

	return TRUE;
}

BOOL ClearCommBreak(HANDLE hFile)
{
	WINPR_COMM* pComm = (WINPR_COMM*) hFile;

	if (!CommInitialized())
		return FALSE;

	/* TODO: not implemented */

	if (!pComm)
		return FALSE;

	return TRUE;
}

BOOL ClearCommError(HANDLE hFile, PDWORD lpErrors, LPCOMSTAT lpStat)
{
	WINPR_COMM* pComm = (WINPR_COMM*) hFile;

	if (!CommInitialized())
		return FALSE;

	/* TODO: not implemented */

	if (!pComm)
		return FALSE;

	return TRUE;
}

BOOL PurgeComm(HANDLE hFile, DWORD dwFlags)
{
	WINPR_COMM* pComm = (WINPR_COMM*) hFile;
	DWORD bytesReturned = 0;

	if (!CommInitialized())
		return FALSE;

	if (!pComm || pComm->Type != HANDLE_TYPE_COMM || !pComm->fd )
	{
		SetLastError(ERROR_INVALID_HANDLE);
		return FALSE;
	}

	if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_PURGE, &dwFlags, sizeof(DWORD), NULL, 0, &bytesReturned, NULL))
	{
		CommLog_Print(WLOG_WARN, "PurgeComm failure.");
		return FALSE;
	}

	return TRUE;
}

BOOL SetupComm(HANDLE hFile, DWORD dwInQueue, DWORD dwOutQueue)
{
	WINPR_COMM* pComm = (WINPR_COMM*) hFile;
	SERIAL_QUEUE_SIZE queueSize;
	DWORD bytesReturned = 0;

	if (!CommInitialized())
		return FALSE;

	if (!pComm || pComm->Type != HANDLE_TYPE_COMM || !pComm->fd )
	{
		SetLastError(ERROR_INVALID_HANDLE);
		return FALSE;
	}

	queueSize.InSize = dwInQueue;
	queueSize.OutSize = dwOutQueue;

	if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_QUEUE_SIZE, &queueSize, sizeof(SERIAL_QUEUE_SIZE), NULL, 0, &bytesReturned, NULL))
	{
		CommLog_Print(WLOG_WARN, "SetCommTimeouts failure.");
		return FALSE;
	}

	return TRUE;
}

BOOL EscapeCommFunction(HANDLE hFile, DWORD dwFunc)
{
	WINPR_COMM* pComm = (WINPR_COMM*) hFile;

	if (!CommInitialized())
		return FALSE;

	/* TODO: not implemented */

	if (!pComm)
		return FALSE;

	return TRUE;
}

BOOL TransmitCommChar(HANDLE hFile, char cChar)
{
	WINPR_COMM* pComm = (WINPR_COMM*) hFile;

	if (!CommInitialized())
		return FALSE;

	/* TODO: not implemented */

	if (!pComm)
		return FALSE;

	return TRUE;
}

BOOL WaitCommEvent(HANDLE hFile, PDWORD lpEvtMask, LPOVERLAPPED lpOverlapped)
{
	WINPR_COMM* pComm = (WINPR_COMM*) hFile;

	if (!CommInitialized())
		return FALSE;

	/* TODO: not implemented */

	if (!pComm)
		return FALSE;

	return TRUE;
}

/* Extended API */

static BOOL _IsReservedCommDeviceName(LPCTSTR lpName)
{
	int i;

	if (!CommInitialized())
		return FALSE;

	/* Serial ports, COM1-9 */
	for (i=1; i<10; i++)
	{
		TCHAR genericName[5];
		if (_stprintf_s(genericName, 5, _T("COM%d"), i) < 0)
		{
			return FALSE;
		}

		if (_tcscmp(genericName, lpName) == 0)
			return TRUE;
	}

	/* Parallel ports, LPT1-9 */
	for (i=1; i<10; i++)
	{
		TCHAR genericName[5];
		if (_stprintf_s(genericName, 5, _T("LPT%d"), i) < 0)
		{
			return FALSE;
		}

		if (_tcscmp(genericName, lpName) == 0)
			return TRUE;
	}

	/* FIXME: what about PRN ? */

	return FALSE;
}

/**
 * Returns TRUE on success, FALSE otherwise. To get extended error
 * information, call GetLastError.
 *
 * ERRORS:
 *   ERROR_DLL_INIT_FAILED
 *   ERROR_OUTOFMEMORY was not possible to get mappings.
 *   ERROR_INVALID_DATA was not possible to add the device.
 */
BOOL DefineCommDevice(/* DWORD dwFlags,*/ LPCTSTR lpDeviceName, LPCTSTR lpTargetPath)
{
	int i = 0;
	LPTSTR storedDeviceName = NULL;
	LPTSTR storedTargetPath = NULL;

	if (!CommInitialized())
		return FALSE;

	EnterCriticalSection(&_CommDevicesLock);

	if (_CommDevices == NULL)
	{
		SetLastError(ERROR_DLL_INIT_FAILED);
		goto error_handle;
	}

	if (_tcsncmp(lpDeviceName, _T("\\\\.\\"), 4) != 0)
	{
		if (!_IsReservedCommDeviceName(lpDeviceName))
		{
			SetLastError(ERROR_INVALID_DATA);
			goto error_handle;
		}
	}

	storedDeviceName = _tcsdup(lpDeviceName);
	if (storedDeviceName == NULL)
	{
		SetLastError(ERROR_OUTOFMEMORY);
		goto error_handle;
	}

	storedTargetPath = _tcsdup(lpTargetPath);
	if (storedTargetPath == NULL)
	{
		SetLastError(ERROR_OUTOFMEMORY);
		goto error_handle;
	}

	for (i=0; i<COMM_DEVICE_MAX; i++)
	{
		if (_CommDevices[i] != NULL)
		{
			if (_tcscmp(_CommDevices[i]->name, storedDeviceName) == 0)
			{
				/* take over the emplacement */
				free(_CommDevices[i]->name);
				free(_CommDevices[i]->path);
				_CommDevices[i]->name = storedDeviceName;
				_CommDevices[i]->path = storedTargetPath;

				break;
			}
		}
		else
		{
			/* new emplacement */
			_CommDevices[i] = (COMM_DEVICE*)calloc(1, sizeof(COMM_DEVICE));
			if (_CommDevices[i] == NULL)
			{
				SetLastError(ERROR_OUTOFMEMORY);
				goto error_handle;
			}

			_CommDevices[i]->name = storedDeviceName;
			_CommDevices[i]->path = storedTargetPath;
			break;
		}
	}

	if (i == COMM_DEVICE_MAX)
	{
		SetLastError(ERROR_OUTOFMEMORY);
		goto error_handle;
	}

	LeaveCriticalSection(&_CommDevicesLock);
	return TRUE;

error_handle:
	free(storedDeviceName);
	free(storedTargetPath);

	LeaveCriticalSection(&_CommDevicesLock);
	return FALSE;
}

/**
 * Returns the number of target paths in the buffer pointed to by
 * lpTargetPath.
 *
 * The current implementation returns in any case 0 and 1 target
 * path. A NULL lpDeviceName is not supported yet to get all the
 * paths.
 *
 * ERRORS:
 *   ERROR_SUCCESS
 *   ERROR_DLL_INIT_FAILED
 *   ERROR_OUTOFMEMORY was not possible to get mappings.
 *   ERROR_NOT_SUPPORTED equivalent QueryDosDevice feature not supported.
 *   ERROR_INVALID_DATA was not possible to retrieve any device information.
 *   ERROR_INSUFFICIENT_BUFFER too small lpTargetPath
 */
DWORD QueryCommDevice(LPCTSTR lpDeviceName, LPTSTR lpTargetPath, DWORD ucchMax)
{
	int i;
	LPTSTR storedTargetPath;

	SetLastError(ERROR_SUCCESS);

	if (!CommInitialized())
		return 0;

	if (_CommDevices == NULL)
	{
		SetLastError(ERROR_DLL_INIT_FAILED);
		return 0;
	}

	if (lpDeviceName == NULL || lpTargetPath == NULL)
	{
		SetLastError(ERROR_NOT_SUPPORTED);
		return 0;
	}

	EnterCriticalSection(&_CommDevicesLock);

	storedTargetPath = NULL;
	for (i=0; i<COMM_DEVICE_MAX; i++)
	{
		if (_CommDevices[i] != NULL)
		{
			if (_tcscmp(_CommDevices[i]->name, lpDeviceName) == 0)
			{
				storedTargetPath = _CommDevices[i]->path;
				break;
			}

			continue;
		}

		break;
	}

	LeaveCriticalSection(&_CommDevicesLock);

	if (storedTargetPath == NULL)
	{
		SetLastError(ERROR_INVALID_DATA);
		return 0;
	}

	if (_tcslen(storedTargetPath) + 2 > ucchMax)
	{
		SetLastError(ERROR_INSUFFICIENT_BUFFER);
		return 0;
	}

	_tcscpy(lpTargetPath, storedTargetPath);
	lpTargetPath[_tcslen(storedTargetPath) + 1] = '\0'; /* 2nd final '\0' */

	return _tcslen(lpTargetPath) + 2;
}

/**
 * Checks whether lpDeviceName is a valid and registered Communication device.
 */
BOOL IsCommDevice(LPCTSTR lpDeviceName)
{
	TCHAR lpTargetPath[MAX_PATH];

	if (!CommInitialized())
		return FALSE;

	if (QueryCommDevice(lpDeviceName, lpTargetPath, MAX_PATH) > 0)
	{
		return TRUE;
	}

	return FALSE;
}

/**
 * Sets
 */
void _comm_setServerSerialDriver(HANDLE hComm, SERIAL_DRIVER_ID driverId)
{
	ULONG Type;
	WINPR_HANDLE* Object;
	WINPR_COMM* pComm;

	if (!CommInitialized())
		return;

	if (!winpr_Handle_GetInfo(hComm, &Type, &Object))
	{
		CommLog_Print(WLOG_WARN, "_comm_setServerSerialDriver failure");
		return;
	}

	pComm = (WINPR_COMM*)Object;
	pComm->serverSerialDriverId = driverId;
}

static HANDLE_OPS ops = {
		CommIsHandled,
		CommCloseHandle,
		CommGetFd,
		NULL /* CleanupHandle */
};

/**
 * http://msdn.microsoft.com/en-us/library/windows/desktop/aa363198%28v=vs.85%29.aspx
 *
 * @param lpDeviceName e.g. COM1, \\.\COM1, ...
 *
 * @param dwDesiredAccess expects GENERIC_READ | GENERIC_WRITE, a
 * warning message is printed otherwise. TODO: better support.
 *
 * @param dwShareMode must be zero, INVALID_HANDLE_VALUE is returned
 * otherwise and GetLastError() should return ERROR_SHARING_VIOLATION.
 *
 * @param lpSecurityAttributes NULL expected, a warning message is printed
 * otherwise. TODO: better support.
 *
 * @param dwCreationDisposition must be OPEN_EXISTING. If the
 * communication device doesn't exist INVALID_HANDLE_VALUE is returned
 * and GetLastError() returns ERROR_FILE_NOT_FOUND.
 *
 * @param dwFlagsAndAttributes zero expected, a warning message is
 * printed otherwise.
 *
 * @param hTemplateFile must be NULL.
 *
 * @return INVALID_HANDLE_VALUE on error.
 */
HANDLE CommCreateFileA(LPCSTR lpDeviceName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes,
		DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
{
	CHAR devicePath[MAX_PATH];
	struct stat deviceStat;
	WINPR_COMM* pComm = NULL;
	struct termios upcomingTermios;

	if (!CommInitialized())
		return INVALID_HANDLE_VALUE;

	if (dwDesiredAccess != (GENERIC_READ | GENERIC_WRITE))
	{
		CommLog_Print(WLOG_WARN, "unexpected access to the device: 0x%lX", dwDesiredAccess);
	}

	if (dwShareMode != 0)
	{
		SetLastError(ERROR_SHARING_VIOLATION);
		return INVALID_HANDLE_VALUE;
	}

	/* TODO: Prevents other processes from opening a file or
         * device if they request delete, read, or write access. */

	if (lpSecurityAttributes != NULL)
	{
		CommLog_Print(WLOG_WARN, "unexpected security attributes, nLength=%lu", lpSecurityAttributes->nLength);
	}

	if (dwCreationDisposition != OPEN_EXISTING)
	{
		SetLastError(ERROR_FILE_NOT_FOUND); /* FIXME: ERROR_NOT_SUPPORTED better? */
		return INVALID_HANDLE_VALUE;
	}

	if (QueryCommDevice(lpDeviceName, devicePath, MAX_PATH) <= 0)
	{
		/* SetLastError(GetLastError()); */
		return INVALID_HANDLE_VALUE;
	}

	if (stat(devicePath, &deviceStat) < 0)
	{
		CommLog_Print(WLOG_WARN, "device not found %s", devicePath);
		SetLastError(ERROR_FILE_NOT_FOUND);
		return INVALID_HANDLE_VALUE;
	}

	if (!S_ISCHR(deviceStat.st_mode))
	{
		CommLog_Print(WLOG_WARN, "bad device %s", devicePath);
		SetLastError(ERROR_BAD_DEVICE);
		return INVALID_HANDLE_VALUE;
	}

	if (dwFlagsAndAttributes != 0)
	{
		CommLog_Print(WLOG_WARN, "unexpected flags and attributes: 0x%lX", dwFlagsAndAttributes);
	}

	if (hTemplateFile != NULL)
	{
		SetLastError(ERROR_NOT_SUPPORTED); /* FIXME: other proper error? */
		return INVALID_HANDLE_VALUE;
	}

	pComm = (WINPR_COMM*) calloc(1, sizeof(WINPR_COMM));
	if (pComm == NULL)
	{
		SetLastError(ERROR_OUTOFMEMORY);
		return INVALID_HANDLE_VALUE;
	}

	WINPR_HANDLE_SET_TYPE_AND_MODE(pComm, HANDLE_TYPE_COMM, FD_READ);

	pComm->ops = &ops;

	/* error_handle */

	pComm->fd = open(devicePath, O_RDWR | O_NOCTTY | O_NONBLOCK);
	if (pComm->fd < 0)
	{
		CommLog_Print(WLOG_WARN, "failed to open device %s", devicePath);
		SetLastError(ERROR_BAD_DEVICE);
		goto error_handle;
	}

	pComm->fd_read = open(devicePath, O_RDONLY | O_NOCTTY | O_NONBLOCK);
	if (pComm->fd_read < 0)
	{
		CommLog_Print(WLOG_WARN, "failed to open fd_read, device: %s", devicePath);
		SetLastError(ERROR_BAD_DEVICE);
		goto error_handle;
	}

	pComm->fd_read_event = eventfd(0, EFD_NONBLOCK); /* EFD_NONBLOCK required because a read() is not always expected */
	if (pComm->fd_read_event < 0)
	{
		CommLog_Print(WLOG_WARN, "failed to open fd_read_event, device: %s", devicePath);
		SetLastError(ERROR_BAD_DEVICE);
		goto error_handle;
	}

	InitializeCriticalSection(&pComm->ReadLock);

	pComm->fd_write = open(devicePath, O_WRONLY | O_NOCTTY | O_NONBLOCK);
	if (pComm->fd_write < 0)
	{
		CommLog_Print(WLOG_WARN, "failed to open fd_write, device: %s", devicePath);
		SetLastError(ERROR_BAD_DEVICE);
		goto error_handle;
	}

	pComm->fd_write_event = eventfd(0, EFD_NONBLOCK); /* EFD_NONBLOCK required because a read() is not always expected */
	if (pComm->fd_write_event < 0)
	{
		CommLog_Print(WLOG_WARN, "failed to open fd_write_event, device: %s", devicePath);
		SetLastError(ERROR_BAD_DEVICE);
		goto error_handle;
	}

	InitializeCriticalSection(&pComm->WriteLock);

	/* can also be setup later on with _comm_setServerSerialDriver() */
	pComm->serverSerialDriverId = SerialDriverUnknown;

	InitializeCriticalSection(&pComm->EventsLock);

	if (ioctl(pComm->fd, TIOCGICOUNT, &(pComm->counters)) < 0)
	{
		CommLog_Print(WLOG_WARN, "TIOCGICOUNT ioctl failed, errno=[%d] %s.", errno, strerror(errno));
		CommLog_Print(WLOG_WARN, "could not read counters.");

		/* could not initialize counters but keep on.
		 *
		 * Not all drivers, especially for USB to serial
		 * adapters (e.g. those based on pl2303), does support
		 * this call.
		 */

		ZeroMemory(&(pComm->counters), sizeof(struct serial_icounter_struct));
	}

	/* The binary/raw mode is required for the redirection but
	 * only flags that are not handle somewhere-else, except
	 * ICANON, are forced here. */

	ZeroMemory(&upcomingTermios, sizeof(struct termios));
	if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
	{
		SetLastError(ERROR_IO_DEVICE);
		goto error_handle;
	}

	upcomingTermios.c_iflag &= ~(/*IGNBRK |*/ BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL /*| IXON*/);
	upcomingTermios.c_oflag = 0; /* <=> &= ~OPOST */
	upcomingTermios.c_lflag = 0; /* <=> &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); */
	/* upcomingTermios.c_cflag &= ~(CSIZE | PARENB); */
	/* upcomingTermios.c_cflag |= CS8; */

	/* About missing flags recommended by termios(3):
	 *
	 *   IGNBRK and IXON, see: IOCTL_SERIAL_SET_HANDFLOW
	 *   CSIZE, PARENB and CS8, see: IOCTL_SERIAL_SET_LINE_CONTROL
	 */

	/* a few more settings required for the redirection */
	upcomingTermios.c_cflag |= CLOCAL | CREAD;

	if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
	{
		SetLastError(ERROR_IO_DEVICE);
		goto error_handle;
	}

	return (HANDLE)pComm;

  error_handle:
	if (pComm != NULL)
	{
		CloseHandle(pComm);
	}

	return INVALID_HANDLE_VALUE;
}

BOOL CommIsHandled(HANDLE handle)
{
	WINPR_COMM *pComm;

	if (!CommInitialized())
		return FALSE;

	pComm = (WINPR_COMM*)handle;

	if (!pComm || (pComm->Type != HANDLE_TYPE_COMM) || (pComm == INVALID_HANDLE_VALUE))
	{
		SetLastError(ERROR_INVALID_HANDLE);
		return FALSE;
	}

	return TRUE;
}

BOOL CommCloseHandle(HANDLE handle)
{
	WINPR_COMM *pComm;

	if (!CommInitialized())
		return FALSE;

	pComm = (WINPR_COMM*)handle;

	if (!pComm || pComm->Type != HANDLE_TYPE_COMM)
	{
		SetLastError(ERROR_INVALID_HANDLE);
		return FALSE;
	}

	if (pComm->PendingEvents & SERIAL_EV_FREERDP_WAITING)
	{
		ULONG WaitMask = 0;
		DWORD BytesReturned = 0;

		/* ensures to gracefully stop the WAIT_ON_MASK's loop */
		if (!CommDeviceIoControl(handle, IOCTL_SERIAL_SET_WAIT_MASK, &WaitMask, sizeof(ULONG), NULL, 0, &BytesReturned, NULL))
		{
			CommLog_Print(WLOG_WARN, "failure to WAIT_ON_MASK's loop!");
		}
	}

	DeleteCriticalSection(&pComm->ReadLock);
	DeleteCriticalSection(&pComm->WriteLock);
	DeleteCriticalSection(&pComm->EventsLock);

	if (pComm->fd > 0)
		close(pComm->fd);

	if (pComm->fd_write > 0)
		close(pComm->fd_write);

	if (pComm->fd_write_event > 0)
		close(pComm->fd_write_event);

	if (pComm->fd_read > 0)
		close(pComm->fd_read);

	if (pComm->fd_read_event > 0)
		close(pComm->fd_read_event);

	free(pComm);

	return TRUE;
}

#ifndef WITH_EVENTFD_READ_WRITE
int eventfd_read(int fd, eventfd_t* value)
{
	return (read(fd, value, sizeof(*value)) == sizeof(*value)) ? 0 : -1;
}

int eventfd_write(int fd, eventfd_t value)
{
	return (write(fd, &value, sizeof(value)) == sizeof(value)) ? 0 : -1;
}
#endif

#endif /* __linux__ */