Repository: nvaccess/nvda

Issue:
Credicle bug of this dictionary.
**Reported by vgjh2005 on 2015-01-20 08:14**
Hi:
This dic file include errors, and put it in speechdic folder, Press CTRL+ALT+N to restart NVDA, NVDA will not start again at all. There are also no error information. Thanks!



Discussion:
Attachment default.dic added by vgjh2005 on 2015-01-20 08:15
Description:

Comment 1 by chrislm on 2015-01-20 09:34
Please check your default.dic in line 41.
Error: unbalanced parenthesis

Comment 2 by vgjh2005 on 2015-01-20 10:18
Hi:
Yes. you're right. This isn't my point. My point is that NVDA cannot launch correctly. I can hear a error sound, and NVDA isn't launched. I have made more times for this. Thanks!

Comment 3 by jteh on 2015-01-20 10:40
Changes:
Milestone changed from None to next

[default.dic.txt](https://github.com/nvaccess/nvda/files/342774/default.dic.txt)

The attachment referenced, just remove the .txt extension.

Trace from log:

```
Traceback (most recent call last):
  File "nvda.pyw", line 190, in <module>
    core.main()
  File "core.py", line 198, in main
    speechDictHandler.initialize()
  File "speechDictHandler.py", line 108, in initialize
    dictionaries["default"].load(os.path.join(speechDictsPath, "default.dic"))
  File "speechDictHandler.py", line 70, in load
    self.append(SpeechDictEntry(temp[0].replace(r'\#','#'),temp[1].replace(r'\#','#'),comment,bool(int(temp[2])),int(temp[3])))
  File "speechDictHandler.py", line 37, in __init__
    self.compiled = re.compile(tempPattern,flags)
  File "C:\Python27\lib\re.py", line 194, in compile
    return _compile(pattern, flags)
  File "C:\Python27\lib\re.py", line 251, in _compile
    raise error, v # invalid expression
error: unbalanced parenthesis
```

This can also be reproduced by adding a new entry through the GUI
- Open default dictionary settings.
- Click add
- fill in the fields, use an invalid regular expression for the pattern (eg unbalanced parenthesis)
- ensure that the reg ex option is selected
- click ok.

From the brief testing I did earlier, doing it from the GUI doesn't seem to actually add the entry if you enter an invalid regexp. So, unless I'm missing something, while we should definitely handle this more gracefully, this can't create a startup failure because it never gets saved. That said, it may make some sense to protect against loading such an invalid entry at startup anyway just in case a file got corrupted or the like.


Resolving patch:
diff --git a/source/gui/settingsDialogs.py b/source/gui/settingsDialogs.py
--- a/source/gui/settingsDialogs.py
+++ b/source/gui/settingsDialogs.py
@@ -1240,6 +1240,7 @@ def __init__(self, parent, title=_("Edit Dictionary Entry")):
 		self.SetSizer(mainSizer)
 		self.setType(speechDictHandler.ENTRY_TYPE_ANYWHERE)
 		self.patternTextCtrl.SetFocus()
+		self.Bind(wx.EVT_BUTTON,self.onOk,id=wx.ID_OK)
 
 	def getType(self):
 		typeRadioValue = self.typeRadioBox.GetSelection()
@@ -1247,6 +1248,16 @@ def getType(self):
 			return speechDictHandler.ENTRY_TYPE_ANYWHERE
 		return DictionaryEntryDialog.TYPE_LABELS_ORDERING[typeRadioValue]
 
+	def onOk(self,evt):
+		try:
+			self.dictEntry=speechDictHandler.SpeechDictEntry(self.patternTextCtrl.GetValue(),self.replacementTextCtrl.GetValue(),self.commentTextCtrl.GetValue(),bool(self.caseSensitiveCheckBox.GetValue()),self.getType())
+		except Exception as e:
+			log.debugWarning("Could not add dictionary entry due to (regex error) : %s" % e)
+			# Translators: This is an error message to let the user know that the dictionary entry is not valid.
+			gui.messageBox(_("Regular Expression error: \"%s\".")%e, _("Dictionary Entry Error"), wx.OK|wx.ICON_WARNING, self)
+			return 
+		evt.Skip()
+
 	def setType(self, type):
 		self.typeRadioBox.SetSelection(DictionaryEntryDialog.TYPE_LABELS_ORDERING.index(type))
 
@@ -1320,7 +1331,7 @@ def OnAddClick(self,evt):
 		# Translators: This is the label for the add dictionary entry dialog.
 		entryDialog=DictionaryEntryDialog(self,title=_("Add Dictionary Entry"))
 		if entryDialog.ShowModal()==wx.ID_OK:
-			self.tempSpeechDict.append(speechDictHandler.SpeechDictEntry(entryDialog.patternTextCtrl.GetValue(),entryDialog.replacementTextCtrl.GetValue(),entryDialog.commentTextCtrl.GetValue(),bool(entryDialog.caseSensitiveCheckBox.GetValue()),entryDialog.getType()))
+			self.tempSpeechDict.append(entryDialog.dictEntry)
 			self.dictList.Append((entryDialog.commentTextCtrl.GetValue(),entryDialog.patternTextCtrl.GetValue(),entryDialog.replacementTextCtrl.GetValue(),self.offOn[int(entryDialog.caseSensitiveCheckBox.GetValue())],DictionaryDialog.TYPE_LABELS[entryDialog.getType()]))
 			index=self.dictList.GetFirstSelected()
 			while index>=0:
@@ -1345,7 +1356,7 @@ def OnEditClick(self,evt):
 		entryDialog.caseSensitiveCheckBox.SetValue(self.tempSpeechDict[editIndex].caseSensitive)
 		entryDialog.setType(self.tempSpeechDict[editIndex].type)
 		if entryDialog.ShowModal()==wx.ID_OK:
-			self.tempSpeechDict[editIndex]=speechDictHandler.SpeechDictEntry(entryDialog.patternTextCtrl.GetValue(),entryDialog.replacementTextCtrl.GetValue(),entryDialog.commentTextCtrl.GetValue(),bool(entryDialog.caseSensitiveCheckBox.GetValue()),entryDialog.getType())
+			self.tempSpeechDict[editIndex]=entryDialog.dictEntry
 			self.dictList.SetStringItem(editIndex,0,entryDialog.commentTextCtrl.GetValue())
 			self.dictList.SetStringItem(editIndex,1,entryDialog.patternTextCtrl.GetValue())
 			self.dictList.SetStringItem(editIndex,2,entryDialog.replacementTextCtrl.GetValue())
diff --git a/source/speechDictHandler.py b/source/speechDictHandler.py
--- a/source/speechDictHandler.py
+++ b/source/speechDictHandler.py
@@ -67,7 +67,13 @@ def load(self, fileName):
 			else:
 				temp=line.split("\t")
 				if len(temp) ==4:
-					self.append(SpeechDictEntry(temp[0].replace(r'\#','#'),temp[1].replace(r'\#','#'),comment,bool(int(temp[2])),int(temp[3])))
+					pattern = temp[0].replace(r'\#','#')
+					replace = temp[1].replace(r'\#','#')
+					try:
+						dictionaryEntry=SpeechDictEntry(pattern, replace, comment, caseSensitive=bool(int(temp[2])), type=int(temp[3]))
+						self.append(dictionaryEntry)
+					except Exception as e:
+						log.exception("Dictionary (\"%s\") entry invalid for \"%s\" error raised: \"%s\"" % (fileName, line, e))
 					comment=""
 				else:
 					log.warning("can't parse line '%s'" % line)


Test patch: