<commit_before>/**
 * @file llfloateropenobject.cpp
 * @brief LLFloaterOpenObject class implementation
 *
 * $LicenseInfo:firstyear=2004&license=viewergpl$
 *
 * Copyright (c) 2004-2009, Linden Research, Inc.
 *
 * Second Life Viewer Source Code
 * The source code in this file ("Source Code") is provided by Linden Lab
 * to you under the terms of the GNU General Public License, version 2.0
 * ("GPL"), unless you have obtained a separate licensing agreement
 * ("Other License"), formally executed by you and Linden Lab.  Terms of
 * the GPL can be found in doc/GPL-license.txt in this distribution, or
 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
 *
 * There are special exceptions to the terms and conditions of the GPL as
 * it is applied to this Source Code. View the full text of the exception
 * in the file doc/FLOSS-exception.txt in this software distribution, or
 * online at
 * http://secondlifegrid.net/programs/open_source/licensing/flossexception
 *
 * By copying, modifying or distributing this software, you acknowledge
 * that you have read and understood your obligations described above,
 * and agree to abide by those obligations.
 *
 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
 * COMPLETENESS OR PERFORMANCE.
 * $/LicenseInfo$
 */

/*
 * Shows the contents of an object.
 * A floater wrapper for LLPanelObjectInventory
 */

#include "llviewerprecompiledheaders.h"

#include "llfloateropenobject.h"

#include "llcachename.h"
#include "llbutton.h"
#include "llnotificationsutil.h"
#include "lltextbox.h"

#include "llalertdialog.h"
#include "llinventorybridge.h"
#include "llfloaterinventory.h"
#include "llinventorymodel.h"
#include "llinventorypanel.h"
#include "llpanelobjectinventory.h"
#include "llfloaterreg.h"
#include "llselectmgr.h"
#include "lluiconstants.h"
#include "llviewerobject.h"
#include "lluictrlfactory.h"
#include "llviewerwindow.h"

LLFloaterOpenObject::LLFloaterOpenObject(const LLSD& key)
:	LLFloater(key),
	mPanelInventoryObject(NULL),
	mDirty(TRUE)
{
//	LLUICtrlFactory::getInstance()->buildFloater(this,"floater_openobject.xml");
	mCommitCallbackRegistrar.add("OpenObject.MoveToInventory",	boost::bind(&LLFloaterOpenObject::onClickMoveToInventory, this));
	mCommitCallbackRegistrar.add("OpenObject.MoveAndWear",		boost::bind(&LLFloaterOpenObject::onClickMoveAndWear, this));
}

LLFloaterOpenObject::~LLFloaterOpenObject()
{
//	sInstance = NULL;
}
// virtual
BOOL LLFloaterOpenObject::postBuild()
{
	childSetTextArg("object_name", "[DESC]", std::string("Object") ); // *Note: probably do not want to translate this
	mPanelInventoryObject = getChild<LLPanelObjectInventory>("object_contents");
	return TRUE;
}

void LLFloaterOpenObject::onOpen(const LLSD& key)
{
	LLObjectSelectionHandle object_selection = LLSelectMgr::getInstance()->getSelection();
	if (object_selection->getRootObjectCount() != 1)
	{
		LLNotificationsUtil::add("UnableToViewContentsMoreThanOne");
		closeFloater();
		return;
	}
	if(!(object_selection->getPrimaryObject()))
	{
		closeFloater();
		return;
	}
	mObjectSelection = LLSelectMgr::getInstance()->getEditSelection();
}
void LLFloaterOpenObject::refresh()
{
	mPanelInventoryObject->refresh();

	std::string name;
	BOOL enabled;

	LLSelectNode* node = mObjectSelection->getFirstRootNode();
	if (node)
	{
		name = node->mName;
		enabled = TRUE;
	}
	else
	{
		name = "";
		enabled = FALSE;
	}

	childSetTextArg("object_name", "[DESC]", name);
	childSetEnabled("copy_to_inventory_button", enabled);
	childSetEnabled("copy_and_wear_button", enabled);

}

void LLFloaterOpenObject::draw()
{
	if (mDirty)
	{
		refresh();
		mDirty = FALSE;
	}
	LLFloater::draw();
}

void LLFloaterOpenObject::dirty()
{
	mDirty = TRUE;
}

void LLFloaterOpenObject::moveToInventory(bool wear)
{
	if (mObjectSelection->getRootObjectCount() != 1)
	{
		LLNotificationsUtil::add("OnlyCopyContentsOfSingleItem");
		return;
	}

	LLSelectNode* node = mObjectSelection->getFirstRootNode();
	if (!node) return;
	LLViewerObject* object = node->getObject();
	if (!object) return;

	LLUUID object_id = object->getID();
	std::string name = node->mName;

	// Either create a sub-folder of clothing, or of the root folder.
	LLUUID parent_category_id;
	if (wear)
	{
		parent_category_id = gInventory.findCategoryUUIDForType(
			LLFolderType::FT_CLOTHING);
	}
	else
	{
		parent_category_id = gInventory.getRootFolderID();
	}
	LLUUID category_id = gInventory.createNewCategory(parent_category_id,
		LLFolderType::FT_NONE,
		name);

	LLCatAndWear* data = new LLCatAndWear;
	data->mCatID = category_id;
	data->mWear = wear;

	// Copy and/or move the items into the newly created folder.
	// Ignore any "you're going to break this item" messages.
	BOOL success = move_inv_category_world_to_agent(object_id, category_id, TRUE,
													callbackMoveInventory,
													(void*)data);
	if (!success)
	{
		delete data;
		data = NULL;

		LLNotificationsUtil::add("OpenObjectCannotCopy");
	}
}

// static
void LLFloaterOpenObject::callbackMoveInventory(S32 result, void* data)
{
	LLCatAndWear* cat = (LLCatAndWear*)data;

	if (result == 0)
	{
		LLFloaterInventory::showAgentInventory();
		LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel();
		if (active_panel)
		{
			active_panel->setSelection(cat->mCatID, TAKE_FOCUS_NO);
		}
	}

	delete cat;
}

void LLFloaterOpenObject::onClickMoveToInventory()
{
	moveToInventory(false);
	closeFloater();
}

void LLFloaterOpenObject::onClickMoveAndWear()
{
	moveToInventory(true);
	closeFloater();
}

<commit_msg>EXT-3278: copy & wear in object inventory is now only enabled if we can wear contents<commit_after>/**
 * @file llfloateropenobject.cpp
 * @brief LLFloaterOpenObject class implementation
 *
 * $LicenseInfo:firstyear=2004&license=viewergpl$
 *
 * Copyright (c) 2004-2009, Linden Research, Inc.
 *
 * Second Life Viewer Source Code
 * The source code in this file ("Source Code") is provided by Linden Lab
 * to you under the terms of the GNU General Public License, version 2.0
 * ("GPL"), unless you have obtained a separate licensing agreement
 * ("Other License"), formally executed by you and Linden Lab.  Terms of
 * the GPL can be found in doc/GPL-license.txt in this distribution, or
 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
 *
 * There are special exceptions to the terms and conditions of the GPL as
 * it is applied to this Source Code. View the full text of the exception
 * in the file doc/FLOSS-exception.txt in this software distribution, or
 * online at
 * http://secondlifegrid.net/programs/open_source/licensing/flossexception
 *
 * By copying, modifying or distributing this software, you acknowledge
 * that you have read and understood your obligations described above,
 * and agree to abide by those obligations.
 *
 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
 * COMPLETENESS OR PERFORMANCE.
 * $/LicenseInfo$
 */

/*
 * Shows the contents of an object.
 * A floater wrapper for LLPanelObjectInventory
 */

#include "llviewerprecompiledheaders.h"

#include "llfloateropenobject.h"

#include "llcachename.h"
#include "llbutton.h"
#include "llnotificationsutil.h"
#include "lltextbox.h"

#include "llalertdialog.h"
#include "llinventorybridge.h"
#include "llfloaterinventory.h"
#include "llinventorymodel.h"
#include "llinventorypanel.h"
#include "llpanelobjectinventory.h"
#include "llfloaterreg.h"
#include "llselectmgr.h"
#include "lluiconstants.h"
#include "llviewerobject.h"
#include "lluictrlfactory.h"
#include "llviewerwindow.h"

LLFloaterOpenObject::LLFloaterOpenObject(const LLSD& key)
:	LLFloater(key),
	mPanelInventoryObject(NULL),
	mDirty(TRUE)
{
//	LLUICtrlFactory::getInstance()->buildFloater(this,"floater_openobject.xml");
	mCommitCallbackRegistrar.add("OpenObject.MoveToInventory",	boost::bind(&LLFloaterOpenObject::onClickMoveToInventory, this));
	mCommitCallbackRegistrar.add("OpenObject.MoveAndWear",		boost::bind(&LLFloaterOpenObject::onClickMoveAndWear, this));
}

LLFloaterOpenObject::~LLFloaterOpenObject()
{
//	sInstance = NULL;
}

// virtual
BOOL LLFloaterOpenObject::postBuild()
{
	childSetTextArg("object_name", "[DESC]", std::string("Object") ); // *Note: probably do not want to translate this
	mPanelInventoryObject = getChild<LLPanelObjectInventory>("object_contents");

	refresh();
	return TRUE;
}

void LLFloaterOpenObject::onOpen(const LLSD& key)
{
	LLObjectSelectionHandle object_selection = LLSelectMgr::getInstance()->getSelection();
	if (object_selection->getRootObjectCount() != 1)
	{
		LLNotificationsUtil::add("UnableToViewContentsMoreThanOne");
		closeFloater();
		return;
	}
	if(!(object_selection->getPrimaryObject()))
	{
		closeFloater();
		return;
	}
	mObjectSelection = LLSelectMgr::getInstance()->getEditSelection();
	refresh();
}

void LLFloaterOpenObject::refresh()
{
	mPanelInventoryObject->refresh();

	std::string name = "";

	// Enable the copy || copy & wear buttons only if we have something we can copy or copy & wear (respectively).
	bool copy_enabled = false;
	bool wear_enabled = false;

	LLSelectNode* node = mObjectSelection->getFirstRootNode();
	if (node)
	{
		name = node->mName;
		copy_enabled = true;

		LLViewerObject* object = node->getObject();
		if (object)
		{
			// this folder is coming from an object, as there is only one folder in an object, the root,
			// we need to collect the entire contents and handle them as a group
			InventoryObjectList inventory_objects;
			object->getInventoryContents(inventory_objects);

			if (!inventory_objects.empty())
			{
				for (InventoryObjectList::iterator it = inventory_objects.begin();
					 it != inventory_objects.end();
					 ++it)
				{
					LLInventoryItem* item = static_cast<LLInventoryItem*> ((LLInventoryObject*)(*it));
					LLInventoryType::EType type = item->getInventoryType();
					if (type == LLInventoryType::IT_OBJECT
						|| type == LLInventoryType::IT_ATTACHMENT
						|| type == LLInventoryType::IT_WEARABLE
						|| type == LLInventoryType::IT_GESTURE)
					{
						wear_enabled = true;
						break;
					}
				}
			}
		}
	}

	childSetTextArg("object_name", "[DESC]", name);
	childSetEnabled("copy_to_inventory_button", copy_enabled);
	childSetEnabled("copy_and_wear_button", wear_enabled);

}

void LLFloaterOpenObject::draw()
{
	if (mDirty)
	{
		refresh();
		mDirty = FALSE;
	}
	LLFloater::draw();
}

void LLFloaterOpenObject::dirty()
{
	mDirty = TRUE;
}

void LLFloaterOpenObject::moveToInventory(bool wear)
{
	if (mObjectSelection->getRootObjectCount() != 1)
	{
		LLNotificationsUtil::add("OnlyCopyContentsOfSingleItem");
		return;
	}

	LLSelectNode* node = mObjectSelection->getFirstRootNode();
	if (!node) return;
	LLViewerObject* object = node->getObject();
	if (!object) return;

	LLUUID object_id = object->getID();
	std::string name = node->mName;

	// Either create a sub-folder of clothing, or of the root folder.
	LLUUID parent_category_id;
	if (wear)
	{
		parent_category_id = gInventory.findCategoryUUIDForType(
			LLFolderType::FT_CLOTHING);
	}
	else
	{
		parent_category_id = gInventory.getRootFolderID();
	}
	LLUUID category_id = gInventory.createNewCategory(parent_category_id,
		LLFolderType::FT_NONE,
		name);

	LLCatAndWear* data = new LLCatAndWear;
	data->mCatID = category_id;
	data->mWear = wear;

	// Copy and/or move the items into the newly created folder.
	// Ignore any "you're going to break this item" messages.
	BOOL success = move_inv_category_world_to_agent(object_id, category_id, TRUE,
													callbackMoveInventory,
													(void*)data);
	if (!success)
	{
		delete data;
		data = NULL;

		LLNotificationsUtil::add("OpenObjectCannotCopy");
	}
}

// static
void LLFloaterOpenObject::callbackMoveInventory(S32 result, void* data)
{
	LLCatAndWear* cat = (LLCatAndWear*)data;

	if (result == 0)
	{
		LLFloaterInventory::showAgentInventory();
		LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel();
		if (active_panel)
		{
			active_panel->setSelection(cat->mCatID, TAKE_FOCUS_NO);
		}
	}

	delete cat;
}

void LLFloaterOpenObject::onClickMoveToInventory()
{
	moveToInventory(false);
	closeFloater();
}

void LLFloaterOpenObject::onClickMoveAndWear()
{
	moveToInventory(true);
	closeFloater();
}

<|endoftext|>


<commit_before>#include "textlayout_p.h"
#include "textedit_p.h"
#include "textdocument.h"
#include "textedit.h"

void TextLayout::dirty(int width)
{
    viewport = width;
    layoutDirty = true;
    if (textEdit)
        textEdit->viewport()->update();
}

int TextLayout::viewportWidth() const
{
    return textEdit ? textEdit->viewport()->width() : viewport;
}

int TextLayout::doLayout(int index, QList<Section*> *sections) // index is in document coordinates
{
    QTextLayout *textLayout = 0;
    if (!unusedTextLayouts.isEmpty()) {
        textLayout = unusedTextLayouts.takeLast();
        textLayout->clearAdditionalFormats();
    } else {
        textLayout = new QTextLayout;
        textLayout->setFont(font);
        QTextOption option;
        option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
        textLayout->setTextOption(option);
    }
    textLayouts.append(textLayout);
    if (index != 0 && bufferReadCharacter(index - 1) != '\n') {
        qWarning() << index << viewportPosition << document->read(index - 1, 20)
                   << bufferReadCharacter(index - 1);
    }
    Q_ASSERT(index == 0 || bufferReadCharacter(index - 1) == '\n');
    const int max = bufferPosition + buffer.size();
    const int lineStart = index;
    while (index < max && bufferReadCharacter(index) != '\n')
        ++index;

    const QString string = buffer.mid(lineStart - bufferPosition, index - lineStart);
    Q_ASSERT(string.size() == index - lineStart);
    Q_ASSERT(!string.contains('\n'));
    if (index < max)
        ++index; // for the newline
    textLayout->setText(string);

    QList<QTextLayout::FormatRange> sectionFormats;
    if (sections) {
        do {
            Q_ASSERT(!sections->isEmpty());
            Section *l = sections->first();
            Q_ASSERT(l->position() + l->size() >= lineStart);
            if (l->position() >= index) {
                break;
            }
            // section is in this QTextLayout
            QTextLayout::FormatRange range;
            range.start = qMax(0, l->position() - lineStart); // offset in QTextLayout
            range.length = qMin(l->position() + l->size(), index) - lineStart - range.start;
            range.format = l->format();
            sectionFormats.append(range);
            if (l->position() + l->size() >= index) { // > ### ???
                // means section didn't end here. It continues in the next QTextLayout
                break;
            }
            sections->removeFirst();
        } while (!sections->isEmpty());
    }

    int leftMargin = LeftMargin;
    int rightMargin = 0;
    int topMargin = 0;
    int bottomMargin = 0;
    if (syntaxHighlighter) {
        syntaxHighlighter->d->currentBlockPosition = lineStart;
        syntaxHighlighter->d->formatRanges.clear();
        syntaxHighlighter->highlightBlock(string);
        if (syntaxHighlighter->d->blockFormat.isValid()) {
            blockFormats[textLayout] = syntaxHighlighter->d->blockFormat;
            if (syntaxHighlighter->d->blockFormat.hasProperty(QTextFormat::BlockLeftMargin))
                leftMargin = syntaxHighlighter->d->blockFormat.leftMargin();
            if (syntaxHighlighter->d->blockFormat.hasProperty(QTextFormat::BlockRightMargin))
                rightMargin = syntaxHighlighter->d->blockFormat.rightMargin();
            if (syntaxHighlighter->d->blockFormat.hasProperty(QTextFormat::BlockTopMargin))
                topMargin = syntaxHighlighter->d->blockFormat.topMargin();
            if (syntaxHighlighter->d->blockFormat.hasProperty(QTextFormat::BlockBottomMargin))
                bottomMargin = syntaxHighlighter->d->blockFormat.bottomMargin();

        }
        syntaxHighlighter->d->previousBlockState = syntaxHighlighter->d->currentBlockState;
    }

    textLayout->beginLayout();

    forever {
        QTextLine line = textLayout->createLine();
        if (!line.isValid()) {
            break;
        }
        line.setLineWidth(viewportWidth() - (leftMargin + rightMargin));
        // ### support blockformat margins etc
        int y = topMargin + lastBottomMargin;
        if (!lines.isEmpty()) {
            y += int(lines.last().second.rect().bottom());
            // QTextLine doesn't seem to get its rect() update until a
            // new line has been created (or presumably in endLayout)
        }
        line.setPosition(QPoint(leftMargin, y));
        lines.append(qMakePair(lineStart + line.textStart(), line));
    }
    if (syntaxHighlighter) {
        textLayout->setAdditionalFormats(syntaxHighlighter->d->formatRanges + sectionFormats);
        // do block format
    } else if (!sectionFormats.isEmpty()) {
        textLayout->setAdditionalFormats(sectionFormats);
    }
    lastBottomMargin = bottomMargin;

    textLayout->endLayout();
#ifndef QT_NO_DEBUG
    for (int i=1; i<lines.size(); ++i) {
        Q_ASSERT(lines.at(i).first - (lines.at(i - 1).first + lines.at(i - 1).second.textLength()) <= 1);
    }
#endif

    contentRect |= textLayout->boundingRect().toRect();
    Q_ASSERT(contentRect.right() <= viewportWidth() + LeftMargin);

    if (syntaxHighlighter) {
        syntaxHighlighter->d->formatRanges.clear();
        syntaxHighlighter->d->blockFormat = QTextBlockFormat();
        syntaxHighlighter->d->currentBlockPosition = -1;
    }

    return index;
}

int TextLayout::textPositionAt(const QPoint &p) const
{
    QPoint pos = p;
    if (pos.x() >= 0 && pos.x() < LeftMargin)
        pos.rx() = LeftMargin; // clicking in the margin area should count as the first characters

    int textLayoutOffset = viewportPosition;
    foreach(const QTextLayout *l, textLayouts) {
        if (l->boundingRect().toRect().contains(pos)) {
            const int lineCount = l->lineCount();
            for (int i=0; i<lineCount; ++i) {
                const QTextLine line = l->lineAt(i);
                if (line.y() <= pos.y() && pos.y() <= line.height() + line.y()) { // ### < ???
                    return textLayoutOffset + line.xToCursor(qMax<int>(LeftMargin, pos.x()));
                }
            }
        }
        textLayoutOffset += l->text().size() + 1; // + 1 for newlines which aren't in the QTextLayout
    }
    return -1;
}

QList<Section*> TextLayout::relayoutCommon()
{
    Q_ASSERT(layoutDirty);
    layoutDirty = false;
    Q_ASSERT(document);
    lines.clear();
    unusedTextLayouts = textLayouts;
    textLayouts.clear();
    contentRect = QRect();
    visibleLines = lastVisibleCharacter = -1;

    if (syntaxHighlighter) {
        syntaxHighlighter->d->previousBlockState = syntaxHighlighter->d->currentBlockState = -1;
    }

    if (viewportPosition < bufferPosition
        || (bufferPosition + buffer.size() < document->documentSize()
            && buffer.size() - bufferOffset() < MinimumBufferSize)) {
        bufferPosition = qMax(0, viewportPosition - MinimumBufferSize);
        buffer = document->read(bufferPosition, int(MinimumBufferSize * 2.5));
        sections = document->sections(bufferPosition, buffer.size(), TextDocument::IncludePartial);
    } else if (sectionsDirty) {
        sections = document->sections(bufferPosition, buffer.size(), TextDocument::IncludePartial);
    }
    sectionsDirty = false;
    QList<Section*> l = sections;
    while (!l.isEmpty() && l.first()->position() + l.first()->size() < viewportPosition)
        l.takeFirst(); // could cache these as well
    return l;
}

void TextLayout::relayoutByGeometry(int height)
{
    if (!layoutDirty)
        return;

    QList<Section*> l = relayoutCommon();

    const int max = viewportPosition + buffer.size() - bufferOffset(); // in document coordinates
    Q_ASSERT(viewportPosition == 0 || bufferReadCharacter(viewportPosition - 1) == '\n');

    static const int extraLines = qMax(2, qgetenv("LAZYTEXTEDIT_EXTRA_LINES").toInt());
    int index = viewportPosition;
    while (index < max) {
        index = doLayout(index, l.isEmpty() ? 0 : &l);
        Q_ASSERT(index == max || document->readCharacter(index - 1) == '\n');
        Q_ASSERT(!textLayouts.isEmpty());
        const int y = int(textLayouts.last()->boundingRect().bottom());
        if (y >= height) {
            if (visibleLines == -1) {
                visibleLines = lines.size();
                lastVisibleCharacter = index;
            } else if (lines.size() >= visibleLines + extraLines) {
                break;
            }
        }
    }
    if (visibleLines == -1) {
        visibleLines = lines.size();
        lastVisibleCharacter = index;
    }

    layoutEnd = qMin(index, max);
    qDeleteAll(unusedTextLayouts);
    unusedTextLayouts.clear();
    Q_ASSERT(viewportPosition < layoutEnd ||
             (viewportPosition == layoutEnd && viewportPosition == document->documentSize()));
}

void TextLayout::relayoutByPosition(int size)
{
    if (!layoutDirty)
        return;

    QList<Section*> l = relayoutCommon();

    const int max = viewportPosition + qMin(size, buffer.size() - bufferOffset());
    Q_ASSERT(viewportPosition == 0 || bufferReadCharacter(viewportPosition - 1) == '\n');
    int index = viewportPosition;
    while (index < max) {
        index = doLayout(index, l.isEmpty() ? 0 : &l);
    }
    layoutEnd = index;

    qDeleteAll(unusedTextLayouts);
    unusedTextLayouts.clear();
    Q_ASSERT(viewportPosition < layoutEnd ||
             (viewportPosition == layoutEnd && viewportPosition == document->documentSize()));
}

QTextLayout *TextLayout::layoutForPosition(int pos, int *offset, int *index) const
{
    if (offset)
        *offset = -1;
    if (index)
        *index = -1;

    if (textLayouts.isEmpty() || pos < viewportPosition || pos > layoutEnd) {
        return 0;
    }

    int textLayoutOffset = viewportPosition;
    int i = 0;

    foreach(QTextLayout *l, textLayouts) {
        if (pos >= textLayoutOffset && pos <= l->text().size() + textLayoutOffset) {
            if (offset)
                *offset = pos - textLayoutOffset;
            if (index)
                *index = i;
            return l;
        }
        ++i;
        textLayoutOffset += l->text().size() + 1;
    }
    return 0;
}

QTextLine TextLayout::lineForPosition(int pos, int *offsetInLine, int *lineIndex) const
{
    if (offsetInLine)
        *offsetInLine = -1;
    if (lineIndex)
        *lineIndex = -1;

    if (pos < viewportPosition || pos >= layoutEnd || textLayouts.isEmpty() || lines.isEmpty()) {
        return QTextLine();
    }
    for (int i=0; i<lines.size(); ++i) {
        const QPair<int, QTextLine> &line = lines.at(i);
        const int lineEnd = line.first + line.second.textLength() + 1; // 1 is for newline characteru
        if (pos < lineEnd) {
            if (offsetInLine) {
                *offsetInLine = pos - line.first;
                Q_ASSERT(*offsetInLine >= 0);
                Q_ASSERT(*offsetInLine < lineEnd + pos);
            }
            if (lineIndex) {
                *lineIndex = i;
            }
            return line.second;
        }
    }
    qWarning() << "Couldn't find a line for" << pos << "viewportPosition" << viewportPosition
               << "layoutEnd" << layoutEnd;
    Q_ASSERT(0);
    return QTextLine();
}

// pos is not necessarily on a newline. Finds closest newline in the
// right direction and sets viewportPosition to that. Updates
// scrollbars if this is a TextEditPrivate

void TextLayout::updatePosition(int pos, Direction direction)
{
    if (document->documentSize() == 0) {
        viewportPosition = 0;
    } else {
        Q_ASSERT(document->documentSize() > 0);
        int index = document->find('\n', qMax(0, pos + (direction == Backward ? -1 : 0)),
                                   TextDocument::FindMode(direction)).position();
        if (index == -1) {
            if (direction == Backward) {
                index = 0;
            } else {
                index = document->find('\n', document->documentSize() - 1, TextDocument::FindBackward).position() + 1;
                // position after last newline in document
            }
        } else {
            ++index;
        }
        Q_ASSERT(index != -1);
        viewportPosition = index;

        if (viewportPosition != 0 && document->read(viewportPosition - 1, 1) != QString("\n"))
            qWarning() << "viewportPosition" << viewportPosition << document->read(viewportPosition - 1, 10) << this;
        Q_ASSERT(viewportPosition == 0 || document->read(viewportPosition - 1, 1) == QString("\n"));
    }
    dirty(viewportWidth());

    if (textEdit) {
        TextEditPrivate *p = static_cast<TextEditPrivate*>(this);
        p->pendingScrollBarUpdate = true;
        p->updateCursorPosition(p->lastHoverPos);
        if (!textEdit->verticalScrollBar()->isSliderDown()) {
            p->updateScrollBar();
        } // sliderReleased is connected to updateScrollBar()
    }
}

#ifndef QT_NO_DEBUG_STREAM
QDebug &operator<<(QDebug &str, const QTextLine &line)
{
    if (!line.isValid()) {
        str << "QTextLine() (invalid)";
        return str;
    }
    str.space() << "lineNumber" << line.lineNumber()
                << "textStart" << line.textStart()
                << "textLength" << line.textLength()
                << "position" << line.position();
    return str;
}

#endif
<commit_msg>Oops. Belonged to the last commit.<commit_after>#include "textlayout_p.h"
#include "textedit_p.h"
#include "textdocument.h"
#include "textedit.h"

void TextLayout::dirty(int width)
{
    viewport = width;
    layoutDirty = true;
    if (textEdit)
        textEdit->viewport()->update();
}

int TextLayout::viewportWidth() const
{
    return textEdit ? textEdit->viewport()->width() : viewport;
}

int TextLayout::doLayout(int index, QList<Section*> *sections) // index is in document coordinates
{
    QTextLayout *textLayout = 0;
    if (!unusedTextLayouts.isEmpty()) {
        textLayout = unusedTextLayouts.takeLast();
        textLayout->clearAdditionalFormats();
    } else {
        textLayout = new QTextLayout;
        textLayout->setFont(font);
        QTextOption option;
        option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
        textLayout->setTextOption(option);
    }
    textLayouts.append(textLayout);
    if (index != 0 && bufferReadCharacter(index - 1) != '\n') {
        qWarning() << index << viewportPosition << document->read(index - 1, 20)
                   << bufferReadCharacter(index - 1);
    }
    Q_ASSERT(index == 0 || bufferReadCharacter(index - 1) == '\n');
    const int max = bufferPosition + buffer.size();
    const int lineStart = index;
    while (index < max && bufferReadCharacter(index) != '\n')
        ++index;

    const QString string = buffer.mid(lineStart - bufferPosition, index - lineStart);
    Q_ASSERT(string.size() == index - lineStart);
    Q_ASSERT(!string.contains('\n'));
    if (index < max)
        ++index; // for the newline
    textLayout->setText(string);

    QList<QTextLayout::FormatRange> sectionFormats;
    if (sections) {
        do {
            Q_ASSERT(!sections->isEmpty());
            Section *l = sections->first();
            Q_ASSERT(l->position() + l->size() >= lineStart);
            if (l->position() >= index) {
                break;
            }
            // section is in this QTextLayout
            QTextLayout::FormatRange range;
            range.start = qMax(0, l->position() - lineStart); // offset in QTextLayout
            range.length = qMin(l->position() + l->size(), index) - lineStart - range.start;
            range.format = l->format();
            sectionFormats.append(range);
            if (l->position() + l->size() >= index) { // > ### ???
                // means section didn't end here. It continues in the next QTextLayout
                break;
            }
            sections->removeFirst();
        } while (!sections->isEmpty());
    }

    int leftMargin = LeftMargin;
    int rightMargin = 0;
    int topMargin = 0;
    int bottomMargin = 0;
    if (syntaxHighlighter) {
        syntaxHighlighter->d->currentBlockPosition = lineStart;
        syntaxHighlighter->d->formatRanges.clear();
        syntaxHighlighter->d->currentBlockSize = string.size();
        syntaxHighlighter->highlightBlock(string);
        syntaxHighlighter->d->currentBlockSize = -1;
        if (syntaxHighlighter->d->blockFormat.isValid()) {
            blockFormats[textLayout] = syntaxHighlighter->d->blockFormat;
            if (syntaxHighlighter->d->blockFormat.hasProperty(QTextFormat::BlockLeftMargin))
                leftMargin = syntaxHighlighter->d->blockFormat.leftMargin();
            if (syntaxHighlighter->d->blockFormat.hasProperty(QTextFormat::BlockRightMargin))
                rightMargin = syntaxHighlighter->d->blockFormat.rightMargin();
            if (syntaxHighlighter->d->blockFormat.hasProperty(QTextFormat::BlockTopMargin))
                topMargin = syntaxHighlighter->d->blockFormat.topMargin();
            if (syntaxHighlighter->d->blockFormat.hasProperty(QTextFormat::BlockBottomMargin))
                bottomMargin = syntaxHighlighter->d->blockFormat.bottomMargin();

        }
        syntaxHighlighter->d->previousBlockState = syntaxHighlighter->d->currentBlockState;
    }

    textLayout->beginLayout();

    forever {
        QTextLine line = textLayout->createLine();
        if (!line.isValid()) {
            break;
        }
        line.setLineWidth(viewportWidth() - (leftMargin + rightMargin));
        // ### support blockformat margins etc
        int y = topMargin + lastBottomMargin;
        if (!lines.isEmpty()) {
            y += int(lines.last().second.rect().bottom());
            // QTextLine doesn't seem to get its rect() update until a
            // new line has been created (or presumably in endLayout)
        }
        line.setPosition(QPoint(leftMargin, y));
        lines.append(qMakePair(lineStart + line.textStart(), line));
    }
    if (syntaxHighlighter) {
        textLayout->setAdditionalFormats(syntaxHighlighter->d->formatRanges + sectionFormats);
        // do block format
    } else if (!sectionFormats.isEmpty()) {
        textLayout->setAdditionalFormats(sectionFormats);
    }
    lastBottomMargin = bottomMargin;

    textLayout->endLayout();
#ifndef QT_NO_DEBUG
    for (int i=1; i<lines.size(); ++i) {
        Q_ASSERT(lines.at(i).first - (lines.at(i - 1).first + lines.at(i - 1).second.textLength()) <= 1);
    }
#endif

    contentRect |= textLayout->boundingRect().toRect();
    Q_ASSERT(contentRect.right() <= viewportWidth() + LeftMargin);

    if (syntaxHighlighter) {
        syntaxHighlighter->d->formatRanges.clear();
        syntaxHighlighter->d->blockFormat = QTextBlockFormat();
        syntaxHighlighter->d->currentBlockPosition = -1;
    }

    return index;
}

int TextLayout::textPositionAt(const QPoint &p) const
{
    QPoint pos = p;
    if (pos.x() >= 0 && pos.x() < LeftMargin)
        pos.rx() = LeftMargin; // clicking in the margin area should count as the first characters

    int textLayoutOffset = viewportPosition;
    foreach(const QTextLayout *l, textLayouts) {
        if (l->boundingRect().toRect().contains(pos)) {
            const int lineCount = l->lineCount();
            for (int i=0; i<lineCount; ++i) {
                const QTextLine line = l->lineAt(i);
                if (line.y() <= pos.y() && pos.y() <= line.height() + line.y()) { // ### < ???
                    return textLayoutOffset + line.xToCursor(qMax<int>(LeftMargin, pos.x()));
                }
            }
        }
        textLayoutOffset += l->text().size() + 1; // + 1 for newlines which aren't in the QTextLayout
    }
    return -1;
}

QList<Section*> TextLayout::relayoutCommon()
{
    Q_ASSERT(layoutDirty);
    layoutDirty = false;
    Q_ASSERT(document);
    lines.clear();
    unusedTextLayouts = textLayouts;
    textLayouts.clear();
    contentRect = QRect();
    visibleLines = lastVisibleCharacter = -1;

    if (syntaxHighlighter) {
        syntaxHighlighter->d->previousBlockState = syntaxHighlighter->d->currentBlockState = -1;
    }

    if (viewportPosition < bufferPosition
        || (bufferPosition + buffer.size() < document->documentSize()
            && buffer.size() - bufferOffset() < MinimumBufferSize)) {
        bufferPosition = qMax(0, viewportPosition - MinimumBufferSize);
        buffer = document->read(bufferPosition, int(MinimumBufferSize * 2.5));
        sections = document->sections(bufferPosition, buffer.size(), TextDocument::IncludePartial);
    } else if (sectionsDirty) {
        sections = document->sections(bufferPosition, buffer.size(), TextDocument::IncludePartial);
    }
    sectionsDirty = false;
    QList<Section*> l = sections;
    while (!l.isEmpty() && l.first()->position() + l.first()->size() < viewportPosition)
        l.takeFirst(); // could cache these as well
    return l;
}

void TextLayout::relayoutByGeometry(int height)
{
    if (!layoutDirty)
        return;

    QList<Section*> l = relayoutCommon();

    const int max = viewportPosition + buffer.size() - bufferOffset(); // in document coordinates
    Q_ASSERT(viewportPosition == 0 || bufferReadCharacter(viewportPosition - 1) == '\n');

    static const int extraLines = qMax(2, qgetenv("LAZYTEXTEDIT_EXTRA_LINES").toInt());
    int index = viewportPosition;
    while (index < max) {
        index = doLayout(index, l.isEmpty() ? 0 : &l);
        Q_ASSERT(index == max || document->readCharacter(index - 1) == '\n');
        Q_ASSERT(!textLayouts.isEmpty());
        const int y = int(textLayouts.last()->boundingRect().bottom());
        if (y >= height) {
            if (visibleLines == -1) {
                visibleLines = lines.size();
                lastVisibleCharacter = index;
            } else if (lines.size() >= visibleLines + extraLines) {
                break;
            }
        }
    }
    if (visibleLines == -1) {
        visibleLines = lines.size();
        lastVisibleCharacter = index;
    }

    layoutEnd = qMin(index, max);
    qDeleteAll(unusedTextLayouts);
    unusedTextLayouts.clear();
    Q_ASSERT(viewportPosition < layoutEnd ||
             (viewportPosition == layoutEnd && viewportPosition == document->documentSize()));
}

void TextLayout::relayoutByPosition(int size)
{
    if (!layoutDirty)
        return;

    QList<Section*> l = relayoutCommon();

    const int max = viewportPosition + qMin(size, buffer.size() - bufferOffset());
    Q_ASSERT(viewportPosition == 0 || bufferReadCharacter(viewportPosition - 1) == '\n');
    int index = viewportPosition;
    while (index < max) {
        index = doLayout(index, l.isEmpty() ? 0 : &l);
    }
    layoutEnd = index;

    qDeleteAll(unusedTextLayouts);
    unusedTextLayouts.clear();
    Q_ASSERT(viewportPosition < layoutEnd ||
             (viewportPosition == layoutEnd && viewportPosition == document->documentSize()));
}

QTextLayout *TextLayout::layoutForPosition(int pos, int *offset, int *index) const
{
    if (offset)
        *offset = -1;
    if (index)
        *index = -1;

    if (textLayouts.isEmpty() || pos < viewportPosition || pos > layoutEnd) {
        return 0;
    }

    int textLayoutOffset = viewportPosition;
    int i = 0;

    foreach(QTextLayout *l, textLayouts) {
        if (pos >= textLayoutOffset && pos <= l->text().size() + textLayoutOffset) {
            if (offset)
                *offset = pos - textLayoutOffset;
            if (index)
                *index = i;
            return l;
        }
        ++i;
        textLayoutOffset += l->text().size() + 1;
    }
    return 0;
}

QTextLine TextLayout::lineForPosition(int pos, int *offsetInLine, int *lineIndex) const
{
    if (offsetInLine)
        *offsetInLine = -1;
    if (lineIndex)
        *lineIndex = -1;

    if (pos < viewportPosition || pos >= layoutEnd || textLayouts.isEmpty() || lines.isEmpty()) {
        return QTextLine();
    }
    for (int i=0; i<lines.size(); ++i) {
        const QPair<int, QTextLine> &line = lines.at(i);
        const int lineEnd = line.first + line.second.textLength() + 1; // 1 is for newline characteru
        if (pos < lineEnd) {
            if (offsetInLine) {
                *offsetInLine = pos - line.first;
                Q_ASSERT(*offsetInLine >= 0);
                Q_ASSERT(*offsetInLine < lineEnd + pos);
            }
            if (lineIndex) {
                *lineIndex = i;
            }
            return line.second;
        }
    }
    qWarning() << "Couldn't find a line for" << pos << "viewportPosition" << viewportPosition
               << "layoutEnd" << layoutEnd;
    Q_ASSERT(0);
    return QTextLine();
}

// pos is not necessarily on a newline. Finds closest newline in the
// right direction and sets viewportPosition to that. Updates
// scrollbars if this is a TextEditPrivate

void TextLayout::updatePosition(int pos, Direction direction)
{
    if (document->documentSize() == 0) {
        viewportPosition = 0;
    } else {
        Q_ASSERT(document->documentSize() > 0);
        int index = document->find('\n', qMax(0, pos + (direction == Backward ? -1 : 0)),
                                   TextDocument::FindMode(direction)).position();
        if (index == -1) {
            if (direction == Backward) {
                index = 0;
            } else {
                index = document->find('\n', document->documentSize() - 1, TextDocument::FindBackward).position() + 1;
                // position after last newline in document
            }
        } else {
            ++index;
        }
        Q_ASSERT(index != -1);
        viewportPosition = index;

        if (viewportPosition != 0 && document->read(viewportPosition - 1, 1) != QString("\n"))
            qWarning() << "viewportPosition" << viewportPosition << document->read(viewportPosition - 1, 10) << this;
        Q_ASSERT(viewportPosition == 0 || document->read(viewportPosition - 1, 1) == QString("\n"));
    }
    dirty(viewportWidth());

    if (textEdit) {
        TextEditPrivate *p = static_cast<TextEditPrivate*>(this);
        p->pendingScrollBarUpdate = true;
        p->updateCursorPosition(p->lastHoverPos);
        if (!textEdit->verticalScrollBar()->isSliderDown()) {
            p->updateScrollBar();
        } // sliderReleased is connected to updateScrollBar()
    }
}

#ifndef QT_NO_DEBUG_STREAM
QDebug &operator<<(QDebug &str, const QTextLine &line)
{
    if (!line.isValid()) {
        str << "QTextLine() (invalid)";
        return str;
    }
    str.space() << "lineNumber" << line.lineNumber()
                << "textStart" << line.textStart()
                << "textLength" << line.textLength()
                << "position" << line.position();
    return str;
}

#endif
<|endoftext|>


<commit_before><commit_msg>INTEGRATION: CWS pdf01 (1.58.26); FILE MERGED 2004/08/12 11:59:51 pl 1.58.26.1: assert only when needed<commit_after><|endoftext|>


<commit_before>/*
 * Copyright (c) [2004-2015] Novell, Inc.
 * Copyright (c) [2017-2020] SUSE LLC
 *
 * All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2 of the GNU General Public License as published
 * by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, contact Novell, Inc.
 *
 * To contact Novell about this file by physical or electronic mail, you may
 * find current contact information at www.novell.com.
 */

#include <locale>
#include <boost/algorithm/string.hpp>

#include "storage/Utils/StorageTypes.h"
#include "storage/Utils/SystemCmd.h"
#include "storage/Utils/LoggerImpl.h"
#include "storage/Utils/StorageDefines.h"
#include "storage/Utils/ExceptionImpl.h"
#include "storage/SystemInfo/CmdBtrfs.h"
#include "storage/Filesystems/BtrfsImpl.h"

namespace storage
{
    using namespace std;

    CmdBtrfsFilesystemShow::CmdBtrfsFilesystemShow()
    {
	SystemCmd::Options cmd_options(BTRFS_BIN " filesystem show", SystemCmd::DoThrow);
	cmd_options.verify = [](int) { return true; };

	SystemCmd cmd(cmd_options);

	if ( cmd.retcode() == 0 && !cmd.stdout().empty() )
	    parse( cmd.stdout() );
	else if ( ! cmd.stderr().empty() )
	{
	    ST_THROW( SystemCmdException( &cmd, "'btrfs filesystem show' complains: "
					  + cmd.stderr().front() ) );
	}

	// Intentionally not throwing an exception here if retcode != 0 since
	// this command might return 1 if no btrfs at all was found -- which is
	// not an error condition: We are probing here to determine if there
	// are any btrfs file systems, and if yes, some more information about
	// them.

	// stdout is rarely empty for this command since in almost all cases it
	// at least reports its version number, so this is also not very useful
	// to indicate errors.
    }

    void
    CmdBtrfsFilesystemShow::parse(const vector<string>& lines)
    {
	static const regex uuid_regex("uuid: (" UUID_REGEX ")", regex::extended);

	smatch match;

	vector<string>::const_iterator it = lines.begin();

	while (it != lines.end())
	{
	    while( it != lines.end() && !boost::contains( *it, " uuid: " ))
		++it;

	    if( it!=lines.end() )
	    {
		y2mil( "uuid line:" << *it );

		Entry entry;

		if (!regex_search(*it, match, uuid_regex))
		    ST_THROW(Exception("did not find uuid"));

		entry.uuid = match[1];
		y2mil("uuid:" << entry.uuid);

		++it;
		while( it!=lines.end() && !boost::contains( *it, " uuid: " ) &&
		       !boost::contains( *it, "devid " ) )
		    ++it;

		while( it!=lines.end() && boost::contains( *it, "devid " ) )
		{
		    y2mil( "devs line:" << *it );

		    Device device;

		    extractNthWord(1, *it) >> device.id;

		    device.name = extractNthWord(7, *it);
		    if (!boost::contains(device.name, DEV_DIR "/"))  // Allow /sys/dev or /proc/devices
			ST_THROW( ParseException( "Not a valid device name", device.name, "/dev/..." ) );

		    entry.devices.push_back( device );
		    ++it;
		}

		if ( entry.devices.empty() )
		{
		    ST_THROW( ParseException( "No devices for UUID " + entry.uuid, "",
					      "devid  1 size 40.00GiB used 16.32GiB path /dev/sda2" ) );
		}

		y2mil("devices:" << entry.devices);

		data.push_back(entry);
	    }
	}

	y2mil(*this);
    }

    CmdBtrfsFilesystemShow::const_iterator
    CmdBtrfsFilesystemShow::find_by_uuid(const string& uuid) const
    {
	return find_if(begin(), end(), [&uuid](const Entry& entry){ return entry.uuid == uuid; });
    }

    std::ostream&
    operator<<(std::ostream& s, const CmdBtrfsFilesystemShow& cmd_btrfs_filesystem_show)
    {
	for (const CmdBtrfsFilesystemShow::Entry& entry : cmd_btrfs_filesystem_show)
	    s << entry << '\n';

	return s;
    }

    std::ostream&
    operator<<(std::ostream& s, const CmdBtrfsFilesystemShow::Entry& entry)
    {
	return s << "uuid:" << entry.uuid << " devices:" << entry.devices;
    }

    std::ostream&
    operator<<(std::ostream& s, const CmdBtrfsFilesystemShow::Device& device)
    {
	return s << "{ id:" << device.id << " name:" << device.name << " }";
    }

    CmdBtrfsSubvolumeList::CmdBtrfsSubvolumeList(const key_t& key, const string& mount_point)
    {
	SystemCmd::Options cmd_options(BTRFS_BIN " subvolume list -a -p " + quote(mount_point),
				       SystemCmd::DoThrow);
	cmd_options.mockup_key = BTRFS_BIN " subvolume list -a -p (device:" + key + ")";

	SystemCmd cmd(cmd_options);
	if (cmd.retcode() == 0)
	    parse(cmd.stdout());
	else
	    ST_THROW(SystemCmdException(&cmd, "'btrfs subvolume list' failed, ret: " +
					to_string(cmd.retcode())));
    }

    void
    CmdBtrfsSubvolumeList::parse(const vector<string>& lines)
    {
	for (const string& line : lines)
	{
	    Entry entry;

	    string::size_type pos1 = line.find("ID ");
	    line.substr(pos1 + 3) >> entry.id;

	    string::size_type pos2 = line.find(" parent ");
	    line.substr(pos2 + 8) >> entry.parent_id;

	    // Subvolume can already be deleted, in which case parent is "0"
	    // (and path "DELETED"). That is a temporary state.
	    if (entry.parent_id == 0)
		continue;

	    string::size_type pos3 = line.find(" path ");
	    entry.path = line.substr(pos3 + 6);
	    if (boost::starts_with(entry.path, "<FS_TREE>/"))
		entry.path.erase(0, 10);

	    data.push_back(entry);
	}

	y2mil(*this);
    }

    CmdBtrfsSubvolumeList::const_iterator
    CmdBtrfsSubvolumeList::find_entry_by_path(const string& path) const
    {
	for (const_iterator it = data.begin(); it != data.end(); ++it)
	{
	    if (it->path == path)
		return it;
	}

	return data.end();
    }

    std::ostream&
    operator<<(std::ostream& s, const CmdBtrfsSubvolumeList& cmd_btrfs_subvolume_list)
    {
	for (const CmdBtrfsSubvolumeList::Entry& entry : cmd_btrfs_subvolume_list)
	    s << entry;

	return s;
    }

    std::ostream&
    operator<<(std::ostream& s, const CmdBtrfsSubvolumeList::Entry& entry)
    {
	s << "id:" << entry.id << " parent-id:" << entry.parent_id
	  << " path:" << entry.path << '\n';

	return s;
    }

    CmdBtrfsSubvolumeGetDefault::CmdBtrfsSubvolumeGetDefault(const key_t& key, const string& mount_point)
	: id(BtrfsSubvolume::Impl::unknown_id)
    {
	SystemCmd::Options cmd_options(BTRFS_BIN " subvolume get-default " + quote(mount_point),
				       SystemCmd::DoThrow);
	cmd_options.mockup_key = BTRFS_BIN " subvolume get-default (device:" + key + ")";

	SystemCmd cmd(cmd_options);
	if (cmd.retcode() == 0)
	    parse(cmd.stdout());
	else
	    ST_THROW(SystemCmdException(&cmd, "'btrfs subvolume get-default' failed, ret: " +
					to_string(cmd.retcode())));
    }

    void
    CmdBtrfsSubvolumeGetDefault::parse(const vector<string>& lines)
    {
	if (lines.size() != 1)
	    ST_THROW(Exception("output has wrong number of lines"));

	const string& line = lines[0];

	if (!boost::starts_with(line, "ID "))
	    ST_THROW(Exception("output does not start with ID"));

	line.substr(3) >> id;

	y2mil(*this);
    }

    std::ostream&
    operator<<(std::ostream& s, const CmdBtrfsSubvolumeGetDefault& cmd_btrfs_subvolume_get_default)
    {
	s << "id:" << cmd_btrfs_subvolume_get_default.id;

	return s;
    }

    CmdBtrfsFilesystemDf::CmdBtrfsFilesystemDf(const key_t& key, const string& mount_point)
	: metadata_raid_level(BtrfsRaidLevel::UNKNOWN), data_raid_level(BtrfsRaidLevel::UNKNOWN)
    {
	SystemCmd::Options cmd_options(BTRFS_BIN " filesystem df " + quote(mount_point),
				       SystemCmd::DoThrow);
	cmd_options.mockup_key = BTRFS_BIN " filesystem df (device:" + key + ")";

	SystemCmd cmd(cmd_options);
	if (cmd.retcode() == 0)
	    parse(cmd.stdout());
	else
	    ST_THROW(SystemCmdException(&cmd, "'btrfs filesystem df' failed, ret: " +
					to_string(cmd.retcode())));
    }

    void
    CmdBtrfsFilesystemDf::parse(const vector<string>& lines)
    {
	static const regex metadata_rx("Metadata, ([A-Za-z0-9]+):.*", regex::extended);
	static const regex data_rx("Data, ([A-Za-z0-9]+):.*", regex::extended);
	static const regex mixed_rx("Data\\+Metadata, ([A-Za-z0-9]+):.*", regex::extended);

	smatch match;

	for (const string& line : lines)
	{
	    if (regex_match(line, match, metadata_rx) && match.size() == 2)
	    {
		string tmp = boost::to_upper_copy(match[1].str(), locale::classic());
		metadata_raid_level = toValueWithFallback(tmp, BtrfsRaidLevel::UNKNOWN);
	    }

	    if (regex_match(line, match, data_rx) && match.size() == 2)
	    {
		string tmp = boost::to_upper_copy(match[1].str(), locale::classic());
		data_raid_level = toValueWithFallback(tmp, BtrfsRaidLevel::UNKNOWN);
	    }

	    if (regex_match(line, match, mixed_rx) && match.size() == 2)
	    {
		string tmp = boost::to_upper_copy(match[1].str(), locale::classic());
		metadata_raid_level = data_raid_level = toValueWithFallback(tmp, BtrfsRaidLevel::UNKNOWN);
	    }
	}

	y2mil(*this);
    }

    std::ostream&
    operator<<(std::ostream& s, const CmdBtrfsFilesystemDf& cmd_btrfs_filesystem_df)
    {
	s << "metadata-raid-level:" << toString(cmd_btrfs_filesystem_df.metadata_raid_level)
	  << " data-raid-level:" << toString(cmd_btrfs_filesystem_df.data_raid_level);

	return s;
    }

}
<commit_msg>- added sanity checks<commit_after>/*
 * Copyright (c) [2004-2015] Novell, Inc.
 * Copyright (c) [2017-2020] SUSE LLC
 *
 * All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2 of the GNU General Public License as published
 * by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, contact Novell, Inc.
 *
 * To contact Novell about this file by physical or electronic mail, you may
 * find current contact information at www.novell.com.
 */

#include <locale>
#include <boost/algorithm/string.hpp>

#include "storage/Utils/StorageTypes.h"
#include "storage/Utils/SystemCmd.h"
#include "storage/Utils/LoggerImpl.h"
#include "storage/Utils/StorageDefines.h"
#include "storage/Utils/ExceptionImpl.h"
#include "storage/SystemInfo/CmdBtrfs.h"
#include "storage/Filesystems/BtrfsImpl.h"

namespace storage
{
    using namespace std;

    CmdBtrfsFilesystemShow::CmdBtrfsFilesystemShow()
    {
	SystemCmd::Options cmd_options(BTRFS_BIN " filesystem show", SystemCmd::DoThrow);
	cmd_options.verify = [](int) { return true; };

	SystemCmd cmd(cmd_options);

	if ( cmd.retcode() == 0 && !cmd.stdout().empty() )
	    parse( cmd.stdout() );
	else if ( ! cmd.stderr().empty() )
	{
	    ST_THROW( SystemCmdException( &cmd, "'btrfs filesystem show' complains: "
					  + cmd.stderr().front() ) );
	}

	// Intentionally not throwing an exception here if retcode != 0 since
	// this command might return 1 if no btrfs at all was found -- which is
	// not an error condition: We are probing here to determine if there
	// are any btrfs file systems, and if yes, some more information about
	// them.

	// stdout is rarely empty for this command since in almost all cases it
	// at least reports its version number, so this is also not very useful
	// to indicate errors.
    }

    void
    CmdBtrfsFilesystemShow::parse(const vector<string>& lines)
    {
	static const regex uuid_regex("uuid: (" UUID_REGEX ")", regex::extended);

	smatch match;

	vector<string>::const_iterator it = lines.begin();

	while (it != lines.end())
	{
	    while( it != lines.end() && !boost::contains( *it, " uuid: " ))
		++it;

	    if( it!=lines.end() )
	    {
		y2mil( "uuid line:" << *it );

		Entry entry;

		if (!regex_search(*it, match, uuid_regex))
		    ST_THROW(Exception("did not find uuid"));

		entry.uuid = match[1];
		y2mil("uuid:" << entry.uuid);

		++it;
		while( it!=lines.end() && !boost::contains( *it, " uuid: " ) &&
		       !boost::contains( *it, "devid " ) )
		    ++it;

		while( it!=lines.end() && boost::contains( *it, "devid " ) )
		{
		    y2mil( "devs line:" << *it );

		    Device device;

		    extractNthWord(1, *it) >> device.id;

		    device.name = extractNthWord(7, *it);
		    if (!boost::contains(device.name, DEV_DIR "/"))  // Allow /sys/dev or /proc/devices
			ST_THROW( ParseException( "Not a valid device name", device.name, "/dev/..." ) );

		    entry.devices.push_back( device );
		    ++it;
		}

		if ( entry.devices.empty() )
		{
		    ST_THROW( ParseException( "No devices for UUID " + entry.uuid, "",
					      "devid  1 size 40.00GiB used 16.32GiB path /dev/sda2" ) );
		}

		y2mil("devices:" << entry.devices);

		data.push_back(entry);
	    }
	}

	y2mil(*this);
    }

    CmdBtrfsFilesystemShow::const_iterator
    CmdBtrfsFilesystemShow::find_by_uuid(const string& uuid) const
    {
	return find_if(begin(), end(), [&uuid](const Entry& entry){ return entry.uuid == uuid; });
    }

    std::ostream&
    operator<<(std::ostream& s, const CmdBtrfsFilesystemShow& cmd_btrfs_filesystem_show)
    {
	for (const CmdBtrfsFilesystemShow::Entry& entry : cmd_btrfs_filesystem_show)
	    s << entry << '\n';

	return s;
    }

    std::ostream&
    operator<<(std::ostream& s, const CmdBtrfsFilesystemShow::Entry& entry)
    {
	return s << "uuid:" << entry.uuid << " devices:" << entry.devices;
    }

    std::ostream&
    operator<<(std::ostream& s, const CmdBtrfsFilesystemShow::Device& device)
    {
	return s << "{ id:" << device.id << " name:" << device.name << " }";
    }

    CmdBtrfsSubvolumeList::CmdBtrfsSubvolumeList(const key_t& key, const string& mount_point)
    {
	SystemCmd::Options cmd_options(BTRFS_BIN " subvolume list -a -p " + quote(mount_point),
				       SystemCmd::DoThrow);
	cmd_options.mockup_key = BTRFS_BIN " subvolume list -a -p (device:" + key + ")";

	SystemCmd cmd(cmd_options);
	if (cmd.retcode() == 0)
	    parse(cmd.stdout());
	else
	    ST_THROW(SystemCmdException(&cmd, "'btrfs subvolume list' failed, ret: " +
					to_string(cmd.retcode())));
    }

    void
    CmdBtrfsSubvolumeList::parse(const vector<string>& lines)
    {
	for (const string& line : lines)
	{
	    Entry entry;

	    string::size_type pos1 = line.find("ID ");
	    if (pos1 == string::npos)
		ST_THROW(Exception("cound not find id"));
	    line.substr(pos1 + 3) >> entry.id;

	    string::size_type pos2 = line.find(" parent ");
	    if (pos2 == string::npos)
		ST_THROW(Exception("cound not find parent"));
	    line.substr(pos2 + 8) >> entry.parent_id;

	    // Subvolume can already be deleted, in which case parent is "0"
	    // (and path "DELETED"). That is a temporary state.
	    if (entry.parent_id == 0)
		continue;

	    string::size_type pos3 = line.find(" path ");
	    if (pos3 == string::npos)
		ST_THROW(Exception("cound not find path"));
	    entry.path = line.substr(pos3 + 6);
	    if (boost::starts_with(entry.path, "<FS_TREE>/"))
		entry.path.erase(0, 10);

	    data.push_back(entry);
	}

	y2mil(*this);
    }

    CmdBtrfsSubvolumeList::const_iterator
    CmdBtrfsSubvolumeList::find_entry_by_path(const string& path) const
    {
	for (const_iterator it = data.begin(); it != data.end(); ++it)
	{
	    if (it->path == path)
		return it;
	}

	return data.end();
    }

    std::ostream&
    operator<<(std::ostream& s, const CmdBtrfsSubvolumeList& cmd_btrfs_subvolume_list)
    {
	for (const CmdBtrfsSubvolumeList::Entry& entry : cmd_btrfs_subvolume_list)
	    s << entry;

	return s;
    }

    std::ostream&
    operator<<(std::ostream& s, const CmdBtrfsSubvolumeList::Entry& entry)
    {
	s << "id:" << entry.id << " parent-id:" << entry.parent_id
	  << " path:" << entry.path << '\n';

	return s;
    }

    CmdBtrfsSubvolumeGetDefault::CmdBtrfsSubvolumeGetDefault(const key_t& key, const string& mount_point)
	: id(BtrfsSubvolume::Impl::unknown_id)
    {
	SystemCmd::Options cmd_options(BTRFS_BIN " subvolume get-default " + quote(mount_point),
				       SystemCmd::DoThrow);
	cmd_options.mockup_key = BTRFS_BIN " subvolume get-default (device:" + key + ")";

	SystemCmd cmd(cmd_options);
	if (cmd.retcode() == 0)
	    parse(cmd.stdout());
	else
	    ST_THROW(SystemCmdException(&cmd, "'btrfs subvolume get-default' failed, ret: " +
					to_string(cmd.retcode())));
    }

    void
    CmdBtrfsSubvolumeGetDefault::parse(const vector<string>& lines)
    {
	if (lines.size() != 1)
	    ST_THROW(Exception("output has wrong number of lines"));

	const string& line = lines[0];

	if (!boost::starts_with(line, "ID "))
	    ST_THROW(Exception("output does not start with ID"));

	line.substr(3) >> id;

	y2mil(*this);
    }

    std::ostream&
    operator<<(std::ostream& s, const CmdBtrfsSubvolumeGetDefault& cmd_btrfs_subvolume_get_default)
    {
	s << "id:" << cmd_btrfs_subvolume_get_default.id;

	return s;
    }

    CmdBtrfsFilesystemDf::CmdBtrfsFilesystemDf(const key_t& key, const string& mount_point)
	: metadata_raid_level(BtrfsRaidLevel::UNKNOWN), data_raid_level(BtrfsRaidLevel::UNKNOWN)
    {
	SystemCmd::Options cmd_options(BTRFS_BIN " filesystem df " + quote(mount_point),
				       SystemCmd::DoThrow);
	cmd_options.mockup_key = BTRFS_BIN " filesystem df (device:" + key + ")";

	SystemCmd cmd(cmd_options);
	if (cmd.retcode() == 0)
	    parse(cmd.stdout());
	else
	    ST_THROW(SystemCmdException(&cmd, "'btrfs filesystem df' failed, ret: " +
					to_string(cmd.retcode())));
    }

    void
    CmdBtrfsFilesystemDf::parse(const vector<string>& lines)
    {
	static const regex metadata_rx("Metadata, ([A-Za-z0-9]+):.*", regex::extended);
	static const regex data_rx("Data, ([A-Za-z0-9]+):.*", regex::extended);
	static const regex mixed_rx("Data\\+Metadata, ([A-Za-z0-9]+):.*", regex::extended);

	smatch match;

	for (const string& line : lines)
	{
	    if (regex_match(line, match, metadata_rx) && match.size() == 2)
	    {
		string tmp = boost::to_upper_copy(match[1].str(), locale::classic());
		metadata_raid_level = toValueWithFallback(tmp, BtrfsRaidLevel::UNKNOWN);
	    }

	    if (regex_match(line, match, data_rx) && match.size() == 2)
	    {
		string tmp = boost::to_upper_copy(match[1].str(), locale::classic());
		data_raid_level = toValueWithFallback(tmp, BtrfsRaidLevel::UNKNOWN);
	    }

	    if (regex_match(line, match, mixed_rx) && match.size() == 2)
	    {
		string tmp = boost::to_upper_copy(match[1].str(), locale::classic());
		metadata_raid_level = data_raid_level = toValueWithFallback(tmp, BtrfsRaidLevel::UNKNOWN);
	    }
	}

	y2mil(*this);
    }

    std::ostream&
    operator<<(std::ostream& s, const CmdBtrfsFilesystemDf& cmd_btrfs_filesystem_df)
    {
	s << "metadata-raid-level:" << toString(cmd_btrfs_filesystem_df.metadata_raid_level)
	  << " data-raid-level:" << toString(cmd_btrfs_filesystem_df.data_raid_level);

	return s;
    }

}
<|endoftext|>


<commit_before><commit_msg>Don't override rclpy._rclpy_pybind11 docs (#863)<commit_after><|endoftext|>


<commit_before>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author:  Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

#include "ValuePrinterSynthesizer.h"

#include "cling/Interpreter/Interpreter.h"
#include "cling/Utils/AST.h"

#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclGroup.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/ExprCXX.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/Sema.h"

#include "llvm/Support/raw_os_ostream.h"

#include <iostream>

using namespace clang;

namespace cling {

  ValuePrinterSynthesizer::ValuePrinterSynthesizer(clang::Sema* S,
                                                   llvm::raw_ostream* Stream)
    : WrapperTransformer(S), m_Context(&S->getASTContext()) {
    if (Stream)
      m_ValuePrinterStream.reset(Stream);
    else
      m_ValuePrinterStream.reset(new llvm::raw_os_ostream(std::cout));
  }

  // pin the vtable here.
  ValuePrinterSynthesizer::~ValuePrinterSynthesizer()
  { }

  ASTTransformer::Result ValuePrinterSynthesizer::Transform(clang::Decl* D) {
    if (getCompilationOpts().ValuePrinting == CompilationOptions::VPDisabled)
      return Result(D, true);

    FunctionDecl* FD = cast<FunctionDecl>(D);
    assert(utils::Analyze::IsWrapper(FD) && "Expected wrapper");
    if (tryAttachVP(FD))
      return Result(FD, true);
    return Result(0, false);
  }

  bool ValuePrinterSynthesizer::tryAttachVP(FunctionDecl* FD) {
    // We have to be able to mark the expression for printout. There are
    // three scenarios:
    // 0: Expression printing disabled - don't do anything just exit.
    // 1: Expression printing enabled - print no matter what.
    // 2: Expression printing auto - analyze - rely on the omitted ';' to
    //    not produce the suppress marker.
    int indexOfLastExpr = -1;
    Expr* To = utils::Analyze::GetOrCreateLastExpr(FD, &indexOfLastExpr,
                                                   /*omitDS*/false,
                                                   m_Sema);
    if (To) {
      // Update the CompoundStmt body, avoiding alloc/dealloc of all the el.
      CompoundStmt* CS = cast<CompoundStmt>(FD->getBody());
      assert(CS && "Missing body?");

      switch (getCompilationOpts().ValuePrinting) {
      case CompilationOptions::VPDisabled:
        assert(0 && "Don't wait that long. Exit early!");
        break;
      case CompilationOptions::VPEnabled:
        break;
      case CompilationOptions::VPAuto: {
        // FIXME: Propagate the flag to the nested transactions also, they
        // must have the same CO as their parents.
        getCompilationOpts().ValuePrinting = CompilationOptions::VPEnabled;
        if ((int)CS->size() > indexOfLastExpr+1
            && (*(CS->body_begin() + indexOfLastExpr + 1))
            && isa<NullStmt>(*(CS->body_begin() + indexOfLastExpr + 1))) {
          // If next is NullStmt disable VP is disabled - exit. Signal this in
          // the CO of the transaction.
          getCompilationOpts().ValuePrinting = CompilationOptions::VPDisabled;
        }
        if (getCompilationOpts().ValuePrinting
            == CompilationOptions::VPDisabled)
          return true;
      }
        break;
      }

      // We can't PushDeclContext, because we don't have scope.
      Sema::ContextRAII pushedDC(*m_Sema, FD);

      if (To) {
        // Strip the parenthesis if any
        if (ParenExpr* PE = dyn_cast<ParenExpr>(To))
          To = PE->getSubExpr();

        Expr* Result = 0;
        // if (!m_Sema->getLangOpts().CPlusPlus)
        //   Result = SynthesizeVP(To);

        if (Result)
          *(CS->body_begin()+indexOfLastExpr) = Result;
      }
      // Clear the artificial NullStmt-s
      if (!ClearNullStmts(CS)) {
        // FIXME: Why it is here? Shouldn't it be in DeclExtractor?
        // if no body remove the wrapper
        DeclContext* DC = FD->getDeclContext();
        Scope* S = m_Sema->getScopeForContext(DC);
        if (S)
          S->RemoveDecl(FD);
        DC->removeDecl(FD);
      }
    }
    else // if nothing to attach to set the CO's ValuePrinting to disabled.
      getCompilationOpts().ValuePrinting = CompilationOptions::VPDisabled;
    return true;
  }

  // We need to artificially create:
  // cling_PrintValue(void* (ASTContext)C, void* (Expr)E, const void* (&i)
  Expr* ValuePrinterSynthesizer::SynthesizeVP(Expr* E) {
    QualType QT = E->getType();
    // For now we skip void and function pointer types.
    if (!QT.isNull() && (QT->isVoidType() || QT->isFunctionType()))
      return 0;

    // Find cling_PrintValue
    if (!m_LookupResult)
      FindAndCacheRuntimeLookupResult(E->getLocStart());

    Expr* VoidEArg = utils::Synthesize::CStyleCastPtrExpr(m_Sema,
                                                          m_Context->VoidPtrTy,
                                                          (uint64_t)E);
    Expr* VoidCArg = utils::Synthesize::CStyleCastPtrExpr(m_Sema,
                                                          m_Context->VoidPtrTy,
                                                          (uint64_t)m_Context);

    SourceLocation NoSLoc = SourceLocation();
    Scope* S = m_Sema->getScopeForContext(m_Sema->CurContext);
    if (!QT->isPointerType()) {
      while(ImplicitCastExpr* ICE = dyn_cast<ImplicitCastExpr>(E))
        E = ICE->getSubExpr();
      E = m_Sema->BuildUnaryOp(S, NoSLoc, UO_AddrOf, E).get();
    }

    llvm::SmallVector<Expr*, 4> CallArgs;
    CallArgs.push_back(VoidEArg);
    CallArgs.push_back(VoidCArg);
    CallArgs.push_back(E);

    CXXScopeSpec CSS;
    Expr* unresolvedLookup
      = m_Sema->BuildDeclarationNameExpr(CSS, *m_LookupResult,
                                         /*ADL*/ false).get();

    Expr* Result = m_Sema->ActOnCallExpr(S, unresolvedLookup, E->getLocStart(),
                                         CallArgs, E->getLocEnd()).get();
    assert(Result && "Cannot create value printer!");

    return Result;
  }

  unsigned ValuePrinterSynthesizer::ClearNullStmts(CompoundStmt* CS) {
    llvm::SmallVector<Stmt*, 8> FBody;
    for (auto&& child: CS->children())
      if (!isa<NullStmt>(child))
        FBody.push_back(child);

    CS->setStmts(*m_Context, FBody);
    return FBody.size();
  }

  void ValuePrinterSynthesizer::FindAndCacheRuntimeLookupResult(
                                                  SourceLocation sourceLoc) {
    assert(!m_LookupResult && "Called multiple times!?");

    DeclarationName PVName = &m_Context->Idents.get("cling_PrintValue");
    m_LookupResult = new LookupResult(*m_Sema, PVName, sourceLoc,
                                      Sema::LookupOrdinaryName,
                                      Sema::ForRedeclaration);

    Scope* S = m_Sema->getScopeForContext(m_Sema->CurContext);
    m_Sema->LookupName(*m_LookupResult, S);
    assert(!m_LookupResult->empty() && "Cannot find cling_PrintValue(...)");
  }

} // namespace cling
<commit_msg>Initialize all pointer members! (Coverity 63235)<commit_after>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author:  Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

#include "ValuePrinterSynthesizer.h"

#include "cling/Interpreter/Interpreter.h"
#include "cling/Utils/AST.h"

#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclGroup.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/ExprCXX.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/Sema.h"

#include "llvm/Support/raw_os_ostream.h"

#include <iostream>

using namespace clang;

namespace cling {

  ValuePrinterSynthesizer::ValuePrinterSynthesizer(clang::Sema* S,
                                                   llvm::raw_ostream* Stream)
    : WrapperTransformer(S), m_Context(&S->getASTContext()),
      m_LookupResult(nullptr) {
    if (Stream)
      m_ValuePrinterStream.reset(Stream);
    else
      m_ValuePrinterStream.reset(new llvm::raw_os_ostream(std::cout));
  }

  // pin the vtable here.
  ValuePrinterSynthesizer::~ValuePrinterSynthesizer()
  { }

  ASTTransformer::Result ValuePrinterSynthesizer::Transform(clang::Decl* D) {
    if (getCompilationOpts().ValuePrinting == CompilationOptions::VPDisabled)
      return Result(D, true);

    FunctionDecl* FD = cast<FunctionDecl>(D);
    assert(utils::Analyze::IsWrapper(FD) && "Expected wrapper");
    if (tryAttachVP(FD))
      return Result(FD, true);
    return Result(0, false);
  }

  bool ValuePrinterSynthesizer::tryAttachVP(FunctionDecl* FD) {
    // We have to be able to mark the expression for printout. There are
    // three scenarios:
    // 0: Expression printing disabled - don't do anything just exit.
    // 1: Expression printing enabled - print no matter what.
    // 2: Expression printing auto - analyze - rely on the omitted ';' to
    //    not produce the suppress marker.
    int indexOfLastExpr = -1;
    Expr* To = utils::Analyze::GetOrCreateLastExpr(FD, &indexOfLastExpr,
                                                   /*omitDS*/false,
                                                   m_Sema);
    if (To) {
      // Update the CompoundStmt body, avoiding alloc/dealloc of all the el.
      CompoundStmt* CS = cast<CompoundStmt>(FD->getBody());
      assert(CS && "Missing body?");

      switch (getCompilationOpts().ValuePrinting) {
      case CompilationOptions::VPDisabled:
        assert(0 && "Don't wait that long. Exit early!");
        break;
      case CompilationOptions::VPEnabled:
        break;
      case CompilationOptions::VPAuto: {
        // FIXME: Propagate the flag to the nested transactions also, they
        // must have the same CO as their parents.
        getCompilationOpts().ValuePrinting = CompilationOptions::VPEnabled;
        if ((int)CS->size() > indexOfLastExpr+1
            && (*(CS->body_begin() + indexOfLastExpr + 1))
            && isa<NullStmt>(*(CS->body_begin() + indexOfLastExpr + 1))) {
          // If next is NullStmt disable VP is disabled - exit. Signal this in
          // the CO of the transaction.
          getCompilationOpts().ValuePrinting = CompilationOptions::VPDisabled;
        }
        if (getCompilationOpts().ValuePrinting
            == CompilationOptions::VPDisabled)
          return true;
      }
        break;
      }

      // We can't PushDeclContext, because we don't have scope.
      Sema::ContextRAII pushedDC(*m_Sema, FD);

      if (To) {
        // Strip the parenthesis if any
        if (ParenExpr* PE = dyn_cast<ParenExpr>(To))
          To = PE->getSubExpr();

        Expr* Result = 0;
        // if (!m_Sema->getLangOpts().CPlusPlus)
        //   Result = SynthesizeVP(To);

        if (Result)
          *(CS->body_begin()+indexOfLastExpr) = Result;
      }
      // Clear the artificial NullStmt-s
      if (!ClearNullStmts(CS)) {
        // FIXME: Why it is here? Shouldn't it be in DeclExtractor?
        // if no body remove the wrapper
        DeclContext* DC = FD->getDeclContext();
        Scope* S = m_Sema->getScopeForContext(DC);
        if (S)
          S->RemoveDecl(FD);
        DC->removeDecl(FD);
      }
    }
    else // if nothing to attach to set the CO's ValuePrinting to disabled.
      getCompilationOpts().ValuePrinting = CompilationOptions::VPDisabled;
    return true;
  }

  // We need to artificially create:
  // cling_PrintValue(void* (ASTContext)C, void* (Expr)E, const void* (&i)
  Expr* ValuePrinterSynthesizer::SynthesizeVP(Expr* E) {
    QualType QT = E->getType();
    // For now we skip void and function pointer types.
    if (!QT.isNull() && (QT->isVoidType() || QT->isFunctionType()))
      return 0;

    // Find cling_PrintValue
    if (!m_LookupResult)
      FindAndCacheRuntimeLookupResult(E->getLocStart());

    Expr* VoidEArg = utils::Synthesize::CStyleCastPtrExpr(m_Sema,
                                                          m_Context->VoidPtrTy,
                                                          (uint64_t)E);
    Expr* VoidCArg = utils::Synthesize::CStyleCastPtrExpr(m_Sema,
                                                          m_Context->VoidPtrTy,
                                                          (uint64_t)m_Context);

    SourceLocation NoSLoc = SourceLocation();
    Scope* S = m_Sema->getScopeForContext(m_Sema->CurContext);
    if (!QT->isPointerType()) {
      while(ImplicitCastExpr* ICE = dyn_cast<ImplicitCastExpr>(E))
        E = ICE->getSubExpr();
      E = m_Sema->BuildUnaryOp(S, NoSLoc, UO_AddrOf, E).get();
    }

    llvm::SmallVector<Expr*, 4> CallArgs;
    CallArgs.push_back(VoidEArg);
    CallArgs.push_back(VoidCArg);
    CallArgs.push_back(E);

    CXXScopeSpec CSS;
    Expr* unresolvedLookup
      = m_Sema->BuildDeclarationNameExpr(CSS, *m_LookupResult,
                                         /*ADL*/ false).get();

    Expr* Result = m_Sema->ActOnCallExpr(S, unresolvedLookup, E->getLocStart(),
                                         CallArgs, E->getLocEnd()).get();
    assert(Result && "Cannot create value printer!");

    return Result;
  }

  unsigned ValuePrinterSynthesizer::ClearNullStmts(CompoundStmt* CS) {
    llvm::SmallVector<Stmt*, 8> FBody;
    for (auto&& child: CS->children())
      if (!isa<NullStmt>(child))
        FBody.push_back(child);

    CS->setStmts(*m_Context, FBody);
    return FBody.size();
  }

  void ValuePrinterSynthesizer::FindAndCacheRuntimeLookupResult(
                                                  SourceLocation sourceLoc) {
    assert(!m_LookupResult && "Called multiple times!?");

    DeclarationName PVName = &m_Context->Idents.get("cling_PrintValue");
    m_LookupResult = new LookupResult(*m_Sema, PVName, sourceLoc,
                                      Sema::LookupOrdinaryName,
                                      Sema::ForRedeclaration);

    Scope* S = m_Sema->getScopeForContext(m_Sema->CurContext);
    m_Sema->LookupName(*m_LookupResult, S);
    assert(!m_LookupResult->empty() && "Cannot find cling_PrintValue(...)");
  }

} // namespace cling
<|endoftext|>


<commit_before>/*
* Copyright (c) 2015-2018 JlnWntr (jlnwntr@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

#ifndef LUA_FUNCTION_H
#define LUA_FUNCTION_H

#ifndef LUA_ADAPTER_H
#include "LuaAdapter.hpp"
#endif

static int LUA_ADAPTER_NULL{};
typedef int (*Lua_callback_function)(lua_State *L);

class LuaFunction {

public:
  /**
  *Constructor
  *@param lua uses an existing lua_state
  */
  LuaFunction(LuaAdapter &lua) : Lua{lua.GetLuaState()} {}

  /**
  *Default constructor
  *@param lua uses an existing lua_state
  */
  LuaFunction(lua_State *const lua = nullptr) : Lua{lua} {}

  /**
  * Destructor
  */
  ~LuaFunction() {}

  /**
   * Calls a lua-function
   * @param f name of the lua-function
   * @param c number of arguments passed to the lua function
   * @param a function arguments
   * @param r new value returned from the called lua function
   * @return true on success, false on error
   */
  template <typename A, typename R>
  bool Call(const char *f, const auto c, const A *const a, R &r) {
    if ((!this->Lua) || (!f) || (lua_getglobal(this->Lua, f) != LUA_TFUNCTION))
      return false;

    for (auto i = 0; i < c; i++) {
      if (!(a + i))
        break;
      if
        constexpr(std::is_same_v<double, A> || std::is_same_v<A, float>)
            lua_pushnumber(this->Lua, a[i]);
      else if
        constexpr(
            std::is_same_v<A, int>) {
          lua_pushinteger(this->Lua, a[i]);
        }
      else if
        constexpr(std::is_same_v<A, std::string>)
            lua_pushlstring(this->Lua, a[i].c_str(), a[i].length());
      else if
        constexpr(std::is_same_v<A, bool>) lua_pushboolean(this->Lua, a[i]);
    }
    if (this->Pcall(c, 1, 0) == false)
      return false;
    if
      constexpr(std::is_same_v<double, R> || std::is_same_v<R, float>) {
        if (lua_isnumber(this->Lua, -1))
          r = lua_tonumber(this->Lua, -1);
      }
    else if
      constexpr(std::is_same_v<R, int>) {
        if (lua_isinteger(this->Lua, -1))
          r = lua_tointeger(this->Lua, -1);
      }
    else if
      constexpr(std::is_same_v<R, bool>) {
        if (lua_isboolean(this->Lua, -1))
          r = lua_toboolean(this->Lua, -1);
      }
    else if
      constexpr(std::is_same_v<R, std::string>) {
        if (lua_isstring(this->Lua, -1))
          r = lua_tostring(this->Lua, -1);
      }
    lua_pop(this->Lua, 1);
    return true;
  }

 /**
   * Calls a lua-function
   * @param f name of the lua-function
   * @param c number of arguments passed to the lua function
   * @param a function arguments
   * @return true on success, false on error
   */
  template <typename A>
  bool Call(const char *f, const auto c, const A *const a) {
    if ((!this->Lua) || (!f) || (lua_getglobal(this->Lua, f) != LUA_TFUNCTION))
      return false;

    for (auto i = 0; i < c; i++) {
      if (!(a + i))
        break;
      if
        constexpr(std::is_same_v<double, A> || std::is_same_v<A, float>)
            lua_pushnumber(this->Lua, a[i]);
      else if
        constexpr(std::is_same_v<A, int>) lua_pushinteger(this->Lua, a[i]);
      else if
        constexpr(std::is_same_v<A, std::string>)
            lua_pushlstring(this->Lua, a[i].c_str(), a[i].length());
      else if
        constexpr(std::is_same_v<A, bool>) lua_pushboolean(this->Lua, a[i]);
    }
    return this->Pcall(c, 0, 0);
  }

 /**
   * Calls a lua-function
   * @param f name of the lua function
   * @param a function argument
   * @param r new value returned from the called lua function
   * @return true on success, false on error
   */
  template <typename A, typename R>
  bool Call(const char *f, const A a, R &r = LUA_ADAPTER_NULL) {
    return this->Call(f, 1, (const A *)&a, r);
  }

  /**
   * Calls a lua-function
   * @param f name of the lua-function
   * @param a function argument
   * @return true on success, false on error
   */
  template <typename A> bool Call(const char *f, const A a) {
    return this->Call(f, 1, (const A *)&a);
  }

  /**
   * Calls a lua-function
   * @param f name of the lua-function
   * @return true on success, false on error
   */
  bool Call(const char *f) {
    if ((!this->Lua) || (!f) || (lua_getglobal(this->Lua, f) != LUA_TFUNCTION))
      return false;
    return this->Pcall(0, 0, 0);
  }

  /**
    * Makes a C-/C++-function call available for lua
    * (it's called pushFunction(), but you're not 'incrementing' the stack)
    * @param function C-/C++-function
    * @param name of the function
    * @return true on success, false on error
    */
  bool Push(Lua_callback_function function, const char *name) {
    if (!this->Lua)
      return false;
    lua_pushcfunction(this->Lua, function);
    lua_setglobal(this->Lua, name);
    return true;
  }

  /**
    * (Re-)Sets the lua state
    * @param lua lua_state
    * @return true on success, false on error
   */
  bool SetLuaState(lua_State *const lua) {
    if (!lua)
      return false;
    this->Lua = lua;
    return true;
  }

private:
  lua_State *Lua;

  /**
  * Calls lua's pcall and outputs errors.
  * @param na number of a
  * @param nresults number of results
  * @param int msgh see https://www.lua.org/manual/5.3/manual.html#lua_pcall
  * @return true on success, false on error
  */
  bool Pcall(int na, int nresults, int msgh) {
    const int call{lua_pcall(this->Lua, na, nresults, msgh)};
    if (call == LUA_OK) {
      return true;
    }
    std::cerr << LUA_PREFIX << "Error: pcall failed. Code: ";
    std::cerr << call;
    std::cerr << ", '" << lua_tostring(this->Lua, -1) << "'\n";
    lua_pop(this->Lua, 1);
    return false;
  }
};
#endif<commit_msg>Added 'Call(std::string..)' interface.<commit_after>/*
* Copyright (c) 2015-2018 JlnWntr (jlnwntr@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

#ifndef LUA_FUNCTION_H
#define LUA_FUNCTION_H

#ifndef LUA_ADAPTER_H
#include "LuaAdapter.hpp"
#endif

static int LUA_ADAPTER_NULL{};
typedef int (*Lua_callback_function)(lua_State *L);

class LuaFunction {

public:
  /**
  *Constructor
  *@param lua uses an existing lua_state
  */
  LuaFunction(LuaAdapter &lua) : Lua{lua.GetLuaState()} {}

  /**
  *Default constructor
  *@param lua uses an existing lua_state
  */
  LuaFunction(lua_State *const lua = nullptr) : Lua{lua} {}

  /**
  * Destructor
  */
  ~LuaFunction() {}

  /**
   * Calls a lua-function
   * @param f name of the lua-function
   * @param c number of arguments passed to the lua function
   * @param a function arguments
   * @param r new value returned from the called lua function
   * @return true on success, false on error
   */
  template <typename A, typename R>
  bool Call(const char *f, const auto c, const A *const a, R &r) {
    if ((!this->Lua) || (!f) || (lua_getglobal(this->Lua, f) != LUA_TFUNCTION))
      return false;

    for (auto i = 0; i < c; i++) {
      if (!(a + i))
        break;
      if
        constexpr(std::is_same_v<double, A> || std::is_same_v<A, float>)
            lua_pushnumber(this->Lua, a[i]);
      else if
        constexpr(
            std::is_same_v<A, int>) {
          lua_pushinteger(this->Lua, a[i]);
        }
      else if
        constexpr(std::is_same_v<A, std::string>)
            lua_pushlstring(this->Lua, a[i].c_str(), a[i].length());
      else if
        constexpr(std::is_same_v<A, bool>) lua_pushboolean(this->Lua, a[i]);
    }
    if (this->Pcall(c, 1, 0) == false)
      return false;
    if
      constexpr(std::is_same_v<double, R> || std::is_same_v<R, float>) {
        if (lua_isnumber(this->Lua, -1))
          r = lua_tonumber(this->Lua, -1);
      }
    else if
      constexpr(std::is_same_v<R, int>) {
        if (lua_isinteger(this->Lua, -1))
          r = lua_tointeger(this->Lua, -1);
      }
    else if
      constexpr(std::is_same_v<R, bool>) {
        if (lua_isboolean(this->Lua, -1))
          r = lua_toboolean(this->Lua, -1);
      }
    else if
      constexpr(std::is_same_v<R, std::string>) {
        if (lua_isstring(this->Lua, -1))
          r = lua_tostring(this->Lua, -1);
      }
    lua_pop(this->Lua, 1);
    return true;
  }
  template <typename A, typename R>
  bool Call(const std::string f, const auto c, const A *const a, R &r) {
      return this->Call(f.c_str(), c, a, r);
  }

 /**
   * Calls a lua-function
   * @param f name of the lua-function
   * @param c number of arguments passed to the lua function
   * @param a function arguments
   * @return true on success, false on error
   */
  template <typename A>
  bool Call(const char *f, const auto c, const A *const a) {
    if ((!this->Lua) || (!f) || (lua_getglobal(this->Lua, f) != LUA_TFUNCTION))
      return false;

    for (auto i = 0; i < c; i++) {
      if (!(a + i))
        break;
      if
        constexpr(std::is_same_v<double, A> || std::is_same_v<A, float>)
            lua_pushnumber(this->Lua, a[i]);
      else if
        constexpr(std::is_same_v<A, int>) lua_pushinteger(this->Lua, a[i]);
      else if
        constexpr(std::is_same_v<A, std::string>)
            lua_pushlstring(this->Lua, a[i].c_str(), a[i].length());
      else if
        constexpr(std::is_same_v<A, bool>) lua_pushboolean(this->Lua, a[i]);
    }
    return this->Pcall(c, 0, 0);
  }
  template <typename A>
  bool Call(const std::string f, const auto c, const A *const a) {
      return this->Call(f.c_str(), c, a);
  }

 /**
   * Calls a lua-function
   * @param f name of the lua function
   * @param a function argument
   * @param r new value returned from the called lua function
   * @return true on success, false on error
   */
  template <typename A, typename R>
  bool Call(const char *f, const A a, R &r = LUA_ADAPTER_NULL) {
    return this->Call(f, 1, (const A *)&a, r);
  }
  template <typename A, typename R>
  bool Call(const std::string f, const A a, R &r = LUA_ADAPTER_NULL) {
    return this->Call(f.c_str(), 1, (const A *)&a, r);
  }

  /**
   * Calls a lua-function
   * @param f name of the lua-function
   * @param a function argument
   * @return true on success, false on error
   */
  template <typename A> bool Call(const char *f, const A a) {
    return this->Call(f, 1, (const A *)&a);
  }
  template <typename A> bool Call(const std::string f, const A a) {
    return this->Call(f.c_str(), 1, (const A *)&a);
  }

  /**
   * Calls a lua-function
   * @param f name of the lua-function
   * @return true on success, false on error
   */
  bool Call(const char *f) {
    if ((!this->Lua) || (!f) || (lua_getglobal(this->Lua, f) != LUA_TFUNCTION))
      return false;
    return this->Pcall(0, 0, 0);
  }
  bool Call(const std::string f) {
      return this->Call(f.c_str());
  }

  /**
    * Makes a C-/C++-function call available for lua
    * (it's called pushFunction(), but you're not 'incrementing' the stack)
    * @param function C-/C++-function
    * @param name of the function
    * @return true on success, false on error
    */
  bool Push(Lua_callback_function function, const char *name) {
    if (!this->Lua)
      return false;
    lua_pushcfunction(this->Lua, function);
    lua_setglobal(this->Lua, name);
    return true;
  }
  bool Push(Lua_callback_function function, const std::string name) {
    return Push(function, name.c_str());
  }

  /**
    * (Re-)Sets the lua state
    * @param lua lua_state
    * @return true on success, false on error
   */
  bool SetLuaState(lua_State *const lua) {
    if (!lua)
      return false;
    this->Lua = lua;
    return true;
  }

private:
  lua_State *Lua;

  /**
  * Calls lua's pcall and outputs errors.
  * @param na number of a
  * @param nresults number of results
  * @param int msgh see https://www.lua.org/manual/5.3/manual.html#lua_pcall
  * @return true on success, false on error
  */
  bool Pcall(int na, int nresults, int msgh) {
    const int call{lua_pcall(this->Lua, na, nresults, msgh)};
    if (call == LUA_OK) {
      return true;
    }
    std::cerr << LUA_PREFIX << "Error: pcall failed. Code: ";
    std::cerr << call;
    std::cerr << ", '" << lua_tostring(this->Lua, -1) << "'\n";
    lua_pop(this->Lua, 1);
    return false;
  }
};
#endif<|endoftext|>


<commit_before>#include "adcs.hpp"
#include "data_point.hpp"
#include "gyro/driver.hpp"
#include "logger/logger.h"
#include "power/power.h"

using experiments::IterationResult;
using experiments::StartResult;
using PID = experiments::fs::ExperimentFile::PID;
using services::fs::FileOpen;
using services::fs::FileAccess;

using namespace std::chrono_literals;

namespace experiment
{
    namespace adcs
    {
        DetumblingExperiment::DetumblingExperiment(::adcs::IAdcsCoordinator& adcs,
            services::time::ICurrentTime& time,
            services::power::IPowerControl& powerControl,
            devices::gyro::IGyroscopeDriver& gyro,
            devices::payload::IPayloadDeviceDriver& payload,
            telemetry::IImtqDataProvider& imtq,
            services::fs::IFileSystem& fileSystem)
            : _adcs(adcs), _time(time), _powerControl(powerControl), _gyro(gyro), _payload(payload), _imtq(imtq), _fileSystem(fileSystem)
        {
        }

        void DetumblingExperiment::Duration(std::chrono::seconds duration)
        {
            this->_duration = duration;
        }

        void DetumblingExperiment::SampleRate(std::chrono::seconds interval)
        {
            this->_sampleRate = interval;
        }

        experiments::ExperimentCode DetumblingExperiment::Type()
        {
            return Code;
        }

        experiments::StartResult DetumblingExperiment::Start()
        {
            do
            {
                if (!_dataSet.Open(this->_fileSystem, "/detum", FileOpen::CreateAlways, FileAccess::WriteOnly))
                {
                    break;
                }

                auto start = this->_time.GetCurrentTime();

                if (!start.HasValue)
                {
                    LOG(LOG_LEVEL_ERROR, "[exp_dtb] Failed to retrieve current time");
                    break;
                }

                if (!this->_powerControl.SensPower(true))
                {
                    LOG(LOG_LEVEL_ERROR, "[exp_dtb] Failed to enable SENS power");
                    break;
                }

                auto r = this->_adcs.EnableExperimentalDetumbling();

                if (OS_RESULT_FAILED(r))
                {
                    LOGF(LOG_LEVEL_ERROR, "[exp_dtb] Failed to enable experimental detumbling (%d)", num(r));

                    break;
                }

                System::SleepTask(2s);

                this->_endAt = start.Value + this->_duration;

                return StartResult::Success;

            } while (false);

            CleanUp();

            return StartResult::Failure;
        }

        void DetumblingExperiment::CleanUp()
        {
            this->_dataSet.Close();

            this->_powerControl.SensPower(false);

            this->_adcs.EnableBuiltinDetumbling();
        }

        void DetumblingExperiment::Stop(IterationResult /*lastResult*/)
        {
            CleanUp();
        }

        IterationResult DetumblingExperiment::Iteration()
        {
            auto now = this->_time.GetCurrentTime();

            if (!now.HasValue)
            {
                return experiments::IterationResult::WaitForNextCycle;
            }

            if (now.Value >= this->_endAt)
            {
                return IterationResult::Finished;
            }

            System::SleepTask(this->_sampleRate);

            return IterationResult::LoopImmediately;
        }

        DetumblingDataPoint DetumblingExperiment::GatherSingleMeasurement()
        {
            DetumblingDataPoint point;

            point.Timestamp = this->_time.GetCurrentTime().Value;
            point.Gyro = this->_gyro.read().Value;

            this->_payload.MeasureSunSRef(point.ReferenceSunS);
            this->_payload.MeasurePhotodiodes(point.Photodiodes);
            this->_payload.MeasureTemperatures(point.Temperatures);

            this->_imtq.GetLastMagnetometerMeasurement(point.Magnetometer);

            this->_imtq.GetLastDipoles(point.Dipoles);

            return point;
        }

        void DetumblingDataPoint::WriteTo(experiments::fs::ExperimentFile& file)
        {
            {
                std::array<std::uint8_t, 8> buf;
                Writer w(buf);
                w.WriteQuadWordLE(this->Timestamp.count());

                file.Write(PID::Timestamp, w.Capture());
            }

            {
                std::array<std::uint8_t, (decltype(this->Gyro)::BitSize() + 7) / 8> buf;
                BitWriter w(buf);

                this->Gyro.Write(w);

                file.Write(PID::Gyro, w.Capture());
            }

            {
                std::array<std::uint8_t, decltype(this->ReferenceSunS)::DeviceDataLength> buf;
                Writer w(buf);

                this->ReferenceSunS.Write(w);

                file.Write(PID::ReferenceSunS, w.Capture());
            }

            {
                std::array<std::uint8_t, decltype(this->Temperatures)::DeviceDataLength> buf;
                Writer w(buf);

                this->Temperatures.Write(w);

                file.Write(PID::AllTemperatures, w.Capture());
            }

            {
                std::array<std::uint8_t, decltype(this->Photodiodes)::DeviceDataLength> buf;
                Writer w(buf);

                this->Photodiodes.Write(w);

                file.Write(PID::Photodiodes, w.Capture());
            }

            {
                std::array<std::uint8_t, (decltype(this->Magnetometer)::BitSize() + 7) / 8> buf;
                BitWriter w(buf);

                this->Magnetometer.Write(w);

                file.Write(PID::Magnetometer, w.Capture());
            }

            {
                std::array<std::uint8_t, (decltype(this->Dipoles)::BitSize() + 7) / 8> buf;
                BitWriter w(buf);

                this->Dipoles.Write(w);

                file.Write(PID::Dipoles, w.Capture());
            }
        }
    }
}
<commit_msg>Dump measurements to file<commit_after>#include "adcs.hpp"
#include "data_point.hpp"
#include "gyro/driver.hpp"
#include "logger/logger.h"
#include "power/power.h"

using experiments::IterationResult;
using experiments::StartResult;
using PID = experiments::fs::ExperimentFile::PID;
using services::fs::FileOpen;
using services::fs::FileAccess;

using namespace std::chrono_literals;

namespace experiment
{
    namespace adcs
    {
        DetumblingExperiment::DetumblingExperiment(::adcs::IAdcsCoordinator& adcs,
            services::time::ICurrentTime& time,
            services::power::IPowerControl& powerControl,
            devices::gyro::IGyroscopeDriver& gyro,
            devices::payload::IPayloadDeviceDriver& payload,
            telemetry::IImtqDataProvider& imtq,
            services::fs::IFileSystem& fileSystem)
            : _adcs(adcs), _time(time), _powerControl(powerControl), _gyro(gyro), _payload(payload), _imtq(imtq), _fileSystem(fileSystem)
        {
        }

        void DetumblingExperiment::Duration(std::chrono::seconds duration)
        {
            this->_duration = duration;
        }

        void DetumblingExperiment::SampleRate(std::chrono::seconds interval)
        {
            this->_sampleRate = interval;
        }

        experiments::ExperimentCode DetumblingExperiment::Type()
        {
            return Code;
        }

        experiments::StartResult DetumblingExperiment::Start()
        {
            do
            {
                if (!_dataSet.Open(this->_fileSystem, "/detum", FileOpen::CreateAlways, FileAccess::WriteOnly))
                {
                    break;
                }

                auto start = this->_time.GetCurrentTime();

                if (!start.HasValue)
                {
                    LOG(LOG_LEVEL_ERROR, "[exp_dtb] Failed to retrieve current time");
                    break;
                }

                if (!this->_powerControl.SensPower(true))
                {
                    LOG(LOG_LEVEL_ERROR, "[exp_dtb] Failed to enable SENS power");
                    break;
                }

                auto r = this->_adcs.EnableExperimentalDetumbling();

                if (OS_RESULT_FAILED(r))
                {
                    LOGF(LOG_LEVEL_ERROR, "[exp_dtb] Failed to enable experimental detumbling (%d)", num(r));

                    break;
                }

                System::SleepTask(2s);

                this->_endAt = start.Value + this->_duration;

                return StartResult::Success;

            } while (false);

            CleanUp();

            return StartResult::Failure;
        }

        void DetumblingExperiment::CleanUp()
        {
            this->_dataSet.Close();

            this->_powerControl.SensPower(false);

            this->_adcs.EnableBuiltinDetumbling();
        }

        void DetumblingExperiment::Stop(IterationResult /*lastResult*/)
        {
            CleanUp();
        }

        IterationResult DetumblingExperiment::Iteration()
        {
            auto now = this->_time.GetCurrentTime();

            if (!now.HasValue)
            {
                return experiments::IterationResult::WaitForNextCycle;
            }

            if (now.Value >= this->_endAt)
            {
                return IterationResult::Finished;
            }

            auto point = GatherSingleMeasurement();

            point.WriteTo(this->_dataSet);

            System::SleepTask(this->_sampleRate);

            return IterationResult::LoopImmediately;
        }

        DetumblingDataPoint DetumblingExperiment::GatherSingleMeasurement()
        {
            DetumblingDataPoint point;

            point.Timestamp = this->_time.GetCurrentTime().Value;
            point.Gyro = this->_gyro.read().Value;

            this->_payload.MeasureSunSRef(point.ReferenceSunS);
            this->_payload.MeasurePhotodiodes(point.Photodiodes);
            this->_payload.MeasureTemperatures(point.Temperatures);

            this->_imtq.GetLastMagnetometerMeasurement(point.Magnetometer);

            this->_imtq.GetLastDipoles(point.Dipoles);

            return point;
        }

        void DetumblingDataPoint::WriteTo(experiments::fs::ExperimentFile& file)
        {
            {
                std::array<std::uint8_t, 8> buf;
                Writer w(buf);
                w.WriteQuadWordLE(this->Timestamp.count());

                file.Write(PID::Timestamp, w.Capture());
            }

            {
                std::array<std::uint8_t, (decltype(this->Gyro)::BitSize() + 7) / 8> buf;
                BitWriter w(buf);

                this->Gyro.Write(w);

                file.Write(PID::Gyro, w.Capture());
            }

            {
                std::array<std::uint8_t, decltype(this->ReferenceSunS)::DeviceDataLength> buf;
                Writer w(buf);

                this->ReferenceSunS.Write(w);

                file.Write(PID::ReferenceSunS, w.Capture());
            }

            {
                std::array<std::uint8_t, decltype(this->Temperatures)::DeviceDataLength> buf;
                Writer w(buf);

                this->Temperatures.Write(w);

                file.Write(PID::AllTemperatures, w.Capture());
            }

            {
                std::array<std::uint8_t, decltype(this->Photodiodes)::DeviceDataLength> buf;
                Writer w(buf);

                this->Photodiodes.Write(w);

                file.Write(PID::Photodiodes, w.Capture());
            }

            {
                std::array<std::uint8_t, (decltype(this->Magnetometer)::BitSize() + 7) / 8> buf;
                BitWriter w(buf);

                this->Magnetometer.Write(w);

                file.Write(PID::Magnetometer, w.Capture());
            }

            {
                std::array<std::uint8_t, (decltype(this->Dipoles)::BitSize() + 7) / 8> buf;
                BitWriter w(buf);

                this->Dipoles.Write(w);

                file.Write(PID::Dipoles, w.Capture());
            }
        }
    }
}
<|endoftext|>