m4: Mkstemp

1 
1 13.5 Making temporary files
1 ===========================
1 
1 Commands specified to 'syscmd' or 'esyscmd' might need a temporary file,
1 for output or for some other purpose.  There is a builtin macro,
1 'mkstemp', for making a temporary file:
1 
1  -- Builtin: mkstemp (TEMPLATE)
1  -- Builtin: maketemp (TEMPLATE)
1      Expands to the quoted name of a new, empty file, made from the
1      string TEMPLATE, which should end with the string 'XXXXXX'.  The
1      six 'X' characters are then replaced with random characters
1      matching the regular expression '[a-zA-Z0-9._-]', in order to make
1      the file name unique.  If fewer than six 'X' characters are found
1      at the end of 'template', the result will be longer than the
1      template.  The created file will have access permissions as if by
1      'chmod =rw,go=', meaning that the current umask of the 'm4' process
1      is taken into account, and at most only the current user can read
1      and write the file.
1 
1      The traditional behavior, standardized by POSIX, is that 'maketemp'
1      merely replaces the trailing 'X' with the process id, without
1      creating a file or quoting the expansion, and without ensuring that
1      the resulting string is a unique file name.  In part, this means
1      that using the same TEMPLATE twice in the same input file will
1      result in the same expansion.  This behavior is a security hole, as
1      it is very easy for another process to guess the name that will be
1      generated, and thus interfere with a subsequent use of 'syscmd'
1      trying to manipulate that file name.  Hence, POSIX has recommended
1      that all new implementations of 'm4' provide the secure 'mkstemp'
1      builtin, and that users of 'm4' check for its existence.
1 
1      The expansion is void and an error issued if a temporary file could
1      not be created.
1 
1      The macros 'mkstemp' and 'maketemp' are recognized only with
1      parameters.
1 
1    If you try this next example, you will most likely get different
1 output for the two file names, since the replacement characters are
1 randomly chosen:
1 
1      $ m4
1      define(`tmp', `oops')
1      =>
1      maketemp(`/tmp/fooXXXXXX')
1      =>/tmp/fooa07346
1      ifdef(`mkstemp', `define(`maketemp', defn(`mkstemp'))',
1            `define(`mkstemp', defn(`maketemp'))dnl
1      errprint(`warning: potentially insecure maketemp implementation
1      ')')
1      =>
1      mkstemp(`doc')
1      =>docQv83Uw
1 
1    Unless you use the '--traditional' command line option (or '-G',
1 ⇒Invoking m4 Limits control.), the GNU version of 'maketemp' is
1 secure.  This means that using the same template to multiple calls will
1 generate multiple files.  However, we recommend that you use the new
1 'mkstemp' macro, introduced in GNU M4 1.4.8, which is secure even in
1 traditional mode.  Also, as of M4 1.4.11, the secure implementation
1 quotes the resulting file name, so that you are guaranteed to know what
1 file was created even if the random file name happens to match an
1 existing macro.  Notice that this example is careful to use 'defn' to
1 avoid unintended expansion of 'foo'.
1 
1      $ m4
1      define(`foo', `errprint(`oops')')
1      =>
1      syscmd(`rm -f foo-??????')sysval
1      =>0
1      define(`file1', maketemp(`foo-XXXXXX'))dnl
1      ifelse(esyscmd(`echo \` foo-?????? \''), ` foo-?????? ',
1             `no file', `created')
1      =>created
1      define(`file2', maketemp(`foo-XX'))dnl
1      define(`file3', mkstemp(`foo-XXXXXX'))dnl
1      ifelse(len(defn(`file1')), len(defn(`file2')),
1             `same length', `different')
1      =>same length
1      ifelse(defn(`file1'), defn(`file2'), `same', `different file')
1      =>different file
1      ifelse(defn(`file2'), defn(`file3'), `same', `different file')
1      =>different file
1      ifelse(defn(`file1'), defn(`file3'), `same', `different file')
1      =>different file
1      syscmd(`rm 'defn(`file1') defn(`file2') defn(`file3'))
1      =>
1      sysval
1      =>0
1