| /******************************************************************************* |
| * Copyright (c) 2007, 2016 Wind River Systems, Inc. and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Ted R Williams (Wind River Systems, Inc.) - initial implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.cdt.debug.ui.memory.search; |
| |
| import java.lang.reflect.Method; |
| import java.math.BigInteger; |
| import java.util.Properties; |
| import java.util.StringTokenizer; |
| import java.util.Vector; |
| |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.debug.core.DebugException; |
| import org.eclipse.debug.core.model.IMemoryBlockExtension; |
| import org.eclipse.debug.core.model.MemoryByte; |
| import org.eclipse.debug.ui.memory.IMemoryRendering; |
| import org.eclipse.debug.ui.memory.IMemoryRenderingContainer; |
| import org.eclipse.debug.ui.memory.IMemoryRenderingSite; |
| import org.eclipse.debug.ui.memory.IRepositionableMemoryRendering; |
| import org.eclipse.jface.action.IAction; |
| import org.eclipse.jface.dialogs.IDialogConstants; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.StructuredSelection; |
| import org.eclipse.search.ui.ISearchQuery; |
| import org.eclipse.search.ui.ISearchResult; |
| import org.eclipse.search.ui.NewSearchUI; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.events.SelectionListener; |
| import org.eclipse.swt.layout.FormAttachment; |
| import org.eclipse.swt.layout.FormData; |
| import org.eclipse.swt.layout.FormLayout; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.layout.GridLayout; |
| import org.eclipse.swt.widgets.Button; |
| import org.eclipse.swt.widgets.Combo; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Group; |
| import org.eclipse.swt.widgets.Label; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.swt.widgets.Text; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.dialogs.SelectionDialog; |
| |
| public class FindReplaceDialog extends SelectionDialog { |
| |
| private IMemoryBlockExtension fMemoryBlock; |
| |
| final static int preFetchSize = 20 * 1024; |
| |
| private Text fFindText; |
| private Text fReplaceText; |
| |
| private Combo fStartText; |
| private Combo fEndText; |
| |
| private Button fFindButton; |
| private Button fFindAllButton; |
| private Button fReplaceButton; |
| private Button fReplaceFindButton; |
| private Button fReplaceAllButton; |
| |
| private IMemoryRenderingSite fMemoryView; |
| |
| private Button fFormatAsciiButton; |
| private Button fFormatHexButton; |
| private Button fFormatOctalButton; |
| private Button fFormatBinaryButton; |
| private Button fFormatDecimalButton; |
| private Button fFormatByteSequenceButton; |
| |
| private Button fCaseInSensitiveCheckbox; |
| |
| private Button fWrapCheckbox; |
| |
| private Button fForwardButton; |
| |
| private Properties fProperties; |
| |
| protected final static String SEARCH_FIND = "SEARCH_FIND"; //$NON-NLS-1$ |
| protected final static String SEARCH_REPLACE = "SEARCH_REPLACE"; //$NON-NLS-1$ |
| protected final static String SEARCH_START = "SEARCH_START"; //$NON-NLS-1$ |
| protected final static String SEARCH_END = "SEARCH_END"; //$NON-NLS-1$ |
| protected final static String SEARCH_LAST_START = "SEARCH_LAST_START"; //$NON-NLS-1$ |
| protected final static String SEARCH_LAST_END = "SEARCH_LAST_END"; //$NON-NLS-1$ |
| protected final static String SEARCH_FORMAT = "SEARCH_FORMAT"; //$NON-NLS-1$ |
| protected final static String SEARCH_FORMAT_ASCII = "SEARCH_FORMAT_ASCII"; //$NON-NLS-1$ |
| protected final static String SEARCH_FORMAT_HEX = "SEARCH_FORMAT_HEX"; //$NON-NLS-1$ |
| protected final static String SEARCH_FORMAT_OCTAL = "SEARCH_FORMAT_OCTAL"; //$NON-NLS-1$ |
| protected final static String SEARCH_FORMAT_BINARY = "SEARCH_FORMAT_BINARY"; //$NON-NLS-1$ |
| protected final static String SEARCH_FORMAT_DECIMAL = "SEARCH_FORMAT_DECIMAL"; //$NON-NLS-1$ |
| protected final static String SEARCH_FORMAT_BYTESEQUENCE = "SEARCH_FORMAT_BYTESEQUENCE"; //$NON-NLS-1$ |
| protected final static String SEARCH_FORMAT_CASEINSENSTIVE = "SEARCH_FORMAT_CASEINSENSTIVE"; //$NON-NLS-1$ |
| protected final static String SEARCH_FORMAT_FORWARD = "SEARCH_FORMAT_FORWARD"; //$NON-NLS-1$ |
| protected final static String SEARCH_FORMAT_WRAP = "SEARCH_FORMAT_WRAP"; //$NON-NLS-1$ |
| protected final static String SEARCH_ENABLE_FIND_NEXT = "SEARCH_ENABLE_FIND_NEXT"; //$NON-NLS-1$ |
| |
| //the width of text fields of Find and Replace, increase it to 400 to fix the tvt defect 356901 |
| protected final static int FIND_REPLACE_TEXT_WIDTH = 400; |
| |
| private IAction fFindAction = null; |
| |
| public FindReplaceDialog(Shell parent, IMemoryBlockExtension memoryBlock, IMemoryRenderingSite memoryView, |
| Properties properties, IAction findAction) { |
| super(parent); |
| super.setTitle(Messages.getString("FindReplaceDialog.Title")); //$NON-NLS-1$ |
| setShellStyle(getShellStyle() | SWT.RESIZE); |
| |
| fMemoryBlock = memoryBlock; |
| fMemoryView = memoryView; |
| fProperties = properties; |
| this.setBlockOnOpen(false); |
| fFindAction = findAction; |
| } |
| |
| private BigInteger getUserStart() { |
| String start = fStartText.getText(); |
| if (start.toUpperCase().startsWith("0X")) //$NON-NLS-1$ |
| start = start.substring(2); |
| return new BigInteger(start, 16); |
| } |
| |
| private BigInteger getUserEnd() { |
| String end = fEndText.getText(); |
| if (end.toUpperCase().startsWith("0X")) //$NON-NLS-1$ |
| end = end.substring(2); |
| return new BigInteger(end, 16); |
| } |
| |
| private boolean getIsDirectionForward() { |
| return fForwardButton.getSelection(); |
| } |
| |
| private SearchPhrase getSearchPhrase() { |
| SearchPhrase phrase = null; |
| |
| if (fFormatAsciiButton.getSelection()) { |
| phrase = new AsciiSearchPhrase(fFindText.getText(), fCaseInSensitiveCheckbox.getSelection()); |
| } else if (fFormatHexButton.getSelection()) { |
| phrase = new BigIntegerSearchPhrase(new BigInteger(fFindText.getText().toUpperCase().startsWith("0X") //$NON-NLS-1$ |
| ? fFindText.getText().substring(2) |
| : fFindText.getText(), 16), 16); |
| } else if (fFormatOctalButton.getSelection()) { |
| phrase = new BigIntegerSearchPhrase(new BigInteger(fFindText.getText().startsWith("0") //$NON-NLS-1$ |
| ? fFindText.getText().substring(1) |
| : fFindText.getText(), 8), 8); |
| } else if (fFormatBinaryButton.getSelection()) { |
| phrase = new BigIntegerSearchPhrase(new BigInteger(fFindText.getText().toUpperCase().startsWith("0B") //$NON-NLS-1$ |
| ? fFindText.getText().substring(2) |
| : fFindText.getText(), 2), 2); |
| } else if (fFormatDecimalButton.getSelection()) { |
| phrase = new BigIntegerSearchPhrase(new BigInteger(fFindText.getText(), 10), 10); |
| } else if (fFormatByteSequenceButton.getSelection()) { |
| phrase = new ByteSequenceSearchPhrase(fFindText.getText()); |
| } |
| |
| return phrase; |
| } |
| |
| protected byte[] parseByteSequence(String s) { |
| Vector<Byte> sequence = new Vector<>(); |
| StringTokenizer st = new StringTokenizer(s, " "); //$NON-NLS-1$ |
| while (st.hasMoreElements()) { |
| String element = ((String) st.nextElement()).trim(); |
| if (element.length() > 0) { |
| BigInteger value; |
| if (element.toUpperCase().startsWith("0X")) //$NON-NLS-1$ |
| value = new BigInteger(element.substring(2), 16); |
| else if (element.toUpperCase().startsWith("0B")) //$NON-NLS-1$ |
| value = new BigInteger(element.substring(2), 2); |
| else if (element.toUpperCase().startsWith("0")) //$NON-NLS-1$ |
| value = new BigInteger(element.substring(1), 8); |
| else |
| value = new BigInteger(element, 10); |
| Byte b = value.byteValue(); |
| |
| if (value.compareTo(BigInteger.valueOf(255)) > 0) |
| return null; |
| |
| sequence.addElement(b); |
| } |
| } |
| Byte seq[] = sequence.toArray(new Byte[sequence.size()]); |
| byte[] bytes = new byte[seq.length]; |
| for (int i = 0; i < seq.length; i++) |
| bytes[i] = seq[i].byteValue(); |
| return bytes; |
| } |
| |
| private byte[] getReplaceData() { |
| if (fFormatAsciiButton.getSelection()) |
| return fReplaceText.getText().getBytes(); |
| else if (fFormatHexButton.getSelection()) |
| return removeZeroPrefixByte(new BigInteger( |
| fReplaceText.getText().toUpperCase().startsWith("0X") ? fReplaceText.getText().substring(2) //$NON-NLS-1$ |
| : fReplaceText.getText(), |
| 16).toByteArray()); |
| else if (fFormatOctalButton.getSelection()) |
| return removeZeroPrefixByte( |
| new BigInteger(fReplaceText.getText().startsWith("0") ? fReplaceText.getText().substring(1) //$NON-NLS-1$ |
| : fReplaceText.getText(), 8).toByteArray()); |
| else if (fFormatBinaryButton.getSelection()) |
| return removeZeroPrefixByte(new BigInteger( |
| fReplaceText.getText().toUpperCase().startsWith("0B") ? fReplaceText.getText().substring(2) //$NON-NLS-1$ |
| : fReplaceText.getText(), |
| 2).toByteArray()); |
| else if (fFormatDecimalButton.getSelection()) |
| return removeZeroPrefixByte(new BigInteger(fReplaceText.getText(), 10).toByteArray()); |
| else if (fFormatByteSequenceButton.getSelection()) |
| return parseByteSequence(fReplaceText.getText()); |
| |
| return new byte[0]; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar(org.eclipse.swt.widgets.Composite) |
| */ |
| @Override |
| protected void createButtonsForButtonBar(Composite parent) { |
| |
| fFindButton = createButton(parent, 10, Messages.getString("FindReplaceDialog.ButtonFind"), true); //$NON-NLS-1$ |
| fFindButton.addSelectionListener(new SelectionListener() { |
| @Override |
| public void widgetDefaultSelected(SelectionEvent e) { |
| } |
| |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| performFind(getUserStart(), getUserEnd(), getSearchPhrase(), getIsDirectionForward(), null, false, |
| false); |
| cancelPressed(); |
| } |
| }); |
| |
| fFindAllButton = createButton(parent, 10, Messages.getString("FindReplaceDialog.ButtonFindAll"), true); //$NON-NLS-1$ |
| fFindAllButton.addSelectionListener(new SelectionListener() { |
| @Override |
| public void widgetDefaultSelected(SelectionEvent e) { |
| } |
| |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| performFind(getUserStart(), getUserEnd(), getSearchPhrase(), getIsDirectionForward(), null, true, |
| false); |
| cancelPressed(); |
| } |
| }); |
| |
| fReplaceFindButton = createButton(parent, 11, Messages.getString("FindReplaceDialog.ButtonReplaceFind"), false); //$NON-NLS-1$ |
| fReplaceFindButton.addSelectionListener(new SelectionListener() { |
| @Override |
| public void widgetDefaultSelected(SelectionEvent e) { |
| } |
| |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| performFind(getUserStart(), getUserEnd(), getSearchPhrase(), getIsDirectionForward(), getReplaceData(), |
| false, true); |
| cancelPressed(); |
| } |
| }); |
| |
| fReplaceButton = createButton(parent, 12, Messages.getString("FindReplaceDialog.ButtonReplace"), false); //$NON-NLS-1$ |
| fReplaceButton.addSelectionListener(new SelectionListener() { |
| @Override |
| public void widgetDefaultSelected(SelectionEvent e) { |
| } |
| |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| performFind(getUserStart(), getUserEnd(), getSearchPhrase(), getIsDirectionForward(), getReplaceData(), |
| false, false); |
| cancelPressed(); |
| } |
| }); |
| |
| fReplaceAllButton = createButton(parent, 13, Messages.getString("FindReplaceDialog.ButtonReplaceAll"), false); //$NON-NLS-1$ |
| fReplaceAllButton.addSelectionListener(new SelectionListener() { |
| @Override |
| public void widgetDefaultSelected(SelectionEvent e) { |
| } |
| |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| performFind(getUserStart(), getUserEnd(), getSearchPhrase(), getIsDirectionForward(), getReplaceData(), |
| true, false); |
| cancelPressed(); |
| } |
| }); |
| |
| createButton(parent, IDialogConstants.CANCEL_ID, Messages.getString("FindReplaceDialog.Close"), false); //$NON-NLS-1$ |
| |
| ((GridLayout) parent.getLayout()).numColumns = 2; |
| |
| validate(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.ui.dialogs.SelectionDialog#getResult() |
| */ |
| @Override |
| public Object[] getResult() { |
| |
| Object[] results = super.getResult(); |
| |
| if (results != null) { |
| return results; |
| } |
| return new Object[0]; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jface.dialogs.Dialog#cancelPressed() |
| */ |
| @Override |
| protected void cancelPressed() { |
| |
| fProperties.setProperty(SEARCH_FIND, fFindText.getText()); |
| fProperties.setProperty(SEARCH_REPLACE, fReplaceText.getText()); |
| fProperties.setProperty(SEARCH_START, fStartText.getText()); |
| fProperties.setProperty(SEARCH_END, fEndText.getText()); |
| if (fFormatAsciiButton.getSelection()) |
| fProperties.setProperty(SEARCH_FORMAT, SEARCH_FORMAT_ASCII); |
| else if (fFormatBinaryButton.getSelection()) |
| fProperties.setProperty(SEARCH_FORMAT, SEARCH_FORMAT_BINARY); |
| else if (fFormatByteSequenceButton.getSelection()) |
| fProperties.setProperty(SEARCH_FORMAT, SEARCH_FORMAT_BYTESEQUENCE); |
| else if (fFormatDecimalButton.getSelection()) |
| fProperties.setProperty(SEARCH_FORMAT, SEARCH_FORMAT_DECIMAL); |
| else if (fFormatHexButton.getSelection()) |
| fProperties.setProperty(SEARCH_FORMAT, SEARCH_FORMAT_HEX); |
| else if (fFormatOctalButton.getSelection()) |
| fProperties.setProperty(SEARCH_FORMAT, SEARCH_FORMAT_OCTAL); |
| |
| fProperties.setProperty(SEARCH_FORMAT_FORWARD, Boolean.toString(fForwardButton.getSelection())); |
| |
| fProperties.setProperty(SEARCH_FORMAT_CASEINSENSTIVE, |
| Boolean.toString(fCaseInSensitiveCheckbox.getSelection())); |
| |
| fProperties.setProperty(SEARCH_FORMAT_WRAP, Boolean.toString(fWrapCheckbox.getSelection())); |
| |
| fProperties.setProperty(SEARCH_ENABLE_FIND_NEXT, Boolean.FALSE.toString()); |
| |
| setResult(null); |
| |
| super.cancelPressed(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jface.dialogs.Dialog#okPressed() |
| */ |
| @Override |
| protected void okPressed() { |
| setSelectionResult(new Object[] { fProperties }); |
| |
| super.okPressed(); |
| } |
| |
| public BigInteger getEndAddress() { |
| String text = fEndText.getText(); |
| boolean hex = text.startsWith("0x"); //$NON-NLS-1$ |
| BigInteger endAddress = new BigInteger(hex ? text.substring(2) : text, hex ? 16 : 10); |
| |
| return endAddress; |
| } |
| |
| public BigInteger getStartAddress() { |
| String text = fStartText.getText(); |
| boolean hex = text.startsWith("0x"); //$NON-NLS-1$ |
| BigInteger startAddress = new BigInteger(hex ? text.substring(2) : text, hex ? 16 : 10); |
| |
| return startAddress; |
| } |
| |
| private void validate() { |
| boolean valid = false; |
| boolean replaceValid = false; |
| |
| try { |
| BigInteger endAddress = getEndAddress(); |
| BigInteger startAddress = getStartAddress(); |
| |
| /* |
| * The end-address must be larger that the start-address. |
| */ |
| if (startAddress.compareTo(endAddress) == -1) { |
| /* |
| * Validate the search phrase. |
| */ |
| if (getSearchPhrase() != null && getSearchPhrase().getByteLength() > 0) { |
| valid = true; |
| } |
| |
| /* |
| * Validate the replacement phrase. |
| */ |
| if (getReplaceData() != null && getReplaceData().length > 0) { |
| replaceValid = true; |
| } |
| } |
| } catch (Throwable ex) { |
| // do nothing |
| } |
| |
| fFindButton.setEnabled(valid); |
| fFindAllButton.setEnabled(valid); |
| fReplaceButton.setEnabled(replaceValid); |
| fReplaceFindButton.setEnabled(replaceValid); |
| fReplaceAllButton.setEnabled(replaceValid); |
| } |
| |
| private String pad(int characterCount, String value) { |
| StringBuilder sb = new StringBuilder(value); |
| for (int i = 0; i < characterCount - value.length(); i++) |
| sb.insert(0, "0"); //$NON-NLS-1$ |
| return sb.toString(); |
| } |
| |
| private String[] removeNullElements(String strings[]) { |
| Vector<String> nonNullStrings = new Vector<>(); |
| for (String string : strings) |
| if (string != null) |
| nonNullStrings.addElement(string); |
| return nonNullStrings.toArray(new String[0]); |
| } |
| |
| private String getMemoryBlockBaseAddress() { |
| BigInteger base = null; |
| try { |
| base = fMemoryBlock.getBigBaseAddress(); |
| } catch (DebugException de) { |
| // do nothing |
| } |
| |
| if (base == null) |
| base = BigInteger.ZERO; |
| |
| return "0x" + pad(getAddressSize() * 2, base.toString(16).toUpperCase()); //$NON-NLS-1$ |
| } |
| |
| private String getViewportStart() { |
| ISelection selection = fMemoryView.getMemoryRenderingContainers()[0].getMemoryRenderingSite().getSite() |
| .getSelectionProvider().getSelection(); |
| if (selection instanceof StructuredSelection) { |
| if (((StructuredSelection) selection).getFirstElement() instanceof IRepositionableMemoryRendering) { |
| ((IRepositionableMemoryRendering) ((StructuredSelection) selection).getFirstElement()) |
| .getSelectedAddress(); |
| } |
| } |
| return null; |
| } |
| |
| private String getStart() { |
| BigInteger start = null; |
| try { |
| start = fMemoryBlock.getMemoryBlockStartAddress(); |
| } catch (DebugException de) { |
| // do nothing |
| } |
| |
| if (start == null) |
| start = BigInteger.ZERO; |
| |
| return "0x" + pad(getAddressSize() * 2, start.toString(16).toUpperCase()); //$NON-NLS-1$ |
| } |
| |
| private String getEnd() { |
| BigInteger end = null; |
| |
| try { |
| end = fMemoryBlock.getMemoryBlockEndAddress(); |
| } catch (DebugException de) { |
| // do nothing |
| } |
| |
| if (end == null) { |
| end = BigInteger.ZERO; |
| |
| for (int i = getAddressSize(); i > 0; i--) { |
| end = end.shiftLeft(8); |
| end = end.or(BigInteger.valueOf(255)); |
| } |
| } |
| |
| return "0x" + pad(getAddressSize() * 2, end.toString(16).toUpperCase()); //$NON-NLS-1$ |
| } |
| |
| private int getAddressSize() { |
| int addressSize; |
| try { |
| addressSize = fMemoryBlock.getAddressSize(); |
| } catch (DebugException de) { |
| addressSize = 4; // default to 32bit? |
| } |
| return addressSize; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite) |
| */ |
| @Override |
| protected Control createDialogArea(Composite parent) { |
| |
| PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, |
| MemorySearchPlugin.getUniqueIdentifier() + ".FindReplaceDialog_context"); //$NON-NLS-1$ |
| Composite composite = new Composite(parent, SWT.NONE); |
| FormLayout formLayout = new FormLayout(); |
| formLayout.spacing = 5; |
| formLayout.marginWidth = formLayout.marginHeight = 9; |
| composite.setLayout(formLayout); |
| |
| // find |
| |
| Label findLabel = new Label(composite, SWT.NONE); |
| Label replaceLabel = new Label(composite, SWT.NONE); |
| fReplaceText = new Text(composite, SWT.BORDER); |
| |
| findLabel.setText(Messages.getString("FindReplaceDialog.LabelFind")); //$NON-NLS-1$ |
| |
| fFindText = new Text(composite, SWT.BORDER); |
| FormData data = new FormData(); |
| data.left = new FormAttachment(fReplaceText, 0, SWT.LEFT); |
| data.width = FIND_REPLACE_TEXT_WIDTH; |
| fFindText.setLayoutData(data); |
| fFindText.setText(fProperties.getProperty(SEARCH_FIND, "")); //$NON-NLS-1$ |
| |
| data = new FormData(); |
| data.top = new FormAttachment(fFindText, 0, SWT.CENTER); |
| findLabel.setLayoutData(data); |
| |
| // replace |
| |
| replaceLabel.setText(Messages.getString("FindReplaceDialog.LabelReplaceWith")); //$NON-NLS-1$ |
| data = new FormData(); |
| data.top = new FormAttachment(fFindText); |
| replaceLabel.setLayoutData(data); |
| |
| data = new FormData(); |
| data.top = new FormAttachment(replaceLabel, 0, SWT.CENTER); |
| data.left = new FormAttachment(replaceLabel); |
| data.width = FIND_REPLACE_TEXT_WIDTH; |
| fReplaceText.setLayoutData(data); |
| fReplaceText.setText(fProperties.getProperty(SEARCH_REPLACE, "")); //$NON-NLS-1$ |
| |
| // group direction |
| |
| Group directionGroup = new Group(composite, SWT.NONE); |
| Group formatGroup = new Group(composite, SWT.NONE); |
| Group rangeGroup = new Group(composite, SWT.NONE); |
| directionGroup.setText(Messages.getString("FindReplaceDialog.LabelDirection")); //$NON-NLS-1$ |
| |
| GridLayout layout = new GridLayout(); |
| layout.numColumns = 1; |
| directionGroup.setLayout(layout); |
| |
| fForwardButton = new Button(directionGroup, SWT.RADIO); |
| fForwardButton.setText(Messages.getString("FindReplaceDialog.ButtonForward")); //$NON-NLS-1$ |
| final Button backwardButton = new Button(directionGroup, SWT.RADIO); |
| backwardButton.setText(Messages.getString("FindReplaceDialog.ButtonBackward")); //$NON-NLS-1$ |
| final boolean isForward = Boolean |
| .parseBoolean(fProperties.getProperty(SEARCH_FORMAT_FORWARD, Boolean.TRUE.toString())); |
| fForwardButton.setSelection(isForward); |
| backwardButton.setSelection(!isForward); |
| |
| data = new FormData(); |
| data.top = new FormAttachment(fReplaceText); |
| data.right = new FormAttachment(formatGroup, 0, SWT.RIGHT); |
| data.left = new FormAttachment(formatGroup, 0, SWT.LEFT); |
| data.bottom = new FormAttachment(rangeGroup, 0, SWT.BOTTOM); |
| directionGroup.setLayoutData(data); |
| |
| // group range |
| |
| rangeGroup.setText(Messages.getString("FindReplaceDialog.LabelRange")); //$NON-NLS-1$ |
| |
| layout = new GridLayout(); |
| layout.numColumns = 2; |
| layout.makeColumnsEqualWidth = false; |
| rangeGroup.setLayout(layout); |
| |
| // group range - start address |
| |
| Label startLabel = new Label(rangeGroup, SWT.NONE); |
| startLabel.setText(Messages.getString("FindReplaceDialog.LabelStartAddress")); //$NON-NLS-1$ |
| |
| fStartText = new Combo(rangeGroup, SWT.BORDER); |
| GridData gridData = new GridData(); |
| gridData.widthHint = 200; |
| gridData.grabExcessHorizontalSpace = true; |
| fStartText.setLayoutData(gridData); |
| |
| // group range - end address |
| |
| Label endLabel = new Label(rangeGroup, SWT.NONE); |
| endLabel.setText(Messages.getString("FindReplaceDialog.LabelEndAddress")); //$NON-NLS-1$ |
| fEndText = new Combo(rangeGroup, SWT.BORDER); |
| gridData = new GridData(); |
| gridData.widthHint = 200; |
| gridData.grabExcessHorizontalSpace = true; |
| fEndText.setLayoutData(gridData); |
| |
| data = new FormData(); |
| data.left = new FormAttachment(directionGroup); |
| data.top = new FormAttachment(directionGroup, 0, SWT.TOP); |
| data.right = new FormAttachment(fFindText, 0, SWT.RIGHT); |
| rangeGroup.setLayoutData(data); |
| |
| fStartText.setItems(removeNullElements( |
| new String[] { getViewportStart(), getStart(), getEnd(), getMemoryBlockBaseAddress() })); |
| fEndText.setItems(removeNullElements( |
| new String[] { getEnd(), getStart(), getMemoryBlockBaseAddress(), getViewportStart() })); |
| if (fProperties.getProperty(SEARCH_START) != null) |
| fStartText.add(fProperties.getProperty(SEARCH_START), 0); |
| if (fProperties.getProperty(SEARCH_END) != null) |
| fEndText.add(fProperties.getProperty(SEARCH_END), 0); |
| fStartText.select(0); |
| fEndText.select(0); |
| |
| // format group |
| |
| formatGroup.setText(Messages.getString("FindReplaceDialog.LabelFormat")); //$NON-NLS-1$ |
| layout = new GridLayout(); |
| layout.numColumns = 1; |
| formatGroup.setLayout(layout); |
| |
| fFormatAsciiButton = new Button(formatGroup, SWT.RADIO); |
| fFormatAsciiButton.setText(Messages.getString("FindReplaceDialog.ButtonASCII")); //$NON-NLS-1$ |
| |
| fFormatHexButton = new Button(formatGroup, SWT.RADIO); |
| fFormatHexButton.setText(Messages.getString("FindReplaceDialog.ButtonHexadecimal")); //$NON-NLS-1$ |
| |
| fFormatOctalButton = new Button(formatGroup, SWT.RADIO); |
| fFormatOctalButton.setText(Messages.getString("FindReplaceDialog.ButtonOctal")); //$NON-NLS-1$ |
| |
| fFormatBinaryButton = new Button(formatGroup, SWT.RADIO); |
| fFormatBinaryButton.setText(Messages.getString("FindReplaceDialog.ButtonBinary")); //$NON-NLS-1$ |
| |
| fFormatDecimalButton = new Button(formatGroup, SWT.RADIO); |
| fFormatDecimalButton.setText(Messages.getString("FindReplaceDialog.ButtonDecimal")); //$NON-NLS-1$ |
| |
| fFormatByteSequenceButton = new Button(formatGroup, SWT.RADIO); |
| fFormatByteSequenceButton.setText(Messages.getString("FindReplaceDialog.ButtonByteSequence")); //$NON-NLS-1$ |
| |
| final String format = fProperties.getProperty(SEARCH_FORMAT, FindReplaceDialog.SEARCH_FORMAT_ASCII); |
| |
| fFormatAsciiButton.setSelection(format.equals(SEARCH_FORMAT_ASCII)); |
| fFormatOctalButton.setSelection(format.equals(SEARCH_FORMAT_OCTAL)); |
| fFormatBinaryButton.setSelection(format.equals(SEARCH_FORMAT_BINARY)); |
| fFormatDecimalButton.setSelection(format.equals(SEARCH_FORMAT_DECIMAL)); |
| fFormatHexButton.setSelection(format.equals(SEARCH_FORMAT_HEX)); |
| fFormatByteSequenceButton.setSelection(format.equals(SEARCH_FORMAT_BYTESEQUENCE)); |
| |
| data = new FormData(); |
| data.top = new FormAttachment(rangeGroup); |
| formatGroup.setLayoutData(data); |
| |
| // options group |
| |
| Group optionsGroup = new Group(composite, SWT.NONE); |
| optionsGroup.setText(Messages.getString("FindReplaceDialog.LabelOptions")); //$NON-NLS-1$ |
| |
| data = new FormData(); |
| data.left = new FormAttachment(formatGroup); |
| data.top = new FormAttachment(rangeGroup); |
| data.bottom = new FormAttachment(formatGroup, 0, SWT.BOTTOM); |
| data.right = new FormAttachment(rangeGroup, 0, SWT.RIGHT); |
| optionsGroup.setLayoutData(data); |
| |
| layout = new GridLayout(); |
| layout.numColumns = 1; |
| optionsGroup.setLayout(layout); |
| |
| // wrap |
| |
| fWrapCheckbox = new Button(optionsGroup, SWT.CHECK); |
| fWrapCheckbox.setText(Messages.getString("FindReplaceDialog.ButtonWrapSearch")); //$NON-NLS-1$ |
| fWrapCheckbox.setEnabled(false); // TODO implement wrap |
| |
| fCaseInSensitiveCheckbox = new Button(optionsGroup, SWT.CHECK); |
| fCaseInSensitiveCheckbox.setText(Messages.getString("FindReplaceDialog.ButtonCaseInsensitive")); //$NON-NLS-1$ |
| fCaseInSensitiveCheckbox.setEnabled(format.equals(SEARCH_FORMAT_ASCII)); |
| fCaseInSensitiveCheckbox.setSelection( |
| Boolean.parseBoolean(fProperties.getProperty(SEARCH_FORMAT_CASEINSENSTIVE, Boolean.FALSE.toString()))); |
| |
| fFormatAsciiButton.addSelectionListener(new SelectionListener() { |
| @Override |
| public void widgetDefaultSelected(SelectionEvent e) { |
| } |
| |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| fCaseInSensitiveCheckbox.setEnabled(true); |
| } |
| }); |
| |
| SelectionListener nonAsciiListener = new SelectionListener() { |
| @Override |
| public void widgetDefaultSelected(SelectionEvent e) { |
| } |
| |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| fCaseInSensitiveCheckbox.setEnabled(false); |
| validate(); |
| } |
| }; |
| fFormatHexButton.addSelectionListener(nonAsciiListener); |
| fFormatOctalButton.addSelectionListener(nonAsciiListener); |
| fFormatBinaryButton.addSelectionListener(nonAsciiListener); |
| fFormatDecimalButton.addSelectionListener(nonAsciiListener); |
| fFormatByteSequenceButton.addSelectionListener(nonAsciiListener); |
| |
| fStartText.addModifyListener(e -> { |
| boolean valid = true; |
| try { |
| getStartAddress(); |
| } catch (Exception ex) { |
| valid = false; |
| } |
| |
| fStartText.setForeground(valid ? Display.getDefault().getSystemColor(SWT.COLOR_BLACK) |
| : Display.getDefault().getSystemColor(SWT.COLOR_RED)); |
| |
| validate(); |
| }); |
| |
| fEndText.addModifyListener(e -> { |
| try { |
| getEndAddress(); |
| fEndText.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK)); |
| } catch (Exception ex) { |
| fEndText.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_RED)); |
| } |
| |
| validate(); |
| }); |
| |
| fFindText.addModifyListener(e -> validate()); |
| |
| fReplaceText.addModifyListener(e -> validate()); |
| |
| composite.setTabList( |
| new Control[] { fFindText, fReplaceText, directionGroup, rangeGroup, formatGroup, optionsGroup, }); |
| |
| fFindText.setFocus(); |
| |
| return composite; |
| } |
| |
| class FindReplaceMemoryCache { |
| BigInteger memoryCacheStartAddress = BigInteger.ZERO; |
| MemoryByte memoryCacheData[] = new MemoryByte[0]; |
| } |
| |
| /** |
| * Function : getSearchableBytes |
| * |
| * This function returns to the user an array of memory |
| * @param start Address ( inclusive ) of the beginning byte of the memory region to be searched |
| * @param end Address ( inclusive ) of the ending |
| * @param forwardSearch direction of the search ( true == searching forward , false = searching backwards |
| * @param address Address ( inclusive ) of the byte set being requested/returned |
| * @param length Number of bytes of data to be returned |
| * @param cache Cached memory byte data ( this routine fetches additional bytes of memory to try and reduce interaction with the debug engine ) |
| * @return MemoryByte[] array which contains the requested bytes |
| * @throws DebugException |
| */ |
| private MemoryByte[] getSearchableBytes(BigInteger start, BigInteger end, boolean forwardSearch, BigInteger address, |
| int length, FindReplaceMemoryCache cache) throws DebugException { |
| BigInteger endCacheAddress = cache.memoryCacheStartAddress |
| .add(BigInteger.valueOf(cache.memoryCacheData.length)); |
| |
| /* |
| * Determine if the requested data is already within the cache. |
| */ |
| |
| if (!((address.compareTo(cache.memoryCacheStartAddress) >= 0) |
| && (address.add(BigInteger.valueOf(length)).compareTo(endCacheAddress) < 0))) { |
| BigInteger prefetchSize = BigInteger.valueOf(preFetchSize); |
| BigInteger len = BigInteger.valueOf(length); |
| BigInteger fetchAddress = address; |
| BigInteger fetchSize; |
| |
| /* |
| * Determine which way we are searching. Whichever way we are searching we need to make sure |
| * we capture the minimum requested amount of data in the forward direction. |
| */ |
| |
| if (forwardSearch) { |
| /* |
| * Legend : "#" == minimum requested data , "*" == additional data we want to prefetch/cache |
| * |
| * This is the best case where everything cleanly fits within the starting/ending ranges |
| * to be searched. What we cannot do is to fetch data outside of these ranges. The user |
| * has specified them, if they are in error that is OK, but we must respect the boundaries |
| * they specified. |
| * |
| * +-- address |
| * | |
| * +--length--+--prefetch--+------------------------------------+ |
| * |##########|************| | |
| * |##########|************| | |
| * |##########|************| | |
| * +----------+------------+------------------------------------+ |
| * | | |
| * +-- start end --+ |
| * |
| * This is the worst case scenario. We cannot even get the requested minimum ( no matter |
| * the desired prefetch ) before we run out of the specified range. |
| * |
| * +-- address |
| * | |
| * +----------------------------------------------------+--length--+--prefetch--+ |
| * | |##########|************| |
| * | |##########|************| |
| * | |##########|************| |
| * +----------------------------------------------------+-------+--+------------+ |
| * | | |
| * +-- start end --+ |
| * |
| * See if the desired size ( minimum length + desired prefetch ) fits in to the current range. |
| * If so there is nothing to adjust. |
| */ |
| |
| if (prefetchSize.compareTo(len) >= 0) { |
| fetchSize = prefetchSize; |
| } else { |
| fetchSize = len; |
| } |
| |
| if (address.add(fetchSize).compareTo(end) > 0) { |
| /* |
| * It does not all fit. Get as much as we can ( end - current ) + 1. |
| */ |
| fetchSize = end.subtract(address).add(BigInteger.ONE); |
| |
| /* |
| * If the amount of data we can get does not even meet the minimum request. In this case |
| * we have to readjust how much we copy to match what we can actually read. If we do not |
| * do this then we will run past the actual data fetched and generate an exception. |
| */ |
| if (fetchSize.compareTo(len) < 0) { |
| length = fetchSize.intValue(); |
| } |
| } |
| |
| /* |
| * The fetch address just starts at the current requested location since we are searching in |
| * the forward direction and thus prefetching in the forward direction. |
| */ |
| fetchAddress = address; |
| } else { |
| |
| /* |
| * Legend : "#" == minimum requested data , "*" == additional data we want to prefetch/cache |
| * |
| * This is the best case where everything cleanly fits within the starting/ending ranges |
| * to be searched. What we cannot do is to fetch data outside of these ranges. The user |
| * has specified them, if they are in error that is OK, but we must respect the boundaries |
| * they specified. |
| * |
| * +-- address |
| * | |
| * +--prefetch--+--length--+------------------------------------+ |
| * |************|##########| | |
| * |************|##########| | |
| * |************|##########| | |
| * +------------+----------+------------------------------------+ |
| * | | |
| * +-- start end --+ |
| * |
| * This is the second worst case scenario. We cannot even get the requested minimum ( no matter |
| * the desired prefetch ) before we run out of the specified range. |
| * |
| * +-- address |
| * | |
| * +--------------------------------------------+--prefetch--+--length--+ |
| * | |************|##########| |
| * | |************|##########| |
| * | |************|##########| |
| * +--------------------------------------------+------------+--+-------+ |
| * | | |
| * +-- start end --+ |
| * |
| * This is the worst case scenario. The minimum length moves us off the end of the high range |
| * end and the prefetch before this minimum data request ( remember we are fetching backwards |
| * since we are searching backwards ) runs us off the start of the data. |
| * |
| * +-- address |
| * | |
| * +---+-----------------------------------------------prefetch--+--length--+ |
| * |*************************************************************|##########| |
| * |*************************************************************|##########| |
| * |*************************************************************|##########| |
| * +---+---------------------------------------------------------+--+-------+ |
| * | | |
| * +-- start end --+ |
| * |
| * See if the desired size ( minimum length + desired prefetch ) fits in to the current range. |
| * Without running off the end. |
| */ |
| if (address.add(len).compareTo(end) > 0) { |
| /* |
| * We need to reduce the amount we can ask for to whats left. Also make sure to reduce the |
| * amount to copy, otherwise we will overrun the buffer and generate an exception. |
| */ |
| len = end.subtract(address).add(BigInteger.ONE); |
| length = len.intValue(); |
| } |
| |
| /* |
| * Now determine if the prefetch is going to run backwards past the "start" of where we are allowed |
| * to access the memory. We will normalize the prefetch size so it takes in to account the amount of |
| * data being gathered as part of the length requested portion. This should insure that in the end |
| * we will request the prefetch amount of data unless there is not enough to service this request. |
| */ |
| if (len.compareTo(prefetchSize) > 0) { |
| prefetchSize = BigInteger.ZERO; |
| } else { |
| prefetchSize = prefetchSize.subtract(len); |
| } |
| |
| if (address.subtract(prefetchSize).compareTo(start) < 0) { |
| /* |
| * Just get what we can from the beginning up to the current required address. |
| */ |
| prefetchSize = address.subtract(start); |
| fetchAddress = start; |
| } else { |
| /* |
| * It fits so just start reading from the calculated position prior to the requested point. |
| */ |
| fetchAddress = address.subtract(prefetchSize); |
| } |
| |
| fetchSize = len.add(prefetchSize); |
| } |
| |
| /* |
| * OK, we have determined where to start reading the data and how much. Just get the data |
| * and store it in the cache. |
| */ |
| MemoryByte bytes[] = fMemoryBlock.getBytesFromAddress(fetchAddress, fetchSize.longValue()); |
| |
| cache.memoryCacheStartAddress = fetchAddress; |
| cache.memoryCacheData = bytes; |
| } |
| |
| /* |
| * Either it was already cached or just has been, either way we have the data so copy what we can |
| * back to the user buffer. |
| */ |
| |
| MemoryByte bytes[] = new MemoryByte[length]; |
| System.arraycopy(cache.memoryCacheData, address.subtract(cache.memoryCacheStartAddress).intValue(), bytes, 0, |
| length); |
| |
| return bytes; |
| } |
| |
| private BigInteger parseHexBigInteger(String s) { |
| if (s.toUpperCase().startsWith("0X")) //$NON-NLS-1$ |
| return new BigInteger(s.substring(2), 16); |
| else |
| return new BigInteger(s, 16); |
| } |
| |
| protected void performFindNext() { |
| try { |
| BigInteger start = parseHexBigInteger(fProperties.getProperty(SEARCH_LAST_START)); |
| BigInteger end = parseHexBigInteger(fProperties.getProperty(SEARCH_LAST_END)); |
| boolean searchForward = Boolean |
| .parseBoolean(fProperties.getProperty(SEARCH_FORMAT_FORWARD, Boolean.FALSE.toString())); |
| boolean caseInSensitive = Boolean |
| .parseBoolean(fProperties.getProperty(SEARCH_FORMAT_CASEINSENSTIVE, Boolean.FALSE.toString())); |
| SearchPhrase phrase = null; |
| String findText = fProperties.getProperty(SEARCH_FIND); |
| |
| if (fProperties.getProperty(SEARCH_FORMAT).equals(SEARCH_FORMAT_ASCII)) |
| phrase = new AsciiSearchPhrase(findText, caseInSensitive); |
| else if (fProperties.getProperty(SEARCH_FORMAT).equals(SEARCH_FORMAT_HEX)) |
| phrase = new BigIntegerSearchPhrase( |
| new BigInteger(findText.toUpperCase().startsWith("0X") ? findText.substring(2) : findText, 16), //$NON-NLS-1$ |
| 16); |
| else if (fProperties.getProperty(SEARCH_FORMAT).equals(SEARCH_FORMAT_OCTAL)) |
| phrase = new BigIntegerSearchPhrase( |
| new BigInteger(findText.startsWith("0") ? findText.substring(1) : findText, 8), 8); //$NON-NLS-1$ |
| else if (fProperties.getProperty(SEARCH_FORMAT).equals(SEARCH_FORMAT_BINARY)) |
| phrase = new BigIntegerSearchPhrase( |
| new BigInteger(findText.toUpperCase().startsWith("0B") ? findText.substring(2) : findText, 2), //$NON-NLS-1$ |
| 2); |
| else if (fProperties.getProperty(SEARCH_FORMAT).equals(SEARCH_FORMAT_DECIMAL)) |
| phrase = new BigIntegerSearchPhrase(new BigInteger(findText, 10), 10); |
| else if (fProperties.getProperty(SEARCH_FORMAT).equals(SEARCH_FORMAT_BYTESEQUENCE)) |
| phrase = new ByteSequenceSearchPhrase(findText); |
| |
| performFind(start, end, phrase, searchForward, null, false, false); |
| |
| } catch (Exception e) { |
| MemorySearchPlugin.logError(Messages.getString("FindReplaceDialog.MemorySearchFailure"), e); //$NON-NLS-1$ |
| } |
| } |
| |
| private void performFind(final BigInteger start, final BigInteger end, final SearchPhrase searchPhrase, |
| final boolean searchForward, final byte[] replaceData, final boolean all, final boolean replaceThenFind) { |
| final ISearchQuery query = new IMemorySearchQuery() { |
| private ISearchResult fSearchResult = null; |
| |
| @Override |
| public boolean canRerun() { |
| return false; |
| } |
| |
| @Override |
| public boolean canRunInBackground() { |
| return true; |
| } |
| |
| @Override |
| public String getLabel() { |
| return Messages.getString("FindReplaceDialog.SearchingMemoryFor") + searchPhrase; //$NON-NLS-1$ |
| } |
| |
| @Override |
| public ISearchResult getSearchResult() { |
| if (fSearchResult == null) |
| fSearchResult = new MemorySearchResult(this, |
| Messages.getString("FindReplaceDialog.SearchingMemoryFor") + searchPhrase); //$NON-NLS-1$ |
| return fSearchResult; |
| } |
| |
| @Override |
| public IStatus run(IProgressMonitor monitor) throws OperationCanceledException { |
| |
| final BigInteger searchPhraseLength = BigInteger.valueOf(searchPhrase.getByteLength()); |
| BigInteger range = end.subtract(start).add(BigInteger.ONE); |
| BigInteger currentPosition = searchForward ? start : end.subtract(searchPhraseLength); |
| |
| if (searchPhraseLength.compareTo(range) >= 0) { |
| return Status.OK_STATUS; |
| } |
| |
| boolean isReplace = replaceData != null; |
| |
| BigInteger jobs = range; |
| BigInteger factor = BigInteger.ONE; |
| if (jobs.compareTo(BigInteger.valueOf(0x07FFFFFF)) > 0) { |
| factor = jobs.divide(BigInteger.valueOf(0x07FFFFFF)); |
| jobs = jobs.divide(factor); |
| } |
| |
| BigInteger jobCount = BigInteger.ZERO; |
| |
| BigInteger replaceCount = BigInteger.ZERO; |
| |
| FindReplaceMemoryCache cache = new FindReplaceMemoryCache(); |
| |
| monitor.beginTask(Messages.getString("FindReplaceDialog.SearchingMemoryFor") + searchPhrase, //$NON-NLS-1$ |
| jobs.intValue()); |
| |
| boolean matched = false; |
| while (((searchForward && currentPosition.compareTo(end.subtract(searchPhraseLength)) < 0) |
| || (!searchForward && currentPosition.compareTo(start) > 0)) && !monitor.isCanceled()) { |
| try { |
| MemoryByte bytes[] = getSearchableBytes(start, end, searchForward, currentPosition, |
| searchPhraseLength.intValue(), cache); |
| matched = searchPhrase.isMatch(bytes); |
| if (matched) { |
| if (all && !isReplace) |
| ((MemorySearchResult) getSearchResult()) |
| .addMatch(new MemoryMatch(currentPosition, searchPhraseLength)); |
| |
| if (isReplace) { |
| try { |
| if ((searchPhrase instanceof BigIntegerSearchPhrase) && (bytes.length > 0) |
| && bytes[0].isEndianessKnown() && !bytes[0].isBigEndian()) { |
| // swap the bytes when replacing an integer on little-endian targets |
| fMemoryBlock.setValue( |
| currentPosition.subtract(fMemoryBlock.getBigBaseAddress()), |
| swapBytes(replaceData)); |
| } else { |
| fMemoryBlock.setValue( |
| currentPosition.subtract(fMemoryBlock.getBigBaseAddress()), |
| replaceData); |
| } |
| } catch (DebugException de) { |
| MemorySearchPlugin |
| .logError(Messages.getString("FindReplaceDialog.MemoryReadFailed"), de); //$NON-NLS-1$ |
| } |
| |
| replaceCount = replaceCount.add(BigInteger.ONE); |
| } |
| |
| if (isReplace && replaceThenFind && replaceCount.compareTo(BigInteger.ONE) == 0) { |
| isReplace = false; |
| matched = false; |
| } |
| |
| if (matched && !all) { |
| final BigInteger finalCurrentPosition = currentPosition; |
| final BigInteger finalStart = start; |
| final BigInteger finalEnd = end; |
| Display.getDefault().asyncExec(() -> { |
| IMemoryRenderingContainer containers[] = getMemoryView() |
| .getMemoryRenderingContainers(); |
| for (int i = 0; i < containers.length; i++) { |
| IMemoryRendering rendering = containers[i].getActiveRendering(); |
| if (rendering instanceof IRepositionableMemoryRendering) { |
| try { |
| ((IRepositionableMemoryRendering) rendering) |
| .goToAddress(finalCurrentPosition); |
| } catch (DebugException e1) { |
| MemorySearchPlugin.logError( |
| Messages.getString( |
| "FindReplaceDialog.RepositioningMemoryViewFailed"), //$NON-NLS-1$ |
| e1); |
| } |
| } |
| if (rendering != null) { |
| // Temporary, until platform accepts/adds new interface for setting the selection |
| try { |
| Method m = rendering.getClass().getMethod("setSelection", //$NON-NLS-1$ |
| new Class[] { BigInteger.class, BigInteger.class }); |
| if (m != null) |
| m.invoke(rendering, finalCurrentPosition, |
| finalCurrentPosition.add(searchPhraseLength)); |
| } catch (Exception e2) { |
| // do nothing |
| } |
| } |
| } |
| }); |
| |
| fProperties.setProperty(SEARCH_ENABLE_FIND_NEXT, Boolean.TRUE.toString()); |
| if (searchForward) { |
| BigInteger newFinalStart = finalCurrentPosition.add(BigInteger.ONE); |
| fProperties.setProperty(SEARCH_LAST_START, "0x" + newFinalStart.toString(16)); //$NON-NLS-1$ |
| fProperties.setProperty(SEARCH_LAST_END, "0x" + finalEnd.toString(16)); //$NON-NLS-1$ |
| } else { |
| BigInteger newFinalEnd = finalCurrentPosition.subtract(BigInteger.ONE); |
| fProperties.setProperty(SEARCH_LAST_START, "0x" + finalStart.toString(16)); //$NON-NLS-1$ |
| fProperties.setProperty(SEARCH_LAST_END, "0x" + newFinalEnd.toString(16)); //$NON-NLS-1$ |
| } |
| if (fFindAction != null) { |
| fFindAction.setEnabled(true); |
| } |
| return Status.OK_STATUS; |
| } |
| } |
| |
| matched = false; |
| |
| if (searchForward) |
| currentPosition = currentPosition.add(BigInteger.ONE); |
| else |
| currentPosition = currentPosition.subtract(BigInteger.ONE); |
| |
| } catch (DebugException e) { |
| MemorySearchPlugin.logError(Messages.getString("FindReplaceDialog.MemorySearchFailure"), e); //$NON-NLS-1$ |
| return Status.CANCEL_STATUS; |
| } |
| |
| jobCount = jobCount.add(BigInteger.ONE); |
| if (jobCount.compareTo(factor) == 0) { |
| jobCount = BigInteger.ZERO; |
| monitor.worked(1); |
| } |
| } |
| |
| if (monitor.isCanceled()) |
| return Status.CANCEL_STATUS; |
| |
| return Status.OK_STATUS; |
| } |
| |
| @Override |
| public IMemoryRenderingSite getMemoryView() { |
| return fMemoryView; |
| } |
| }; |
| |
| if (all && replaceData == null) { |
| Display.getDefault().asyncExec(() -> { |
| NewSearchUI.activateSearchResultView(); |
| NewSearchUI.runQueryInBackground(query); |
| }); |
| } else { |
| Job job = new Job("Searching memory for " + searchPhrase) { //$NON-NLS-1$ |
| @Override |
| public IStatus run(IProgressMonitor monitor) { |
| return query.run(monitor); |
| } |
| }; |
| job.schedule(); |
| } |
| |
| } |
| |
| interface SearchPhrase { |
| boolean isMatch(MemoryByte[] bytes); |
| |
| int getByteLength(); |
| |
| @Override |
| String toString(); |
| } |
| |
| class AsciiSearchPhrase implements SearchPhrase { |
| private String fPhrase; |
| private boolean fIsCaseInsensitive; |
| |
| public AsciiSearchPhrase(String phrase, boolean isCaseInsensitive) { |
| fPhrase = phrase; |
| fIsCaseInsensitive = isCaseInsensitive; |
| } |
| |
| @Override |
| public int getByteLength() { |
| return fPhrase.length(); |
| } |
| |
| @Override |
| public String toString() { |
| return fPhrase; |
| } |
| |
| @Override |
| public boolean isMatch(MemoryByte[] bytes) { |
| byte[] targetBytes = new byte[bytes.length]; |
| for (int i = 0; i < bytes.length; i++) |
| targetBytes[i] = bytes[i].getValue(); |
| |
| String searchString = fPhrase; |
| String targetString = new String(targetBytes); |
| |
| if (fIsCaseInsensitive) { |
| searchString = searchString.toUpperCase(); |
| targetString = targetString.toUpperCase(); |
| } |
| |
| return searchString.equals(targetString); |
| } |
| } |
| |
| class ByteSequenceSearchPhrase implements SearchPhrase { |
| private byte[] fBytes = null; |
| |
| public ByteSequenceSearchPhrase(String phrase) { |
| fBytes = parseByteSequence(phrase); |
| } |
| |
| @Override |
| public int getByteLength() { |
| if (fBytes != null) { |
| return fBytes.length; |
| } else { |
| return 0; |
| } |
| } |
| |
| @Override |
| public String toString() { |
| if (fBytes == null) |
| return ""; //$NON-NLS-1$ |
| StringBuilder buf = new StringBuilder(); |
| for (int i = 0; i < fBytes.length; i++) |
| buf.append(BigInteger.valueOf(fBytes[i]).toString(16)).append(' '); |
| return buf.toString(); |
| } |
| |
| @Override |
| public boolean isMatch(MemoryByte[] bytes) { |
| if (fBytes == null) |
| return false; |
| for (int i = 0; i < bytes.length; i++) |
| if (bytes[i].getValue() != fBytes[i]) |
| return false; |
| return true; |
| } |
| } |
| |
| class BigIntegerSearchPhrase implements SearchPhrase { |
| private BigInteger fPhrase; |
| private int fRadix; |
| |
| public BigIntegerSearchPhrase(BigInteger phrase, int radix) { |
| fPhrase = phrase; |
| fRadix = radix; |
| } |
| |
| @Override |
| public int getByteLength() { |
| return removeZeroPrefixByte(fPhrase.toByteArray()).length; |
| } |
| |
| @Override |
| public String toString() { |
| return fPhrase.toString(fRadix); |
| } |
| |
| @Override |
| public boolean isMatch(MemoryByte[] bytes) { |
| byte[] targetBytes = new byte[bytes.length + 1]; |
| targetBytes[0] = 0; |
| for (int i = 0; i < bytes.length; i++) { |
| if (bytes[i].isEndianessKnown() && !bytes[i].isBigEndian()) { |
| // swap the bytes when matching an integer on little-endian targets |
| targetBytes[i + 1] = bytes[bytes.length - i - 1].getValue(); |
| } else { |
| targetBytes[i + 1] = bytes[i].getValue(); |
| } |
| } |
| |
| BigInteger targetBigInteger = new BigInteger(targetBytes); |
| |
| return fPhrase.equals(targetBigInteger); |
| } |
| } |
| |
| private byte[] removeZeroPrefixByte(byte[] bytes) { |
| if (bytes[0] != 0 || bytes.length == 1) |
| return bytes; |
| |
| byte[] processedBytes = new byte[bytes.length - 1]; |
| System.arraycopy(bytes, 1, processedBytes, 0, processedBytes.length); |
| return processedBytes; |
| } |
| |
| private byte[] swapBytes(byte[] bytes) { |
| byte[] processedBytes = new byte[bytes.length]; |
| for (int i = 0; i < bytes.length; i++) |
| processedBytes[i] = bytes[bytes.length - i - 1]; |
| return processedBytes; |
| } |
| |
| interface IMemorySearchQuery extends ISearchQuery { |
| public IMemoryRenderingSite getMemoryView(); |
| } |
| |
| } |