summaryrefslogtreecommitdiff
path: root/ipl/progs/post.icn
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2013-01-27 23:51:56 +0000
committerIgor Pashev <pashev.igor@gmail.com>2013-01-27 23:51:56 +0000
commit6ab0c0f5bf14ed9c15370407b9ee7e0b4b089ae1 (patch)
tree926065cf45450116098db664e3c61dced9e1f21a /ipl/progs/post.icn
downloadicon-6ab0c0f5bf14ed9c15370407b9ee7e0b4b089ae1.tar.gz
Initial upstream version 9.4.3upstream/9.4.3
Diffstat (limited to 'ipl/progs/post.icn')
-rw-r--r--ipl/progs/post.icn366
1 files changed, 366 insertions, 0 deletions
diff --git a/ipl/progs/post.icn b/ipl/progs/post.icn
new file mode 100644
index 0000000..bc6ffd4
--- /dev/null
+++ b/ipl/progs/post.icn
@@ -0,0 +1,366 @@
+############################################################################
+#
+# File: post.icn
+#
+# Subject: Program to post news
+#
+# Author: Ronald Florence
+#
+# Date: August 14, 1996
+#
+############################################################################
+#
+# This file is in the public domain.
+#
+############################################################################
+#
+# Version: 1.5
+#
+############################################################################
+#
+# This program posts a news article to Usenet. Given an optional
+# argument of the name of a file containing a news article, or an
+# argument of "-" and a news article via stdin, post creates a
+# follow-up article, with an attribution and quoted text. The
+# newsgroups, subject, distribution, follow-up, and quote-prefix can
+# optionally be specified on the command line.
+#
+# usage: post [options] [article | -]
+# -n newsgroups
+# -s subject
+# -d distribution
+# -f followup-to
+# -p quote-prefix (default ` > ')
+#
+# See the site & system configuration options below. On systems
+# posting via inews, post validates newsgroups and distributions in
+# the `active' and `distributions' files in the news library directory.
+#
+############################################################################
+#
+# Bugs: Newsgroup validation assumes the `active' file is sorted.
+# Non-UNIX sites need hardcoded system information.
+#
+############################################################################
+#
+# Links: options
+#
+############################################################################
+
+link options
+
+global mode, sysname, domain, tz, tmpfile, opts, console, newslib, org
+
+procedure main(arg)
+ local usage, smarthost, editor, default_distribution, generic_from
+ local tmpdir, logname, fullname, sigfile, article, inf, edstr, outf, tmp2
+
+ usage := ["usage: post [options] [article]",
+ "\t-n newsgroups",
+ "\t-s subject",
+ "\t-d distribution",
+ "\t-f followup-to",
+ "\t-p quote-prefix (default ` > ')",
+ "\t- read article from stdin"]
+
+ # Site configuration. Mode can be
+ # "local" (post via inews),
+ # "uux" (post via rnews to an upstream host),
+ # "mail" (post via mail to an upstream host).
+ # For either uux or mail mode,
+ # smarthost := the uucp nodename of the upstream news feed.
+ # Use generic_from to force a generic address instead
+ # of the hostname provided by system commands.
+
+ mode := "local"
+ smarthost := ""
+ editor := "vi"
+ domain := ".UUCP"
+ default_distribution := "world"
+ generic_from := &null
+
+ # For UNIX, the rest of the configuration is automatic.
+
+ if find("UNIX", &features) then {
+ console := "/dev/tty"
+ newslib := "/usr/lib/news/"
+ tz := "unix"
+ tmpdir := "/tmp/"
+ logname := pipe("logname")
+ sysname := trim(pipe("hostname", "uname -n", "uuname -l"))
+ # BSD passwd: `:fullname[,...]:'
+ # SysV passwd: `-fullname('
+ \logname & every lookup("/etc/passwd") ? {
+ =(logname) & {
+ every tab(upto(':')+1) \4
+ fullname := (tab(upto('-')+1), tab(upto('(:'))) | tab(upto(',:'))
+ break
+ }
+ }
+ sigfile := getenv("HOME") || "/.signature"
+ }
+
+ # For non-UNIX systems, we need hard coded configuration:
+ # console := the system's name for the user's terminal.
+ # libdir := the directory for news configuration files, like
+ # an `organization' file.
+ # tmpdir := optional directory for temporary files; terminated
+ # with the appropriate path separator: `/' or `\\'.
+ # logname := user's login name.
+ # tz := local time zone (e.g., EST).
+ # fullname := user's full name.
+ # sigfile := full path of file with user's email signature.
+
+ else {
+ console := "CON"
+ newslib := ""
+ tmpdir := ""
+ logname := &null
+ tz := &null
+ fullname := &null
+ sigfile := &null
+ sysname := getenv("HOST") | &host
+ }
+
+ # End of user configuration.
+
+ (\logname & \sysname & \tz & (mode == "local" | *smarthost > 0)) |
+ stop("post: missing system information")
+ opts := options(arg, "n:s:d:f:p:h?")
+ \opts["h"] | \opts["?"] | arg[1] == "?" & {
+ every write(!usage)
+ exit(-1)
+ }
+ org := getenv("ORGANIZATION") | lookup(newslib || "organization")
+ article := open(tmpfile := tempname(tmpdir), "w") |
+ stop("post: cannot write temp file")
+ write(article, "Path: ", sysname, "!", logname)
+ writes(article, "From: ", logname, "@", \generic_from | sysname, domain)
+ \fullname & writes(article, " (", fullname, ")")
+ write(article)
+
+ # For a follow-up article, reply_headers() does the work.
+
+ if \arg[1] then {
+ inf := (arg[1] == "-" & &input) |
+ open(arg[1]) | (remove(tmpfile) & stop("post: cannot read " || arg[1]))
+ reply_headers(inf, article)
+ every write(article, \opts["p"] | " > ", !inf)
+ close(inf)
+ }
+
+ # Query if newsgroups, subject, and distribution have
+ # not been specified on the command line.
+
+ else {
+ write(article, "Newsgroups: ",
+ validate(\opts["n"] | query("Newsgroups: "), "active"))
+ write(article, "Subject: ", \opts["s"] | query("Subject: "))
+ write(article, "Distribution: ",
+ validate(\opts["d"] | query("Distribution: ", default_distribution),
+ "distributions"))
+ every write(article, req_headers())
+ write(article, "\n")
+ }
+ close(article)
+ edstr := (getenv("EDITOR") | editor) || " " || tmpfile || " < " || console
+ system(edstr)
+ upto('nN', query("Are you sure you want to post this to Usenet y/n? ")) & {
+ if upto('yY', query("Save your draft article y/n? ")) then
+ stop("Your article is saved in ", tmpfile)
+ else {
+ remove(tmpfile)
+ stop("Posting aborted.")
+ }
+ }
+ # For inews, we supply the headers, inews supplies the .signature.
+
+ if mode == "local" then mode := newslib || "inews -h"
+ else {
+ \sigfile & {
+ article := open(tmpfile, "a")
+ write(article, "--")
+ every write(article, lookup(sigfile))
+ }
+ # To post via sendnews (mail), we prefix lines with 'N'.
+ # For rnews, don't force an immediate poll.
+
+ case mode of {
+ "mail": {
+ mode ||:= " " || smarthost || "!rnews"
+ outf := open(tmp2 := tempname(tmpdir), "w")
+ every write(outf, "N", lookup(tmpfile))
+ remove(tmpfile)
+ rename(tmp2, tmpfile)
+ }
+ "uux": mode ||:= " - -r " || smarthost || "!rnews"
+ }
+ }
+ mode ||:= " < " || tmpfile
+ (system(mode) = 0) & write("Article posted!")
+ remove(tmpfile)
+end
+
+ # To parse the original article, we use case-insensitive
+ # matches on the headers. The Reply-to and Followup-To
+ # headers usually appear later than From and Newsgroups, so
+ # they take precedence. By usenet convention, we query
+ # the user if Followup-To on the original is `poster'.
+
+procedure reply_headers(infile, art)
+ local fullname, address, quoter, date, id, subject, distribution
+ local group, refs
+
+ every !infile ? {
+ tab(match("from: " | "reply-to: ", map(&subject))) & {
+ if find("<") then {
+ fullname := (trim(tab(upto('<'))) ~== "")
+ address := (move(1), tab(find(">")))
+ }
+ else {
+ address := trim(tab(upto('(') | 0))
+ fullname := (move(1), tab(find(")")))
+ }
+ quoter := (\fullname | address)
+ }
+ tab(match("date: ", map(&subject))) & date := tab(0)
+ tab(match("message-id: ", map(&subject))) & id := tab(0)
+ tab(match("subject: ", map(&subject))) & subject := tab(0)
+ tab(match("distribution: ", map(&subject))) & distribution := tab(0)
+ tab(match("newsgroups: " | "followup-to: ", map(&subject))) &
+ group := tab(0)
+ tab(match("references: ", map(&subject))) & refs := tab(0)
+ (\quoter & *&subject = 0) & {
+ find("poster", group) & {
+ write(quoter, " has requested followups by email.")
+ upto('yY', query("Do you want to abort this posting y/n? ")) & {
+ remove(tmpfile)
+ stop("Posting aborted.")
+ }
+ group := &null
+ }
+ write(art, "Newsgroups: ", \group |
+ validate(\opts["n"] | query("Newsgroups: "), "active"))
+ write(art, "Subject: ", \opts["s"] | \subject | query("Subject: "))
+ \distribution | distribution := validate(\opts["d"], "distributions") &
+ write(art, "Distribution: ", distribution)
+ write(art, "References: ", (\refs ||:= " ") | "", id)
+ every write(art, req_headers())
+ write(art, "In-reply-to: ", quoter, "'s message of ", date)
+ write(art, "\nIn ", id, ", ", quoter, " writes:\n")
+ return
+ }
+ }
+end
+
+ # We need a unique message-id, and a date in RFC822 format.
+ # Easy with UNIX systems that support `date -u'; with the
+ # others, we leave the local timezone. The first inews site
+ # will correct it.
+
+procedure req_headers()
+ local uniq, date, month, day, time, zone, year
+
+ uniq := "<"
+ &date || &clock ? while tab(upto(&digits)) do uniq ||:= tab(many(&digits))
+ uniq ||:= "@" || sysname || domain || ">"
+ if tz == "unix" then {
+ date := pipe("date -u", "date")
+ date ? {
+ month := (tab(find(" ") + 1), tab(many(&letters)))
+ day := (tab(upto(&digits)), tab(many(&digits)))
+ time := (tab(upto(&digits++':')), tab(many(&digits++':')))
+ zone := (tab(upto(&ucase)), tab(many(&ucase)))
+ year := (tab(upto(&digits)+ 2), tab(0))
+ }
+ date := day || " " || month || " " || year || " " || time || " " || zone
+ }
+ else {
+ &dateline ? {
+ month := left((tab(find(" ")+1), tab(many(&letters))), 3) || " "
+ date := (tab(upto(&digits)), tab(many(&digits))) || " " || month
+ date ||:= (tab(upto(&digits)), right(tab(many(&digits)), 2))
+ }
+ date ||:= " " || &clock || " " || tz
+ }
+ mode ~== "local" & suspend "Message-ID: " || uniq
+ suspend "Date: " || date
+ \org & suspend "Organization: " || org
+ \opts["f"] & return "Followup-To: " || ((opts["f"] == "poster") |
+ validate(opts["f"], "active"))
+end
+
+ # Richard Goerwitz's generator.
+
+procedure tempname(dir)
+ local temp_name
+
+ every temp_name := dir || "article." || right(1 to 999,3,"0") do {
+ close(open(temp_name)) & next
+ suspend \temp_name
+ }
+end
+
+ # On systems with pipes, pipe() will read from the first
+ # successful command of the list given as arguments.
+
+procedure pipe(cmd[])
+ local inf, got
+
+ initial find("pipes" | "compiled", &features) | stop("No pipes.")
+ while inf := open("(" || pop(cmd) || ") 2>&1", "pr") do {
+ got := []
+ every put(got, !inf)
+ close(inf) = 0 & {
+ suspend !got
+ break
+ }
+ }
+end
+
+ # The dirty work of reading from a file.
+
+procedure lookup(what)
+ local inf
+
+ inf := open(what, "r") | fail
+ suspend !inf
+ close(inf)
+end
+
+ # Query opens stdin because the system call to the editor
+ # redirects input. The optional parameter is a default
+ # response if the user answers with <return>.
+
+procedure query(prompt, def)
+ local ans
+ static stdin
+
+ initial stdin := open(console)
+ writes(prompt)
+ ans := read(stdin)
+ return (*ans = 0 & \def) | ans
+end
+
+ # A quick and dirty kludge. Validate() builds a sorted list.
+ # When an element is found, it is popped and the search moves
+ # to the next item. The procedure assumes the file is also
+ # sorted.
+
+procedure validate(what, where)
+ local valid, stuff, sf, a
+
+ mode ~== "local" & return what
+ valid := &letters ++ '.-' ++ &digits
+ stuff := []
+ what ? while tab(upto(valid)) do put(stuff,tab(many(valid)))
+ sf := open(newslib || where) | {
+ remove(tmpfile)
+ stop("post: cannot open ", newslib || where)
+ }
+ stuff := sort(stuff)
+ a := pop(stuff)
+ every !sf ? match(a) & (a := pop(stuff)) | return what
+ remove(tmpfile)
+ stop("`", a, "' is not in ", newslib || where)
+end