reorder_message_file.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. # Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
  2. #
  3. # Permission to use, copy, modify, and/or distribute this software for any
  4. # purpose with or without fee is hereby granted, provided that the above
  5. # copyright notice and this permission notice appear in all copies.
  6. #
  7. # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. # REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. # AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. # LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. # PERFORMANCE OF THIS SOFTWARE.
  14. # Reorder Message File
  15. #
  16. # Reads a message file into memory, then outputs it with the messages and
  17. # associated descriptions in alphabetical order.
  18. #
  19. # Invocation:
  20. # The code is invoked using the command line:
  21. #
  22. # python reorder.py message_file
  23. #
  24. # Output is written to stdout.
  25. import sys
  26. def remove_empty_leading_trailing(lines):
  27. """
  28. Removes leading and trailing empty lines.
  29. A list of strings is passed as argument, some of which may be empty.
  30. This function removes from the start and end of the list a contiguous
  31. sequence of empty lines and returns the result. Embedded sequences of
  32. empty lines are not touched.
  33. Parameters:
  34. lines List of strings to be modified.
  35. Return:
  36. Input list of strings with leading/trailing blank line sequences
  37. removed.
  38. """
  39. retlines = []
  40. # Dispose of degenerate case of empty array
  41. if len(lines) == 0:
  42. return retlines
  43. # Search for first non-blank line
  44. start = 0
  45. while start < len(lines):
  46. if len(lines[start]) > 0:
  47. break
  48. start = start + 1
  49. # Handle case when entire list is empty
  50. if start >= len(lines):
  51. return retlines
  52. # Search for last non-blank line
  53. finish = len(lines) - 1
  54. while finish >= 0:
  55. if len(lines[finish]) > 0:
  56. break
  57. finish = finish - 1
  58. retlines = lines[start:finish + 1]
  59. return retlines
  60. def canonicalise_message_line(line):
  61. """
  62. Given a line known to start with the '%' character (i.e. a line
  63. introducing a message), canonicalise it by ensuring that the result
  64. is of the form '%<single-space>MESSAGE_IDENTIFIER<single-space>text'.
  65. Parameters:
  66. line - input line. Known to start with a '%' and to have leading
  67. and trailing spaces removed.
  68. Return:
  69. Canonicalised line.
  70. """
  71. # Cope with degenerate case of a single "%"
  72. if len(line) == 1:
  73. return line
  74. # Get the rest of the line
  75. line = line[1:].lstrip()
  76. # Extract the first word (the message ID)
  77. words = line.split()
  78. message_line = "% " + words[0]
  79. # ... and now the rest of the line
  80. if len(line) > len(words[0]):
  81. message_line = message_line + " " + line[len(words[0]):].lstrip()
  82. return message_line
  83. def make_dict(lines):
  84. """
  85. Split the lines into segments starting with the message definition and
  86. place into a dictionary.
  87. Parameters:
  88. lines - list of lines containing the text of the message file (less the
  89. header).
  90. Returns:
  91. dictionary - map of the messages, keyed by the line that holds the message
  92. ID.
  93. """
  94. dictionary = {}
  95. message_key = canonicalise_message_line(lines[0])
  96. message_lines = [message_key]
  97. index = 1;
  98. while index < len(lines):
  99. if lines[index].startswith("%"):
  100. # Start of new message
  101. dictionary[message_key] = remove_empty_leading_trailing(message_lines)
  102. message_key = canonicalise_message_line(lines[index])
  103. message_lines = [message_key]
  104. else:
  105. message_lines.append(lines[index])
  106. index = index + 1
  107. dictionary[message_key] = remove_empty_leading_trailing(message_lines)
  108. return dictionary
  109. def print_dict(dictionary):
  110. """
  111. Prints the dictionary with a blank line between entries.
  112. Parameters:
  113. dicitionary - Map holding the message dictionary
  114. """
  115. count = 0
  116. for msgid in sorted(dictionary):
  117. # Blank line before all entries but the first
  118. if count > 0:
  119. print("")
  120. count = count + 1
  121. # ... and the entry itself.
  122. for l in dictionary[msgid]:
  123. print(l.strip())
  124. def process_file(filename):
  125. """
  126. Processes a file by reading it and searching for the first line starting
  127. with the '%' sign. Everything before that line is treated as the file
  128. header and is copied to the output with leading and trailing spaces removed.
  129. After that, each message block is read and stored for later sorting.
  130. Parameters:
  131. filename Name of the message file to process
  132. """
  133. lines = open(filename).read().splitlines()
  134. # Search for the first line starting with the percent character. Everything
  135. # before it is considered the file header and is copied to the output with
  136. # leading and trailing spaces removed.
  137. index = 0
  138. while index < len(lines):
  139. if lines[index].startswith("%"):
  140. break
  141. print(lines[index].strip())
  142. index = index + 1
  143. # Now put the remaining lines into the message dictionary
  144. dictionary = make_dict(lines[index:])
  145. # ...and print it
  146. print_dict(dictionary)
  147. # Main program
  148. if __name__ == "__main__":
  149. # Read the files and load the data
  150. if len(sys.argv) != 2:
  151. print("Usage: python reorder.py message_file")
  152. else:
  153. process_file(sys.argv[1])