Arch Linux helpers and parsers
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.

244 lines
5.7 KiB

  1. package archlinux
  2. import (
  3. "bufio"
  4. "encoding/base64"
  5. "encoding/hex"
  6. "fmt"
  7. "io"
  8. "net/url"
  9. "reflect"
  10. "strings"
  11. "time"
  12. )
  13. // ReadPackageDesc reads a package description from a desc file
  14. func ReadPackageDesc(r io.Reader) (*Package, error) {
  15. pkg := new(Package)
  16. if n, ok := r.(namer); ok {
  17. pkg.Filename = n.Name()
  18. }
  19. if err := pkg.readPackageDesc(r); err != nil {
  20. return nil, err
  21. }
  22. if pkg.Name == "" {
  23. return nil, ErrNoPackageName
  24. }
  25. if pkg.Version == "" {
  26. return nil, ErrNoPackageVersion
  27. }
  28. if pkg.Release == "" {
  29. return nil, ErrNoPackageRelease
  30. }
  31. return pkg, nil
  32. }
  33. func (pkg *Package) readPackageDesc(r io.Reader) (err error) {
  34. var (
  35. s = bufio.NewScanner(r)
  36. line string
  37. lines []string
  38. section string
  39. )
  40. values, encodings := parsePackageFields(parseFieldDesc, pkg)
  41. for s.Scan() {
  42. if err = s.Err(); err != nil {
  43. if err == io.EOF {
  44. err = nil
  45. }
  46. break
  47. }
  48. line = s.Text()
  49. if len(line) == 0 {
  50. v, ok := values[section]
  51. if !ok {
  52. return fmt.Errorf("archlinux: unsupported section %q", section)
  53. }
  54. switch v.Interface().(type) {
  55. case []string:
  56. for _, line = range lines {
  57. if err = setPackageValue(pkg, v, line, encodings[section]); err != nil {
  58. return
  59. }
  60. }
  61. default:
  62. if err = setPackageValue(pkg, v, strings.Join(lines, "\n"), encodings[section]); err != nil {
  63. return
  64. }
  65. }
  66. /*
  67. switch v.Interface().(type) {
  68. case string:
  69. switch encodings[section] {
  70. case "":
  71. v.SetString(lines[0])
  72. case evrEncoding:
  73. p := version.Parse(lines[0])
  74. pkg.Version = p.Version
  75. pkg.Release = p.Release
  76. if p.Epoch != "" {
  77. if pkg.Epoch, err = strconv.Atoi(p.Epoch); err != nil {
  78. return fmt.Errorf("archlinux: invalid epoch %q: %v", p.Epoch, err)
  79. }
  80. }
  81. }
  82. case []string:
  83. c := make([]string, len(lines))
  84. copy(c, lines)
  85. v.Set(reflect.ValueOf(c))
  86. case []byte:
  87. switch encodings[section] {
  88. case "":
  89. v.SetBytes([]byte(strings.Join(lines, "\n")))
  90. case base64Encoding:
  91. var b []byte
  92. if b, err = base64.StdEncoding.DecodeString(lines[0]); err != nil {
  93. return fmt.Errorf("archlinux: error parsing section %q: %v", section, err)
  94. }
  95. v.SetBytes(b)
  96. case hexEncoding:
  97. var b []byte
  98. if b, err = hex.DecodeString(lines[0]); err != nil {
  99. return fmt.Errorf("archlinux: error parsing section %q: %v", section, err)
  100. }
  101. v.SetBytes(b)
  102. }
  103. case time.Time:
  104. var i int64
  105. if i, err = strconv.ParseInt(lines[0], 10, 64); err != nil {
  106. return fmt.Errorf("archlinux: error parsing section %q: %v", section, err)
  107. }
  108. v.Set(reflect.ValueOf(time.Unix(i, 0)))
  109. case *url.URL:
  110. var u *url.URL
  111. if u, err = url.Parse(lines[0]); err != nil {
  112. return fmt.Errorf("archlinux: error parsing section %q: %v", section, err)
  113. }
  114. v.Set(reflect.ValueOf(u))
  115. }
  116. */
  117. } else if line[0] == '%' && line[len(line)-1] == '%' {
  118. section = line[1 : len(line)-1]
  119. lines = lines[:0]
  120. } else {
  121. lines = append(lines, line)
  122. }
  123. }
  124. return
  125. }
  126. func parseFieldDesc(field reflect.StructField) (name, encoding string) {
  127. tag := field.Tag.Get("pkgdesc")
  128. switch tag {
  129. case "-":
  130. // Ignored tag
  131. case "":
  132. // No desc tag, default to upper case field name
  133. name = strings.ToUpper(field.Name)
  134. default:
  135. if i := strings.IndexByte(tag, ','); i != -1 {
  136. name, encoding = strings.ToUpper(tag[:i]), tag[i+1:]
  137. } else {
  138. name = strings.ToUpper(tag)
  139. }
  140. }
  141. return
  142. }
  143. var timeZero time.Time
  144. // WritePackageDesc writes a package desc file to the target Writer.
  145. func (pkg *Package) WritePackageDesc(w io.Writer) (int, error) {
  146. var (
  147. value = reflect.Indirect(reflect.ValueOf(pkg))
  148. typ = value.Type()
  149. total, n int
  150. err error
  151. )
  152. for i := 0; i < value.NumField(); i++ {
  153. fieldValue := value.Field(i)
  154. fieldType := typ.Field(i)
  155. name, encoding := parseFieldDesc(fieldType)
  156. if name == "" {
  157. // No name means ignored field
  158. continue
  159. }
  160. switch v := fieldValue.Interface().(type) {
  161. case string:
  162. if v == "" {
  163. continue
  164. }
  165. switch encoding {
  166. case "":
  167. // No additional encoding
  168. case evrEncoding:
  169. // Epoch:Version-Release
  170. if pkg.Epoch > 0 {
  171. v = fmt.Sprintf("%d:%s-%s", pkg.Epoch, pkg.Version, pkg.Release)
  172. } else {
  173. v = fmt.Sprintf("%s-%s", pkg.Version, pkg.Release)
  174. }
  175. }
  176. if n, err = fmt.Fprintf(w, "%%%s%%\n%s\n\n", name, v); err != nil {
  177. return total + n, err
  178. }
  179. total += n
  180. case int64:
  181. if v == 0 {
  182. continue
  183. }
  184. if n, err = fmt.Fprintf(w, "%%%s%%\n%d\n\n", name, v); err != nil {
  185. return total + n, err
  186. }
  187. total += n
  188. case []string:
  189. if len(v) == 0 {
  190. continue
  191. }
  192. if n, err = fmt.Fprintf(w, "%%%s%%\n%s\n\n", name, strings.Join(v, "\n")); err != nil {
  193. return total + n, err
  194. }
  195. total += n
  196. case []byte:
  197. if len(v) == 0 {
  198. continue
  199. }
  200. var o []byte
  201. switch encoding {
  202. case "":
  203. o = v
  204. case base64Encoding:
  205. o = make([]byte, base64.StdEncoding.EncodedLen(len(v)))
  206. base64.StdEncoding.Encode(o, v)
  207. case hexEncoding:
  208. o = make([]byte, hex.EncodedLen(len(v)))
  209. hex.Encode(o, v)
  210. }
  211. if n, err = fmt.Fprintf(w, "%%%s%%\n%s\n\n", name, o); err != nil {
  212. return total + n, err
  213. }
  214. total += n
  215. case time.Time:
  216. if v.Equal(timeZero) {
  217. continue
  218. }
  219. if n, err = fmt.Fprintf(w, "%%%s%%\n%d\n\n", name, v.Unix()); err != nil {
  220. return total + n, err
  221. }
  222. total += n
  223. case *url.URL:
  224. if v == nil {
  225. continue
  226. }
  227. if n, err = fmt.Fprintf(w, "%%%s%%\n%s\n\n", name, v); err != nil {
  228. return total + n, err
  229. }
  230. total += n
  231. default:
  232. return total, fmt.Errorf("archlinux: can't write %T to desc", v)
  233. }
  234. }
  235. return total, nil
  236. }