Debian package metadata parser
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

field.go 6.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. package dpkg
  2. import (
  3. "errors"
  4. "fmt"
  5. "strings"
  6. "git.maze.io/maze/go-dpkg/text"
  7. "git.maze.io/maze/go-dpkg/version"
  8. )
  9. var (
  10. nameAlsoAllowed = "-+._" // _ is deprecated
  11. priorityInfos = map[string]int{
  12. "required": PriorityRequired,
  13. "important": PriorityImportant,
  14. "standard": PriorityStandard,
  15. "optional": PriorityOptional,
  16. "extra": PriorityExtra,
  17. "unknown": PriorityUnknown,
  18. "this is a bug - please report": PriorityOther,
  19. }
  20. priorityMap = map[int]string{}
  21. statusInfos = map[string]int{
  22. "not-installed": StatusNotInstalled,
  23. "config-files": StatusConfigFiles,
  24. "half-installed": StatusHalfInstalled,
  25. "unpacked": StatusUnpacked,
  26. "half-configured": StatusHalfConfigured,
  27. "triggers-awaited": StatusTriggersAwaited,
  28. "triggers-pending": StatusTriggersPending,
  29. "installed": StatusInstalled,
  30. }
  31. wantInfos = map[string]int{
  32. "unknown": WantUnknown,
  33. "install": WantInstall,
  34. "hold": WantHold,
  35. "deinstall": WantDeinstall,
  36. "purge": WantPurge,
  37. }
  38. )
  39. type Field interface {
  40. Check() error
  41. Parse(string) error
  42. String() string
  43. }
  44. type Name string
  45. func (n Name) Check() error {
  46. if n == "" {
  47. return errors.New(`may not be empty string`)
  48. }
  49. if !text.IsAlnum(n[0]) {
  50. return errors.New(`must start with an alphanumeric`)
  51. }
  52. for i := 0; i < len(n); i++ {
  53. c := n[i]
  54. if !text.IsAlnum(c) && strings.IndexByte(nameAlsoAllowed, c) == -1 {
  55. return fmt.Errorf(`character '%c' is not allowed (only letters, digits and characters %q`, c, nameAlsoAllowed)
  56. }
  57. }
  58. return nil
  59. }
  60. func (n *Name) Parse(s string) error {
  61. *n = Name(s)
  62. return nil
  63. }
  64. func (n Name) String() string {
  65. return string(n)
  66. }
  67. type Version version.Version
  68. func (f Version) Check() error {
  69. return nil
  70. }
  71. func (f *Version) Parse(s string) error {
  72. v, err := version.New(s)
  73. if err == nil {
  74. *f = Version(v)
  75. }
  76. return err
  77. }
  78. func (f Version) String() string {
  79. return version.Version(f).String()
  80. }
  81. type Status struct {
  82. Want string
  83. Flag string
  84. Status string
  85. }
  86. func (f *Status) Check() error {
  87. if f.Status == "" {
  88. return errors.New(`dpkg: third (status) word in status field`)
  89. }
  90. if _, ok := statusInfos[f.Status]; !ok {
  91. return fmt.Errorf(`dpkg: invalid status %q`, f.Status)
  92. }
  93. if _, ok := wantInfos[f.Want]; !ok {
  94. return fmt.Errorf(`dpkg: invalid want %q`, f.Want)
  95. }
  96. return nil
  97. }
  98. func (f *Status) Parse(s string) error {
  99. var part = strings.SplitN(s, " ", 3)
  100. if len(part) != 3 {
  101. return errors.New(`dpkg: error in status field`)
  102. }
  103. f.Want = part[0]
  104. f.Flag = part[1]
  105. f.Status = part[2]
  106. return nil
  107. }
  108. func (f *Status) String() string {
  109. return fmt.Sprintf("%s %s %s", f.Want, f.Flag, f.Status)
  110. }
  111. type Priority int
  112. func (f *Priority) Check() error { return nil }
  113. func (f *Priority) Parse(s string) error {
  114. var (
  115. prio int
  116. ok bool
  117. )
  118. if prio, ok = priorityInfos[s]; !ok {
  119. return fmt.Errorf(`dpkg: unknown word %q in priority field`, s)
  120. }
  121. *f = Priority(prio)
  122. return nil
  123. }
  124. func (f Priority) String() string {
  125. return priorityMap[int(f)]
  126. }
  127. type PackageInfo struct {
  128. Name string
  129. Want int
  130. Status int
  131. Priority int
  132. Section string
  133. Version string
  134. Revision string
  135. }
  136. type Dependancy struct {
  137. PackageInfo
  138. Relation int
  139. Version version.Version
  140. Type int
  141. }
  142. func (dep Dependancy) String() string {
  143. var rel = "any"
  144. switch dep.Relation {
  145. case 0: // No relation
  146. return dep.Name
  147. case None:
  148. case EarlierEqual:
  149. rel = "<="
  150. case LaterEqual:
  151. rel = ">="
  152. case EarlierStrict:
  153. rel = "<"
  154. case LaterStrict:
  155. rel = ">"
  156. case Exact:
  157. rel = "="
  158. default:
  159. rel = fmt.Sprintf("? (%d)", dep.Relation)
  160. }
  161. return fmt.Sprintf("%s %s %s", dep.Name, rel, dep.Version.String())
  162. }
  163. type Dependancies []Dependancy
  164. func (f Dependancies) Check() error { return nil }
  165. func (f *Dependancies) Parse(s string) (err error) {
  166. var parts = strings.Split(s, ",")
  167. for _, part := range parts {
  168. part = strings.TrimSpace(part)
  169. if part == "" {
  170. continue
  171. }
  172. var dep = Dependancy{
  173. PackageInfo: PackageInfo{
  174. Name: part,
  175. },
  176. }
  177. if i := strings.IndexByte(dep.Name, ' '); i > -1 {
  178. var rel string
  179. dep.Name, rel = dep.Name[:i], dep.Name[i+1:]
  180. rel = strings.TrimSpace(rel)
  181. if rel != "" && rel[0] == '(' {
  182. var c1 = rel[1]
  183. if c1 == '<' || c1 == '>' {
  184. if c1 == '<' {
  185. dep.Relation = Earlier
  186. } else {
  187. dep.Relation = Later
  188. }
  189. var c2 = rel[2]
  190. if c2 == '=' {
  191. dep.Relation |= OrEqual | BuiltUp
  192. } else if c2 == c1 {
  193. dep.Relation |= Strict | BuiltUp
  194. } else if c2 == '<' || c2 == '>' {
  195. dep.Relation = None
  196. return fmt.Errorf(`dpkg: bad relationship %c%c`, c1, c2)
  197. } else {
  198. dep.Relation |= OrEqual | BuiltUp
  199. }
  200. } else if c1 == '=' {
  201. dep.Relation = Exact
  202. } else {
  203. dep.Relation = Exact
  204. }
  205. rel = rel[3:]
  206. }
  207. if rel[0] != '|' {
  208. // TODO(maze): support alternatives
  209. var ver = rel
  210. if i = strings.IndexByte(ver, ')'); i != -1 {
  211. ver = ver[:i]
  212. }
  213. if dep.Version, err = version.New(ver); err != nil {
  214. return fmt.Errorf("%v: %q", err, ver)
  215. }
  216. }
  217. }
  218. *f = append(*f, dep)
  219. }
  220. return nil
  221. }
  222. func (f Dependancies) String() string {
  223. var parts []string
  224. for _, dep := range f {
  225. parts = append(parts, dep.String())
  226. }
  227. return strings.Join(parts, ", ")
  228. }
  229. type Conffile struct {
  230. Name string
  231. Hash string
  232. }
  233. type Conffiles []Conffile
  234. func (f Conffiles) Check() error { return nil }
  235. func (f *Conffiles) Parse(s string) (err error) {
  236. var lines = strings.Split(s, "\n")
  237. for _, line := range lines {
  238. if line == "" {
  239. continue
  240. }
  241. if line[0] != ' ' {
  242. return fmt.Errorf(`dpkg: value for "conffiles" has line starting with non-space "%c"`, line[0])
  243. }
  244. line = strings.TrimLeft(line, " \t")
  245. var part = strings.SplitN(line, " ", 2)
  246. if len(part) != 2 {
  247. return fmt.Errorf(`dpkg: invalid conffile line %q`, line)
  248. }
  249. *f = append(*f, Conffile{Name: part[0], Hash: part[1]})
  250. }
  251. return
  252. }
  253. func (f Conffiles) String() string {
  254. return "" // TODO
  255. }
  256. // Interface checks
  257. var (
  258. _ Field = (*Name)(nil)
  259. _ Field = (*Status)(nil)
  260. _ Field = (*Dependancies)(nil)
  261. )
  262. func init() {
  263. for s, i := range priorityInfos {
  264. priorityMap[i] = s
  265. }
  266. }