about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--res/config.toml2
-rw-r--r--src/config/mailcap.nim92
-rw-r--r--src/local/pager.nim35
3 files changed, 57 insertions, 72 deletions
diff --git a/res/config.toml b/res/config.toml
index 739a7099..20b1227f 100644
--- a/res/config.toml
+++ b/res/config.toml
@@ -149,7 +149,7 @@ sourceEdit = '''
 () => {
 	const url = pager.url;
 	pager.extern(pager.getEditorCommand(url.protocol == "file:" ?
-		url.pathname :
+		decodeURIComponent(url.pathname) :
 		pager.cacheFile));
 }
 '''
diff --git a/src/config/mailcap.nim b/src/config/mailcap.nim
index 41575ca8..fd4e1f0d 100644
--- a/src/config/mailcap.nim
+++ b/src/config/mailcap.nim
@@ -206,111 +206,115 @@ proc parseMailcap*(stream: Stream): Result[Mailcap, string] =
 
 # Mostly based on w3m's mailcap quote/unquote
 type UnquoteState = enum
-  STATE_NORMAL, STATE_QUOTED, STATE_PERC, STATE_ATTR, STATE_ATTR_QUOTED,
-  STATE_DOLLAR
+  usNormal, usQuoted, usPerc, usAttr, usAttrQuoted, usDollar
 
 type UnquoteResult* = object
   canpipe*: bool
   cmd*: string
 
-type QuoteState = enum
-  QS_NORMAL, QS_DQUOTED, QS_SQUOTED
+type QuoteState* = enum
+  qsNormal, qsDoubleQuoted, qsSingleQuoted
 
-proc quoteFile(file: string; qs: QuoteState): string =
+proc quoteFile*(file: string; qs: QuoteState): string =
   var s = ""
   for c in file:
     case c
     of '$', '`', '"', '\\':
-      if qs != QS_SQUOTED:
+      if qs != qsSingleQuoted:
         s &= '\\'
     of '\'':
-      if qs == QS_SQUOTED:
+      if qs == qsSingleQuoted:
         s &= "'\\'" # then re-open the quote by appending c
-      elif qs == QS_NORMAL:
+      elif qs == qsNormal:
         s &= '\\'
       # double-quoted: append normally
     of AsciiAlphaNumeric, '_', '.', ':', '/':
       discard # no need to quote
-    elif qs == QS_NORMAL:
+    elif qs == qsNormal:
       s &= '\\'
     s &= c
   return s
 
 proc unquoteCommand*(ecmd, contentType, outpath: string; url: URL;
-    canpipe: var bool): string =
+    canpipe: var bool; line = -1): string =
   var cmd = ""
   var attrname = ""
   var state: UnquoteState
-  var qss = @[QS_NORMAL] # quote state stack. len >1
+  var qss = @[qsNormal] # quote state stack. len >1
   template qs: var QuoteState = qss[^1]
   for c in ecmd:
     case state
-    of STATE_QUOTED:
+    of usQuoted:
       cmd &= c
-      state = STATE_NORMAL
-    of STATE_ATTR_QUOTED:
+      state = usNormal
+    of usAttrQuoted:
       attrname &= c.toLowerAscii()
-      state = STATE_ATTR
-    of STATE_NORMAL, STATE_DOLLAR:
-      let prev_dollar = state == STATE_DOLLAR
-      state = STATE_NORMAL
+      state = usAttr
+    of usNormal, usDollar:
+      let prev_dollar = state == usDollar
+      state = usNormal
       case c
       of '%':
-        state = STATE_PERC
+        state = usPerc
       of '\\':
-        state = STATE_QUOTED
+        state = usQuoted
       of '\'':
-        if qs == QS_SQUOTED:
-          qs = QS_NORMAL
+        if qs == qsSingleQuoted:
+          qs = qsNormal
         else:
-          qs = QS_SQUOTED
+          qs = qsSingleQuoted
         cmd &= c
       of '"':
-        if qs == QS_DQUOTED:
-          qs = QS_NORMAL
+        if qs == qsDoubleQuoted:
+          qs = qsNormal
         else:
-          qs = QS_DQUOTED
+          qs = qsDoubleQuoted
         cmd &= c
       of '$':
-        if qs != QS_SQUOTED:
-          state = STATE_DOLLAR
+        if qs != qsSingleQuoted:
+          state = usDollar
         cmd &= c
       of '(':
         if prev_dollar:
-          qss.add(QS_NORMAL)
+          qss.add(qsNormal)
         cmd &= c
       of ')':
-        if qs != QS_SQUOTED:
+        if qs != qsSingleQuoted:
           if qss.len > 1:
             qss.setLen(qss.len - 1)
           else:
             # mismatched parens; probably an invalid shell command...
-            qss[0] = QS_NORMAL
+            qss[0] = qsNormal
         cmd &= c
       else:
         cmd &= c
-    of STATE_PERC:
-      if c == '%':
-        cmd &= c
-      elif c == 's':
+    of usPerc:
+      case c
+      of '%': cmd &= c
+      of 's':
         cmd &= quoteFile(outpath, qs)
         canpipe = false
-      elif c == 't':
+      of 't':
         cmd &= quoteFile(contentType.until(';'), qs)
-      elif c == 'u': # extension
-        cmd &= quoteFile($url, qs)
-      elif c == '{':
-        state = STATE_ATTR
+      of 'u': # Netscape extension
+        if url != nil: # nil in getEditorCommand
+          cmd &= quoteFile($url, qs)
+      of 'd': # line; not used in mailcap, only in getEditorCommand
+        if line != -1: # -1 in mailcap
+          cmd &= $line
+      of '{':
+        state = usAttr
         continue
-      state = STATE_NORMAL
-    of STATE_ATTR:
+      else: discard
+      state = usNormal
+    of usAttr:
       if c == '}':
         let s = contentType.getContentTypeAttr(attrname)
         cmd &= quoteFile(s, qs)
         attrname = ""
-        state = STATE_NORMAL
+        state = usNormal
       elif c == '\\':
-        state = STATE_ATTR_QUOTED
+        state = usAttrQuoted
       else:
         attrname &= c
   return cmd
diff --git a/src/local/pager.nim b/src/local/pager.nim
index 5c639a20..25042f5f 100644
--- a/src/local/pager.nim
+++ b/src/local/pager.nim
@@ -801,39 +801,20 @@ proc toggleSource(pager: Pager) {.jsfunc.} =
       pager.container.sourcepair = container
       pager.addContainer(container)
 
-func formatEditorName(editor, file: string; line: int): string =
-  result = newStringOfCap(editor.len + file.len)
-  var i = 0
-  var filefound = false
-  while i < editor.len:
-    if editor[i] == '%' and i < editor.high:
-      if editor[i + 1] == 's':
-        result &= file
-        filefound = true
-        i += 2
-        continue
-      elif editor[i + 1] == 'd':
-        result &= $line
-        i += 2
-        continue
-      elif editor[i + 1] == '%':
-        result &= '%'
-        i += 2
-        continue
-    result &= editor[i]
-    inc i
-  if not filefound:
-    if result[^1] != ' ':
-      result &= ' '
-    result &= file
-
 func getEditorCommand(pager: Pager; file: string; line = 1): string {.jsfunc.} =
   var editor = pager.config.external.editor
   if editor == "":
     editor = getEnv("EDITOR")
     if editor == "":
       editor = "vi %s +%d"
-  return formatEditorName(editor, file, line)
+  var canpipe = false
+  var s = unquoteCommand(editor, "", file, nil, canpipe, line)
+  if canpipe:
+    # %s not in command; add file name ourselves
+    if s[^1] != ' ':
+      s &= ' '
+    s &= quoteFile(file, qsNormal)
+  return s
 
 proc openInEditor(pager: Pager; input: var string): bool =
   try: