docs: def save(self, dirname=None, refresh=0, refresh_index=True, emit_message=True):
          """Saves entry in the given directory. Data products will be copied over if not
          residing in that directory.
          'refresh' is a timestamp, passed to renderIndex(), causing all data products OLDER than the specified time to be regenerated.
          'refresh_index', if true, causes index files to be re-rendered unconditionally
          """
          if not refresh and not self.updated:
              return
          timestr = time.strftime("%Y%m%d-%H%M%S", time.localtime(self.timestamp))
          Purr.progressMessage("Rendering entry for %s" % timestr)
          if dirname:
              self.pathname = pathname = os.path.join(dirname, "%s-%s" %
                                                      (("ignore" if self.ignore else "entry"), timestr))
          elif not self.pathname:
              raise ValueError("Cannot save entry: pathname not specified")
          else:
              pathname = self.pathname
          # set timestamp
          if not self.timestamp:
              self.timestamp = int(time.time())
          # get canonized path to output directory
          pathname = Purr.canonizePath(pathname)
          if not os.path.exists(pathname):
              os.mkdir(pathname)
          # now save content
          # get device of pathname -- need to know whether we move or copy
          devnum = os.stat(pathname).st_dev
          # copy data products as needed
          dprintf(2, "saving entry %s, %d data products\n", pathname, len(self.dps))
          dps = []
          for dp in self.dps:
              # if archived, this indicates a previously saved data product, so ignore it
              # if ignored, no need to save the DP -- but keep it in list
              if dp.archived or dp.ignored:
                  dprintf(3, "dp %s is archived or ignored, skipping\n", dp.sourcepath)
                  dps.append(dp)
                  continue
              # file missing for some reason (perhaps it got removed on us?) skip data product entirely
              if not os.path.exists(dp.sourcepath):
                  dprintf(2, "data product %s missing, ignoring\n", dp.sourcepath)
                  continue
              Purr.progressMessage("archiving %s" % dp.filename, sub=True)
              # get normalized source and destination paths
              dprintf(2, "data product: %s, rename %s, policy %s\n", dp.sourcepath, dp.filename, dp.policy)
              sourcepath = Purr.canonizePath(dp.sourcepath)
              destname = dp.fullpath = os.path.join(pathname, dp.filename)
              dprintf(2, "data product: %s -> %s\n", sourcepath, destname)
              # does the destination product already exist? skip if same file, else remove
              if os.path.exists(destname):
                  if os.path.samefile(destname, sourcepath):
                      dprintf(2, "same file, skipping\n")
                      dp.timestamp = os.path.getmtime(destname)
                      dps.append(dp)
                      continue
                  if os.system("/bin/rm -fr '%s'" % destname):
                      print("Error removing %s, which is in the way of %s" % (destname, sourcepath))
                      print("This data product is not saved.")
                      continue
              # for directories, compress with tar
              if os.path.isdir(sourcepath):
                  sourcepath = sourcepath.rstrip('/')
                  if dp.policy == "copy" or dp.policy.startswith("move"):
                      dprintf(2, "archiving to tgz\n")
                      if os.system("tar zcf '%s' -C '%s' '%s'" % (destname,
                                                                  os.path.dirname(sourcepath),
                                                                  os.path.basename(sourcepath))):
                          print("Error archiving %s to %s" % (sourcepath, destname))
                          print("This data product is not saved.")
                          continue
                      if dp.policy.startswith("move"):
                          os.system("/bin/rm -fr '%s'" % sourcepath)
              # else just a file
              else:
                  # now copy/move it over
                  if dp.policy == "copy":
                      dprintf(2, "copying\n")
                      if _copy_update(sourcepath, destname):
                          print("Error copying %s to %s" % (sourcepath, destname))
                          print("This data product is not saved.")
                          continue
                  elif dp.policy.startswith('move'):
                      if _move_update(sourcepath, destname):
                          print("Error moving %s to %s" % (sourcepath, destname))
                          print("This data product is not saved.")
                          continue
              # success, set timestamp and append
              dp.timestamp = os.path.getmtime(destname)
              dp.archived = True
              dps.append(dp)
          # reset list of data products
          self.dps = dps
          # now write out content
          self.cached_include = os.path.join(pathname, 'index.include.html')
          self.cached_include_valid = False
          self.index_file = os.path.join(pathname, "index.html")
          self.generateIndex(refresh=refresh, refresh_index=refresh_index and time.time())
          self.updated = False