_unknown.scm 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. ;; _unknown.scm -- An example for an `unknown' service.
  2. ;; Copyright (C) 2003 Wolfgang Jährling <wolfgang@pro-linux.de>
  3. ;;
  4. ;; This file is part of the GNU Shepherd.
  5. ;;
  6. ;; The GNU Shepherd is free software; you can redistribute it and/or modify it
  7. ;; under the terms of the GNU General Public License as published by
  8. ;; the Free Software Foundation; either version 3 of the License, or (at
  9. ;; your option) any later version.
  10. ;;
  11. ;; The GNU Shepherd is distributed in the hope that it will be useful, but
  12. ;; WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;; GNU General Public License for more details.
  15. ;;
  16. ;; You should have received a copy of the GNU General Public License
  17. ;; along with the GNU Shepherd. If not, see <http://www.gnu.org/licenses/>.
  18. ;; Return true if STR1 lacks a character that exists in STR2, but
  19. ;; otherwise both are identical.
  20. (define (lacks-char-from? str1 str2)
  21. (and (= (string-length str1)
  22. (+ (string-length str2) 1))
  23. (letrec ((next
  24. (lambda (pos)
  25. (and (not (= pos (string-length str1)))
  26. (or (string=? str2
  27. (string-append
  28. (substring str1 0 pos)
  29. (substring str1
  30. (+ pos 1)
  31. (string-length str1))))
  32. (next (+ pos 1)))))))
  33. (next 0))))
  34. ;; Return true if either of STR1 and STR2 lacks a character found in
  35. ;; the other one, but otherwise both are identical (e.g. as is the
  36. ;; case for "blah" and "bla").
  37. (define (differs-by-missing-char? str1 str2)
  38. (or (lacks-char-from? str1 str2)
  39. (lacks-char-from? str2 str1)))
  40. ;; Return true if the only difference between STR1 and STR2 is that a
  41. ;; successive pair of characters is switched in one of them.
  42. (define (differs-by-switched-chars? str1 str2)
  43. (and (= (string-length str1)
  44. (string-length str2))
  45. (> (string-length str1) 1)
  46. (letrec ((next
  47. (lambda (pos)
  48. (and (not (= pos (string-length str1)))
  49. (or (string=? str2
  50. (string-append
  51. (substring str1 0 (- pos 1))
  52. (string (string-ref str1 pos)
  53. (string-ref str1 (- pos 1)))
  54. (substring str1
  55. (+ pos 1)
  56. (string-length str1))))
  57. (next (+ pos 1)))))))
  58. (next 1))))
  59. ;; Return true if they differ by exactly one character (e.g. as is the
  60. ;; case for "blah" and "bleh"), if it isn't the only one.
  61. (define (differs-by-one-char? str1 str2)
  62. (and (= (string-length str1)
  63. (string-length str2))
  64. (> (string-length str1) 1)
  65. (letrec ((next
  66. (lambda (pos found-difference)
  67. (if (= pos (string-length str1))
  68. found-difference
  69. (if (char=? (string-ref str1 pos)
  70. (string-ref str2 pos))
  71. (next (+ pos 1) found-difference)
  72. (and (not found-difference)
  73. (next (+ pos 1) #t)))))))
  74. (next 0 #f))))
  75. ;; Return true if STR1 and STR2 are identical, except for case
  76. ;; (e.g. this gives true for "foobar" and "FooBAR").
  77. (define (differs-only-in-case? str1 str2)
  78. (and (not (string=? str1 str2))
  79. (string-ci=? str1 str2)))
  80. ;; Return true if STR1 and STR2 are `similar' strings, meaning that
  81. ;; they only differ in a minor way.
  82. (define (similar? str1 str2)
  83. (any (lambda (pred?)
  84. (pred? str1 str2))
  85. (list differs-by-missing-char?
  86. differs-by-switched-chars?
  87. differs-by-one-char?
  88. differs-only-in-case?)))
  89. ;; TODO
  90. ;; - We could look for non-running services first on `start' etc.
  91. ;; - We also should do `unknown-action' (if service is known)
  92. ;; - If doing this, we should enable the service to handle it
  93. ;; - Make this the `default unknown service'
  94. ;; - Messages if nothing found.
  95. ;; Suggest a service that satisfies PRED?, if given, and has a name
  96. ;; similar to SERVICE-SYMBOL.
  97. (define look-for-service
  98. (case-lambda
  99. ((service-symbol) (look-for-service service-symbol (lambda (x) #t)))
  100. ((service-symbol pred?)
  101. (call/ec
  102. (lambda (return)
  103. (for-each-service
  104. (lambda (s)
  105. (and (pred? s)
  106. (similar? (symbol->string service-symbol)
  107. (symbol->string (canonical-name s)))
  108. (begin
  109. (format #t "Did you mean ~a maybe?" (canonical-name s))
  110. (newline)
  111. (return #t)))))
  112. #f)))))
  113. ;; The classical compose.
  114. (define (compose f g)
  115. (lambda (x)
  116. (f (g x)))
  117. (define unknown-service
  118. (make <service>
  119. #:provides '(unknown)
  120. #:actions (make-actions
  121. (start
  122. "Called if user wants to start an unknown service."
  123. (lambda (running service-sym . args)
  124. (or (look-for-service service-sym (compose not running?))
  125. (look-for-service service-sym))
  126. running))
  127. (stop
  128. "Called if user wants to stop an unknown service."
  129. (lambda (running service-sym . args)
  130. (or (look-for-service service-sym running?)
  131. (look-for-service service-sym))
  132. running))
  133. (action
  134. "Called if user frobs an unknown service."
  135. (lambda (running service-sym the-action . args)
  136. (or (look-for-service service-sym running?)
  137. (look-for-service service-sym))
  138. running)))))
  139. (register-services unknown-service)
  140. (start unknown-service)