| 983 | | "Search for a subshell embedded in a string. Find all the unescaped |
|---|
| 984 | | \" characters within said subshell, remembering that subshells can nest." |
|---|
| 985 | | (if (re-search-forward "\"\\(?:.\\|\n\\)*?\\(\\$(\\|`\\)" limit t) |
|---|
| 986 | | ;; bingo we have a $( or a ` inside a "" |
|---|
| 987 | | (let ((char (char-after (point))) |
|---|
| 988 | | (continue t) |
|---|
| 989 | | (pos (point)) |
|---|
| 990 | | (data nil) ;; value to put into match-data (and return) |
|---|
| 991 | | (last nil) ;; last char seen |
|---|
| 992 | | (bq (equal (match-string 1) "`")) ;; ` state flip-flop |
|---|
| 993 | | (seen nil) ;; list of important positions |
|---|
| 994 | | (nest 1)) ;; subshell nesting level |
|---|
| 995 | | (while (and continue char (<= pos limit)) |
|---|
| 996 | | ;; unescaped " inside a $( ... ) construct. |
|---|
| 997 | | ;; state machine time... |
|---|
| 998 | | ;; \ => ignore next char; |
|---|
| 999 | | ;; ` => increase or decrease nesting level based on bq flag |
|---|
| 1000 | | ;; ) [where nesting > 0] => decrease nesting |
|---|
| 1001 | | ;; ( [where nesting > 0] => increase nesting |
|---|
| 1002 | | ;; ( [preceeded by $ ] => increase nesting |
|---|
| 1003 | | ;; " [nesting <= 0 ] => terminate, we're done. |
|---|
| 1004 | | ;; " [nesting > 0 ] => remember this, it's not a proper " |
|---|
| 1005 | | (if (eq ?\\ last) nil |
|---|
| 1006 | | (if (eq ?\` char) (setq nest (+ nest (if bq -1 1)) bq (not bq)) |
|---|
| 1007 | | (if (and (> nest 0) (eq ?\) char)) (setq nest (1- nest)) |
|---|
| 1008 | | (if (and (eq ?$ last) (eq ?\( char)) (setq nest (1+ nest)) |
|---|
| 1009 | | (if (and (> nest 0) (eq ?\( char)) (setq nest (1+ nest)) |
|---|
| 1010 | | (if (eq char ?\") |
|---|
| 1011 | | (if (>= 0 nest) (setq continue nil) |
|---|
| 1012 | | (setq seen (cons pos seen)) ) )))))) |
|---|
| 1013 | | ;;(message "POS: %d [%d]" pos nest) |
|---|
| 1014 | | (setq last char |
|---|
| 1015 | | pos (1+ pos) |
|---|
| 1016 | | char (char-after pos)) ) |
|---|
| 1017 | | (when seen |
|---|
| 1018 | | ;;(message "SEEN: %S" seen) |
|---|
| 1019 | | (setq data (list (current-buffer))) |
|---|
| 1020 | | (mapc (lambda (P) |
|---|
| 1021 | | (setq data (cons P (cons (1+ P) data)) ) ) seen) |
|---|
| 1022 | | (store-match-data data)) |
|---|
| 1023 | | data) )) |
|---|
| | 983 | "Search for a subshell embedded in a string. |
|---|
| | 984 | Find all the unescaped \" characters within said subshell, remembering that |
|---|
| | 985 | subshells can nest." |
|---|
| | 986 | ;; FIXME: This can (and often does) match multiple lines, yet it makes no |
|---|
| | 987 | ;; effort to handle multiline cases correctly, so it ends up being |
|---|
| | 988 | ;; rather flakey. |
|---|
| | 989 | (when (re-search-forward "\"\\(?:\\(?:.\\|\n\\)*?[^\\]\\(?:\\\\\\\\\\)*\\)??\\(\\$(\\|`\\)" limit t) |
|---|
| | 990 | ;; bingo we have a $( or a ` inside a "" |
|---|
| | 991 | (let ((char (char-after (point))) |
|---|
| | 992 | (continue t) |
|---|
| | 993 | (pos (point)) |
|---|
| | 994 | (data nil) ;; value to put into match-data (and return) |
|---|
| | 995 | (last nil) ;; last char seen |
|---|
| | 996 | (bq (equal (match-string 1) "`")) ;; ` state flip-flop |
|---|
| | 997 | (seen nil) ;; list of important positions |
|---|
| | 998 | (nest 1)) ;; subshell nesting level |
|---|
| | 999 | (while (and continue char (<= pos limit)) |
|---|
| | 1000 | ;; unescaped " inside a $( ... ) construct. |
|---|
| | 1001 | ;; state machine time... |
|---|
| | 1002 | ;; \ => ignore next char; |
|---|
| | 1003 | ;; ` => increase or decrease nesting level based on bq flag |
|---|
| | 1004 | ;; ) [where nesting > 0] => decrease nesting |
|---|
| | 1005 | ;; ( [where nesting > 0] => increase nesting |
|---|
| | 1006 | ;; ( [preceeded by $ ] => increase nesting |
|---|
| | 1007 | ;; " [nesting <= 0 ] => terminate, we're done. |
|---|
| | 1008 | ;; " [nesting > 0 ] => remember this, it's not a proper " |
|---|
| | 1009 | ;; FIXME: don't count parens that appear within quotes. |
|---|
| | 1010 | (cond |
|---|
| | 1011 | ((eq ?\\ last) nil) |
|---|
| | 1012 | ((eq ?\` char) (setq nest (+ nest (if bq -1 1)) bq (not bq))) |
|---|
| | 1013 | ((and (> nest 0) (eq ?\) char)) (setq nest (1- nest))) |
|---|
| | 1014 | ((and (eq ?$ last) (eq ?\( char)) (setq nest (1+ nest))) |
|---|
| | 1015 | ((and (> nest 0) (eq ?\( char)) (setq nest (1+ nest))) |
|---|
| | 1016 | ((eq char ?\") |
|---|
| | 1017 | (if (>= 0 nest) (setq continue nil) (push pos seen)))) |
|---|
| | 1018 | ;;(message "POS: %d [%d]" pos nest) |
|---|
| | 1019 | (setq last char |
|---|
| | 1020 | pos (1+ pos) |
|---|
| | 1021 | char (char-after pos)) ) |
|---|
| | 1022 | ;; FIXME: why construct a costly match data to pass to |
|---|
| | 1023 | ;; sh-apply-quoted-subshell rather than apply the highlight |
|---|
| | 1024 | ;; directly here? -- Stef |
|---|
| | 1025 | (when seen |
|---|
| | 1026 | ;;(message "SEEN: %S" seen) |
|---|
| | 1027 | (setq data (list (current-buffer))) |
|---|
| | 1028 | (dolist(P seen) |
|---|
| | 1029 | (setq data (cons P (cons (1+ P) data)))) |
|---|
| | 1030 | (store-match-data data)) |
|---|
| | 1031 | data) )) |
|---|