Compare commits

..

7 Commits

7 changed files with 33 additions and 29 deletions

8
README
View File

@@ -47,12 +47,12 @@ The attributes section follows the text section. The attributes section ends
with a single newline. Text including and following a hash in the attributes with a single newline. Text including and following a hash in the attributes
section will be ignoroed. section will be ignoroed.
The command section follows the attributes section. The command section is The content section follows the attributes section. The content section is
run as a single argument following the invocation `$ sh -c`, like the C run as a single argument following the invocation `$ sh -c`, like the C
standard library's system(3). Each line in the command section is preceded by standard library's system(3). Each line in the content section is preceded by
a horizontal tab, which is removed from the argument; if a line in the command a horizontal tab, which is removed from the argument; if a line in the content
section is not preceded by a tab, the omission is considered a syntax error (a section is not preceded by a tab, the omission is considered a syntax error (a
"naked line") and menu(1) exits unsuccessfully. The command section ends with "naked line") and menu(1) exits unsuccessfully. The content section ends with
two consecutive newlines. two consecutive newlines.
## POSIX systems ## POSIX systems

View File

@@ -1,7 +1,7 @@
#!usr/bin/env menu #!usr/bin/env menu
$ echo Hello, world. $ echo Hello, world.
# ^^^ Text VVV Command # ^^^ Text VVV Content
echo Hello, world. echo Hello, world.
$ cat Makefile $ cat Makefile

View File

@@ -4,13 +4,14 @@ import re, sys, xml.etree.ElementTree as ET
ET.register_namespace("", "http://www.w3.org/2005/Atom") ET.register_namespace("", "http://www.w3.org/2005/Atom")
ET.register_namespace("media", "http://search.yahoo.com/mrss/") ET.register_namespace("media", "http://search.yahoo.com/mrss/")
ET.register_namespace("yt", "http://www.youtube.com/xml/schemas/2015") ET.register_namespace("yt", "http://www.youtube.com/xml/schemas/2015")
def sortby(entry):
return entry.findall("{http://www.w3.org/2005/Atom}published")[0].text
macrofeed = ET.Element('feed') macrofeed = ET.Element('feed')
macrofeed.extend( macrofeed.extend(
sorted([ sorted([
entry entry
# Split the input by file. # Split the input by file. This relies on a typical <XML /> tag
# starting a file. Theoretically this assumption could be wrong,
# but in that case the XML parser would break before
# ytfeed.aggregate could spit malformed output.
for feed in re.split(r'<\?xml.*?\?>', sys.stdin.read())[1:] for feed in re.split(r'<\?xml.*?\?>', sys.stdin.read())[1:]
# Get the entries out of each file. # Get the entries out of each file.
for entry for entry
@@ -18,9 +19,8 @@ macrofeed.extend(
.findall("{http://www.w3.org/2005/Atom}entry") .findall("{http://www.w3.org/2005/Atom}entry")
], ],
# Gets the publication date and sorts the entries from old to new. # Gets the publication date and sorts the entries from old to new.
key = lambda entry key = lambda entry # (this is a text sort but it still works)
: entry.findall("{http://www.w3.org/2005/Atom}published")[0].text, : entry.findall("{http://www.w3.org/2005/Atom}published")[0].text,
reverse = False # toggle this to switch the order
) )
) )
print(ET.tostring(macrofeed, encoding="unicode")) print(ET.tostring(macrofeed, encoding="unicode"))

View File

@@ -20,7 +20,7 @@ for entry in root.findall("{http://www.w3.org/2005/Atom}entry"):
+ f("%-22s - ", entry, "author/name") + f("%-22s - ", entry, "author/name")
+ f("%s", entry, "title") + "\n\n" + f("%s", entry, "title") + "\n\n"
# Command # Command
+ "\t" + "ytfeed.browse-entry <<EOF | ../menu" + "\n" + "\t" + "ytfeed.browse-entry <<EOF | menu" + "\n"
+ "\t" + ET.tostring(entry, encoding="unicode") + "\t" + ET.tostring(entry, encoding="unicode")
.replace("\n", "\n\t").rstrip() + "\n" .replace("\n", "\n\t").rstrip() + "\n"
+ "\tEOF", + "\tEOF",

View File

@@ -9,8 +9,11 @@ directory = sys.argv[1]
for f in os.listdir(directory): for f in os.listdir(directory):
file_name = os.path.join(directory, f) file_name = os.path.join(directory, f)
if os.path.isfile(file_name) and f[-4:] == ".xml": if os.path.isfile(file_name) and f[-4:] == ".xml":
tree = ET.parse(file_name) try: tree = ET.parse(file_name)
root = tree.getroot() except ET.ParseError as e:
print("%s: %s: %s" % (sys.argv[0], file_name, e), file = sys.stderr)
continue
else: root = tree.getroot()
try: try:
channels += [ channels += [
"%s\n\n" % root.find('{http://www.w3.org/2005/Atom}title').text "%s\n\n" % root.find('{http://www.w3.org/2005/Atom}title').text

View File

@@ -13,9 +13,12 @@ while test -n "$2"; do
else curl=false else curl=false
fi fi
filename="$(printf '%s/%s.xml\n' "$directory" "$2")" filename="$(printf '%s/%s.xml\n' "$directory" "$2")"
if ! $curl "$xml_url_prefix""$2" >"$filename" $curl "$xml_url_prefix""$2" >"$filename".new
then rm -f "$filename" if ! grep '<title>' <"$filename".new | head -n 1 \
else printf '%s\n' "$filename" | grep 404 >/dev/null 2>&1
then mv "$filename".new "$filename" \
&& printf '%s\n' "$filename"
fi fi
rm -f "$filename".new
shift shift
done done

24
menu.rs
View File

@@ -59,7 +59,7 @@ fn print_entries(entries: &[Entry]) {
eprintln!(""); eprintln!("");
for (index, entry) in entries.iter().enumerate() { for (index, entry) in entries.iter().enumerate() {
eprint!( eprintln!(
"[{:>index_len$}]: {}", "[{:>index_len$}]: {}",
index + 1, index + 1,
entry.text, entry.text,
@@ -82,9 +82,7 @@ fn main() -> ExitCode {
} }
Ok(file) => Box::new(BufReader::new(file)), Ok(file) => Box::new(BufReader::new(file)),
} }
} else { } else { Box::new(io::stdin().lock()) };
Box::new(io::stdin().lock())
};
let user_input: Box<dyn BufRead> = if argv.len() <= 1 { let user_input: Box<dyn BufRead> = if argv.len() <= 1 {
match File::open("/dev/tty") { match File::open("/dev/tty") {
Err(e) => { Err(e) => {
@@ -92,9 +90,7 @@ fn main() -> ExitCode {
} }
Ok(file) => Box::new(BufReader::new(file)), Ok(file) => Box::new(BufReader::new(file)),
} }
} else { } else { Box::new(io::stdin().lock()) }; // ...Oops! I did it again.
Box::new(io::stdin().lock())
}; // ...Oops! I did it again.
// Parser // Parser
{ {
@@ -114,11 +110,11 @@ fn main() -> ExitCode {
} }
} }
State::Text => { State::Text => {
entry.text = line.clone() + "\n"; entry.text = line.clone();
state = State::Attributes; state = State::Attributes;
} }
State::Attributes => { State::Attributes => {
entry.attr = line.clone() + "\n"; entry.attr = line.clone();
state = State::Content; state = State::Content;
} }
State::Content => match line.chars().next() { State::Content => match line.chars().next() {
@@ -160,10 +156,11 @@ fn main() -> ExitCode {
if line != "" { if line != "" {
match line.parse::<usize>() { match line.parse::<usize>() {
Err(e) => { Err(e) => {
return error(&argv[0], &line, &e); eprintln!("{}: Could not parse selection (try 0..{})",
argv[0], entries.len());
} }
Ok(n) => { Ok(n) => {
if n != 0 { if n != 0 && n <= entries.len() {
match Command::new("sh") match Command::new("sh")
.arg("-c") .arg("-c")
.arg(entries[n - 1].cont.clone()) .arg(entries[n - 1].cont.clone())
@@ -177,8 +174,9 @@ fn main() -> ExitCode {
Ok(_) => { print_entries(&entries); } Ok(_) => { print_entries(&entries); }
}, },
} }
} } else if n > entries.len() {
if n == 0 || exit_on_selection { eprintln!("{}: Selection out of range", argv[0]);
} else if n == 0 || exit_on_selection {
return ExitCode::SUCCESS; return ExitCode::SUCCESS;
} }
} }