1
0
mirror of https://github.com/becarpenter/book6.git synced 2024-05-07 02:54:53 +00:00

290 lines
8.6 KiB
Python
Raw Permalink Normal View History

2023-05-21 14:28:34 +12:00
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Build IPv6 RFC bibliography"""
# Version: 2023-05-23 - original
2023-07-29 17:16:36 +12:00
# Version: 2023-07-29 - check age of index
2023-07-31 15:21:51 +12:00
# Version: 2023-07-31 - added STD/BCP numbers;
# caught more RFCs; cosmetics
2023-08-01 17:29:54 +12:00
# Version: 2023-08-01 - catch by WG acronym;
# display counts
2023-08-10 14:42:12 +12:00
# Version: 2023-08-10 - download & cache xml index
# Version: 2024-04-28 - handle directory on command line;
# catch DHCPv6
2023-05-21 14:28:34 +12:00
########################################################
# Copyright (C) 2023-24 Brian E. Carpenter.
2023-05-21 14:28:34 +12:00
# All rights reserved.
#
# Redistribution and use in source and binary forms, with
# or without modification, are permitted provided that the
# following conditions are met:
#
# 1. Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of
# its contributors may be used to endorse or promote products
# derived from this software without specific prior written
# permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS
# AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
# THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
########################################################
from tkinter import Tk
from tkinter.filedialog import askdirectory
from tkinter.messagebox import askokcancel, askyesno, showinfo
import time
import os
import sys
2023-08-10 14:42:12 +12:00
import requests
2023-05-21 14:28:34 +12:00
def show(msg):
"""Show a message"""
global T, cmd_line
if cmd_line:
print(msg)
else:
showinfo(title=T, message = msg)
2023-05-21 14:28:34 +12:00
def logit(msg):
"""Add a message to the log file"""
global flog, printing
flog.write(msg+"\n")
if printing:
print(msg)
def logitw(msg):
"""Add a warning message to the log file"""
global warnings
logit("WARNING: "+msg)
warnings += 1
def dprint(*msg):
""" Diagnostic print """
global printing
if printing:
print(*msg)
def crash(msg):
"""Log and crash"""
printing = True
logit("CRASH "+msg)
flog.close()
exit()
def rf(f):
"""Return a file as a list of strings"""
file = open(f, "r",encoding='utf-8', errors='replace')
l = file.readlines()
file.close()
#ensure last line has a newline
if l[-1][-1] != "\n":
l[-1] += "\n"
return l
def wf(f,l):
"""Write list of strings to file"""
file = open(f, "w",encoding='utf-8')
for line in l:
file.write(line+"\n")
file.close()
logit("'"+f+"' written")
def field(fname, block):
"""Extract named field from XML block"""
2023-08-01 15:19:46 +12:00
if fname in block:
_,temp = block.split("<"+fname+">", maxsplit=1)
result,_ = temp.split("</"+fname+">", maxsplit=1)
return result
return ""
2023-05-21 14:28:34 +12:00
def title(block):
"""Extract title from XML block"""
return field("title", block)
def doc_id(block):
"""Extract doc-id from XML block"""
2023-08-01 15:19:46 +12:00
return field("doc-id", block)
2023-05-21 14:28:34 +12:00
def interesting(block):
"""Save interesting RFC data from XML block"""
global stds, bcps, infos, exps
if "UNKNOWN" in block:
return None
elif "<current-status>HISTORIC" in block:
return False
elif "<obsoleted-by>" in block:
return False
elif ("IPv6" in title(block) or "IP Version 6" in title(block) or "DHCPv6" in title(block)
or (field("wg_acronym", block) in wgs)):
2023-05-21 14:28:34 +12:00
#print(block)
status = field("current-status", block)
if "is-also" in block:
also,_ = field("is-also", block).split("<doc-id>")[1].split("</")
if also.startswith("BCP0"):
also = "BCP"+also[3:].lstrip("0")
elif also.startswith("STD0"):
also = "STD"+also[3:].lstrip("0")
also = " ({{{"+also+"}}})"
else:
also = ""
#print(also)
2023-05-21 14:28:34 +12:00
if "STANDARD" in status:
stds.append("- {{{"+doc_id(block)+"}}}"+also+": "+title(block))
2023-05-21 14:28:34 +12:00
elif status == "BEST CURRENT PRACTICE":
bcps.append("- {{{"+doc_id(block)+"}}}"+also+": "+title(block))
2023-05-21 14:28:34 +12:00
elif status =="INFORMATIONAL":
infos.append("- {{{"+doc_id(block)+"}}}: "+title(block))
elif status == "EXPERIMENTAL":
exps.append("- {{{"+doc_id(block)+"}}}: "+title(block))
else:
logitw("Unclassified:\n"+block)
return True
return False
######### Startup
#Define some globals
inrfc = False
new = ''
numberfound = False
count = 0
stds = []
bcps = []
infos= []
exps = []
printing = False # True for extra diagnostic prints
warnings = 0
2023-08-01 15:19:46 +12:00
wgs = ["6man","v6ops"]
2023-05-21 14:28:34 +12:00
#Has the user supplied a directory on the command line?
2023-05-21 14:28:34 +12:00
cmd_line = False
if len(sys.argv) > 1:
#user provided directory name?
if os.path.isdir(sys.argv[1]):
#assume user has provided directory
#and set all options to defaults
os.chdir(sys.argv[1])
cmd_line = True
2023-05-21 14:28:34 +12:00
#Announce
if not cmd_line:
Tk().withdraw() # we don't want a full GUI
T = "IPv6 RFC bibliography maker."
2023-05-21 14:28:34 +12:00
printing = askyesno(title=T,
message = "Diagnostic printing?")
2023-05-21 14:28:34 +12:00
os.chdir(askdirectory(title = "Select main book directory"))
2023-05-21 14:28:34 +12:00
2023-05-21 14:28:34 +12:00
#Open log file
flog = open("RFCbib6.log", "w",encoding='utf-8')
timestamp = time.strftime("%Y-%m-%d %H:%M:%S UTC%z",time.localtime())
logit("RFCbib6 run at "+timestamp)
logit("Running in directory "+ os.getcwd())
show("Will read complete RFC index.\nTouch no files until done!")
2023-08-10 14:42:12 +12:00
fp = "rfc-index.xml"
if (not os.path.exists(fp)) or (time.time()-os.path.getmtime(fp) > 60*60*24*30):
#need fresh copy of index
try:
if cmd_line or askyesno(title=T, message = "OK to download RFC index?\n(15 MB file)"):
2023-08-10 14:42:12 +12:00
response = requests.get("https://www.rfc-editor.org/rfc/rfc-index.xml")
open(fp, "wb").write(response.content)
logit("Downloaded and cached RFC index")
else:
2023-08-10 14:42:12 +12:00
raise Exception("Invalid choice")
except Exception as E:
logitw(str(E))
crash("Cannot run without RFC index")
whole = rf(fp)
2023-05-21 14:28:34 +12:00
timestamp = time.strftime("%Y-%m-%d %H:%M:%S UTC%z",time.localtime())
for line in whole:
#print(line)
if (not inrfc) and (not "<rfc-entry>" in line):
continue
inrfc = True
new += line
if inrfc and "</rfc-entry>" in line:
#end of an rfc entry
if interesting(new):
count += 1
inrfc = False
numberfound = False
new = ''
continue
logit(str(count)+" IPv6 RFCs found")
if len(stds)+len(bcps)+len(infos)+len(exps) != count:
logitw("Warning: count mismatch")
md = ["## RFC bibliography","",
2023-08-01 15:19:46 +12:00
"""This section is a machine-generated list of all current RFCs that
mention IPv6 or DHCPv6 in their title or come from the major IPv6 working groups.
2023-08-01 15:19:46 +12:00
Obsolete RFCs are not included. There are subsections for Standards, BCPs,
Informational and Experimental RFCs. Be *cautious* about old Informational
or Experimental RFCs - they may be misleading as well as out of date."""]
2023-08-01 17:29:54 +12:00
md += ["","RFCbib6 run at "+timestamp+" ("+str(count)+" RFCs found)"]
md += ["","### Standards Track ("+str(len(stds))+" RFCs)",""]
2023-05-21 14:28:34 +12:00
md += stds
2023-08-01 17:29:54 +12:00
md += ["", "### Best Current Practice ("+str(len(bcps))+" RFCs)", ""]
2023-05-21 14:28:34 +12:00
md += bcps
2023-08-01 17:29:54 +12:00
md += ["", "### Informational ("+str(len(infos))+" RFCs)", ""]
2023-05-21 14:28:34 +12:00
md += infos
2023-08-01 17:29:54 +12:00
md += ["", "### Experimental ("+str(len(exps))+" RFCs)", ""]
2023-05-21 14:28:34 +12:00
md += exps
md += ["", "<!-- Link lines generated automatically; do not delete -->",
"### [<ins>Chapter Contents</ins>](20.%20Further%20Reading.md)"]
wf("20. Further Reading/RFC bibliography.md", md)
######### Close log and exit
flog.close()
if warnings:
warn = str(warnings)+" warning(s)\n"
else:
warn = ""
show(warn+"Check RFCbib6.log, then run makeBook.")
2023-05-21 14:28:34 +12:00