check-pcp-style.sh 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. #!/bin/bash
  2. set -eu -o pipefail
  3. PCP_DIR="$(dirname "$0")/pcp"
  4. if [ ! -d "${PCP_DIR}" ]; then
  5. echo Could not find PCP platform sources. >&2
  6. exit 1
  7. fi
  8. echo Scanning definitions in ${PCP_DIR} ...
  9. PCP_COLUMNS="${PCP_DIR}/columns"
  10. PCP_METERS="${PCP_DIR}/meters"
  11. PCP_SCREENS="${PCP_DIR}/screens"
  12. if [ ! -d "${PCP_COLUMNS}" ]; then
  13. echo Could not find PCP column definitions. >&2
  14. exit 1
  15. fi
  16. if [ ! -d "${PCP_METERS}" ]; then
  17. echo Could not find PCP meter definitions. >&2
  18. exit 1
  19. fi
  20. if [ ! -d "${PCP_SCREENS}" ]; then
  21. echo Could not find PCP screen definitions. >&2
  22. exit 1
  23. fi
  24. check_file() {
  25. f="$1"
  26. r="$2"
  27. echo "Processing $f ..."
  28. awk -v required_names_str="$2" '
  29. BEGIN {
  30. # Define the required names
  31. split(required_names_str, required_names)
  32. section = ""
  33. }
  34. # Skip comment lines
  35. /^#/ {
  36. next
  37. }
  38. # Detect section headers
  39. /^\[.*\]$/ {
  40. if (section != "") {
  41. check_section()
  42. }
  43. section = $0
  44. if (section in sections_seen) {
  45. print "Error: Duplicate section " section
  46. exit 1
  47. }
  48. sections_seen[section] = 1
  49. delete seen
  50. delete groups
  51. next
  52. }
  53. # Process name = value pairs with whitespace around the equals sign
  54. /^[^=]+ = [^=]+$/ {
  55. split($0, pair, " = ")
  56. name = trim(pair[1])
  57. value = trim(pair[2])
  58. group = ""
  59. known = 0
  60. for (i in required_names) {
  61. rname = required_names[i]
  62. if (rname ~ /\?$/) {
  63. rname = substr(rname, 1, length(rname) - 1)
  64. }
  65. if (rname ~ /^\*\./) {
  66. rlname = substr(rname, 2, length(rname) - 1)
  67. if (substr(name, length(name) - length(rlname) + 1) == rlname) {
  68. group = substr(name, 1, length(name) - length(rlname) + 1)
  69. known = 1
  70. break
  71. }
  72. }
  73. if (rname == name) {
  74. known = 1
  75. break
  76. }
  77. }
  78. if (!known) {
  79. print "Error: Unknown name " name " in section " section
  80. exit 1
  81. }
  82. if (name in seen) {
  83. print "Error: Duplicate name " name " in section " section
  84. exit 1
  85. }
  86. seen[name] = 1
  87. groups[group] = 1
  88. if (name ~ /(^|\.)width$/) {
  89. if (!(value ~ /^-?[0-9]{1,3}$/)) {
  90. print "Error: Specified width " value " for " name " in section " section " is not an integer or out of range (-999..999)."
  91. exit 1
  92. }
  93. }
  94. if (name ~ /(^|\.)type$/) {
  95. if (!(value ~ /^(bar|text|graph|led)$/)) {
  96. print "Error: Specified type " value " for " name " in section " section " is not recognized."
  97. exit 1
  98. }
  99. }
  100. if (name ~ /(^|\.)color$/) {
  101. if (!(value ~ /^(black|blue|green|red|cyan|magenta|yellow|(dark)?gray|white)$/)) {
  102. print "Error: Specified color " value " for " name " in section " section " is not recognized."
  103. exit 1
  104. }
  105. }
  106. next
  107. }
  108. # Function to trim whitespace
  109. function trim(s) {
  110. gsub(/^[ \t]+|[ \t]+$/, "", s)
  111. return s
  112. }
  113. # Function to check if all required names are present
  114. function check_section() {
  115. missing = ""
  116. for (i in required_names) {
  117. rname = required_names[i]
  118. if (rname ~ /\?$/) {
  119. continue
  120. }
  121. if (rname ~ /^\*\./) {
  122. rname = substr(rname, 3, length(rname) - 2)
  123. for (g in groups) {
  124. if (g == "") {
  125. continue
  126. }
  127. if (!(g rname in seen)) {
  128. missing = missing g rname " "
  129. }
  130. }
  131. continue
  132. }
  133. if (!(rname in seen)) {
  134. missing = missing rname " "
  135. }
  136. }
  137. if (missing != "") {
  138. print "Error: Missing " missing "in section " section
  139. exit 1
  140. }
  141. }
  142. # End of file processing
  143. END {
  144. if (section != "") {
  145. check_section()
  146. }
  147. # Ensure the file ends with a single newline
  148. if (NR > 0 && length($0) < 1) {
  149. print "Error: Whitespace at EOF."
  150. exit 1
  151. }
  152. }
  153. ' "$f"
  154. }
  155. error_occurred=0
  156. for pcp_file in "${PCP_COLUMNS}"/*; do
  157. if ! check_file "$pcp_file" "heading caption? width metric description"; then
  158. echo "Error processing file: $pcp_file" >&2
  159. error_occurred=1
  160. fi
  161. done
  162. for pcp_file in "${PCP_METERS}"/*; do
  163. if ! check_file "$pcp_file" "caption description? type? *.metric *.color? *.label? *.suffix?"; then
  164. echo "Error processing file: $pcp_file" >&2
  165. error_occurred=1
  166. fi
  167. done
  168. for pcp_file in "${PCP_SCREENS}"/*; do
  169. if ! check_file "$pcp_file" "heading caption default? *.heading *.metric *.default? *.caption? *.format? *.instances? *.width?"; then
  170. echo "Error processing file: $pcp_file" >&2
  171. error_occurred=1
  172. fi
  173. done
  174. if [ $error_occurred -ne 0 ]; then
  175. echo "One or more files failed to process." >&2
  176. exit 1
  177. else
  178. echo "All files processed successfully." >&2
  179. fi