diff --git a/annogen.py b/annogen.py
index f408c0139be60976fe05c8fe6b45b768c48b7ff0..d9831b4a5a84ba667991c6247deddfa3eb097f67 100755
--- a/annogen.py
+++ b/annogen.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 
-program_name = "Annotator Generator v0.623 (c) 2012-17 Silas S. Brown"
+program_name = "Annotator Generator v0.624 (c) 2012-17 Silas S. Brown"
 
 # See http://people.ds.cam.ac.uk/ssb22/adjuster/annogen.html
 
@@ -846,9 +846,11 @@ enum {
 if data_driven and not ndk: c_preamble += '#include <stdlib.h>\n' # for malloc (ndk includes it anyway, above)
 if zlib: c_preamble += '#include "zlib.h"\n'
 
-if ndk: c_start = ""
-# line below: just say 'code generated by', not 'C code' as it might also be Objective-C (if ios is set; TODO: check and say which one?)
-else: c_start = "/* -*- coding: "+outcode+" -*- */\n/* code generated by "+program_name[:program_name.index("(c)")].strip()+" */\n"
+version_stamp = time.strftime("generated %Y-%m-%d by ")+program_name[:program_name.index("(c)")].strip()
+
+if ios: c_name = "Objective-C"
+else: c_name = "C"
+c_start = "/* -*- coding: "+outcode+" -*- */\n/* "+c_name+" code "+version_stamp+" */\n"
 c_start += c_preamble+r"""
 enum { ybytes = %%YBYTES%% }; /* for Yarowsky matching, minimum readahead */
 static int nearbytes = ybytes;
@@ -1390,20 +1392,21 @@ public String result() {
 
 if os.environ.get("ANNOGEN_CSHARP_NO_MAIN",""):
   cSharp_mainNote = ""
-else: cSharp_mainNote = """
+else: cSharp_mainNote = r"""
 // or just use the Main() at end (compile with csc, and
 // see --help for usage)
 //   (to omit this Main() from the generated file, set
 //    the environment variable ANNOGEN_CSHARP_NO_MAIN before
 //    running Annotator Generator)"""
 
-cSharp_start = r"""// C# generated by """+program_name[:program_name.index("(c)")].strip()+r"""
+cSharp_start = "// C# code "+version_stamp+r"""
 // use: new Annotator(txt).result()
-// (can also set annotation_mode on the Annotator)"""+cSharp_mainNote+"""
+// (can also set annotation_mode on the Annotator)"""+cSharp_mainNote+r"""
 
 enum Annotation_Mode { ruby_markup, annotations_only, brace_notation };
 
 class Annotator {
+public const string version="""+'"'+version_stamp+r"""";
 public Annotator(string txt) { nearbytes=%%YBYTES%%; inBytes=System.Text.Encoding.UTF8.GetBytes(txt); inPtr=0; writePtr=0; needSpace=false; outBuf=new System.IO.MemoryStream(); annotation_mode = Annotation_Mode.ruby_markup; }
 int nearbytes;
 public Annotation_Mode annotation_mode;
@@ -1503,7 +1506,7 @@ class Test {
 }
 """
 
-golang_start = r"""/* "Go" code generated by """+program_name[:program_name.index("(c)")].strip()+r"""
+golang_start = '/* "Go" code '+version_stamp+r"""
 
 To set up a Web service on GAE, put this file in a
 subdirectory of your project, and create a top-level .go
@@ -1917,7 +1920,7 @@ class BytecodeAssembler:
         except TooNarrow: pass
     assert 0, "can't even assemble it with 255-byte addressing !?!"
 
-js_start = r"""/* Javascript generated by """+program_name[:program_name.index("(c)")].strip()+r"""
+js_start = '/* Javascript '+version_stamp+r"""
 
 Usage:
 
@@ -1938,7 +1941,7 @@ Usage:
 */
 
 var Annotator={
-"""
+version: '"""+version_stamp+"',\n"
 js_end = r"""
 annotate: function(input) {
 /* TODO: if input is a whole html doc, insert css in head
@@ -2051,7 +2054,7 @@ if (typeof require != "undefined" && typeof module != "undefined" && require.mai
 }
 """
 
-py_start = r"""# Python generated by """+program_name[:program_name.index("(c)")].strip()+r"""
+py_start = '# Python '+version_stamp+r"""
 
 # You can import this module and call annotate(utf8 bytes)
 # (from multiple threads if desired),
@@ -2063,6 +2066,7 @@ py_start = r"""# Python generated by """+program_name[:program_name.index("(c)")
 """
 py_end = r"""
 class Annotator:
+ version="""+'"'+version_stamp+r""""
  def __call__(self,inStr,aType):
   if aType=="ruby": self.startA,self.midA,self.endA = "<ruby><rb>","</rb><rt>","</rt></ruby>"
   elif aType=="raw": self.startA=self.midA=self.endA = ""
@@ -3193,7 +3197,7 @@ def outputParser(rulesAndConds):
     longest_rule_len += ybytes_max # because buffer len is 2*longest_rule_len, we shift half of it when (readPtr-bufStart +ybytes >= bufLen) and we don't want this shift to happen when writePtr-bufStart = Half_Bufsize-1 and readPtr = writePtr + Half_Bufsize-1 (TODO: could we get away with max(0,ybytes_max-1) instead? but check how this interacts with the line below; things should be safe as they are now).  This line's correction was missing in Annogen v0.599 and below, which could therefore occasionally emit code that, when running from stdin, occasionally replaced one of the document's bytes with an undefined byte (usually 0) while emitting correct annotation for the original byte.  (This could result in bad UTF-8 that crashed the bookmarklet feature of Web Adjuster v0.21 and below.)
     longest_rule_len = max(ybytes_max*2, longest_rule_len) # make sure the half-bufsize is at least ybytes_max*2, so that a read-ahead when pos is ybytes_max from the end, resulting in a shift back to the 1st half of the buffer, will still leave ybytes_max from the beginning, so yar() can look ybytes_max-wide in both directions
     if javascript:
-      outfile.write(js_start+"\n")
+      outfile.write(js_start)
       b = BytecodeAssembler()
       b.addActionDictSwitch(byteSeq_to_action_dict,False)
       outfile.write("data: "+repr(b.link())+",\n")