Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

When I \immediate\write\somefile{foo}, TeX writes foo to the file open in the output stream \somefile, but it also goes to the next line. (The resulting file has 4 bytes.)

Since I am writing files and reading them as the body of some environments, it would be nice if I could disable this extra new-line character.

The main problem is when I write foo^^M at the end of a file, and the newline I write combines with the 'default' one: it leads to a spurious \par, which breaks short macros (and the align environment).

So, is it possible to avoid going to the next line using \write?

share|improve this question
    
Could you post a full minimal example? –  Joseph Wright Jan 1 '11 at 20:59
    
That's trickier than I thought at first: I need to both include the code for verbatim-writing-to-file, and the code for reading from the file. I'll try to do this tomorrow. –  Bruno Le Floch Jan 1 '11 at 22:15
add comment

3 Answers 3

up vote 8 down vote accepted

To answer your question: No, you cannot avoid the new line character that gets inserted. See below for how I arrived at this conclusion.

I find the source code to TeX very confusing so it's certainly possible that I'm missing something here. The way I read this, \write is implemented as an extension, not because it's actually an extension, just that Knuth wanted to give an example of writing one. In particular, \write is implemented as a whatsit node:

@<Implement \.{\\write}@>=
begin k:=cur_cs; new_write_whatsit(write_node_size);@/
cur_cs:=k; p:=scan_toks(false,false); write_tokens(tail):=def_ref;
end

Rather than track down exactly how whatsits are treated, it suffices to look for the implementation of \immediate since \immediate\write performs the write immediately. The relevant portion of the code is the following.

@<Implement \.{\\immediate}@>=
begin get_x_token;
if cur_cmd=extension then begin
    if cur_chr<=close_node then
      begin p:=tail; do_extension; {append a whatsit node}
      out_what(tail); {do the action immediately}
      flush_node_list(tail); tail:=p; link(p):=null;
      end

Clearly, this is appending a whatsit node, executing the node, flushing it (presumably freeing the memory associated with it), and then restoring the linked list to what it was before it appended the node. What we're interested in here is out_what. Again, the relevant portion follows.

procedure out_what(@!p:pointer);
var j:small_number; {write stream number}
begin case subtype(p) of
open_node,write_node,close_node:@<Do some work that has been queued up
  for \.{\\write}@>;

 

@<Do some work that has been queued up...@>=
if not doing_leaders then
  begin j:=write_stream(p);
  if subtype(p)=write_node then write_out(p)

Obviously, we want write_out.

procedure write_out(@!p:pointer);
var old_setting:0..max_selector; {holds print |selector|}
@!old_mode:integer; {saved |mode|}
@!j:small_number; {write stream number}
@!q,@!r:pointer; {temporary variables for list manipulation}
begin @<Expand macros in the token list
  and make |link(def_ref)| point to the result@>;
old_setting:=selector; j:=write_stream(p);
if write_open[j] then selector:=j
else  begin {write to the terminal if file isn't open}
  if (j=17)and(selector=term_and_log) then selector:=log_only;
  print_nl("");
  end;
token_show(def_ref); print_ln;
flush_list(def_ref); selector:=old_setting;
end;

The global variable selector is used to determine where output should be directed. In the case of a \write to a file, it will be a number 0–15. The line if write_open[j] then selector:=j sets the selector to j. token_show(def_ref) prints the tokens pointed to by def_ref based on selector. Then print_ln is called.

@ To end a line of text output, we call |print_ln|.

@<Basic print...@>=
procedure print_ln; {prints an end-of-line}
begin case selector of
term_and_log: begin wterm_cr; wlog_cr;
  term_offset:=0; file_offset:=0;
  end;
log_only: begin wlog_cr; file_offset:=0;
  end;
term_only: begin wterm_cr; term_offset:=0;
  end;
no_print,pseudo,new_string: do_nothing;
othercases write_ln(write_file[selector])
endcases;@/
end; {|tally| is not affected}

As that comment says, print_ln is used to end a line of text.

It's actually at this point that I get stuck. I can't find a definition for write_ln. My best guess is that WEB removes underscores so that write_ln becomes the pascal writeln which the web2c binary fixwrites converts. From the top of fixwrites.c:

/* fixwrites -- convert Pascal write/writeln's into fprintf's or putc's.
   Originally by Tim Morgan, October 10, 1987.  */

At any rate, the very first function in the generated pdftex0.c is

void
println ( void )
{
  println_regmem
  switch ( selector )
  {
    /* elided cases 17 through 21 */
    default:
    putc ('\n', writefile [selector ]);
    break;
  }
}
share|improve this answer
    
@TH : impressive!! (I can track things down in La/TeX code, but definitely not in C or WEB.) Now I know why Knuth does not talk about customizing this. And I will just need to strip my input from trailing newlines. Thanks for the info. –  Bruno Le Floch Jan 2 '11 at 9:35
add comment

Thinking about it, I suspect you are running into the followin

Each message given with \immediate\write starts on a new line; the user can force a new line in the message by including the character with number \newlinechar. This parameter also works in \message.

(TeX by Topic, as so often.) To keep control of lines, you need to use only one \write and use ^^J where you want a new line.

share|improve this answer
    
No, I am asking in some sense for the opposite of \newlinechar: how does TeX decide what character to append at each write command? --- cf. page 227 of the TeXbook: " \immediate\write16 differs from \message in that \write prints the text on a line by itself " --- I would like a \message\file if it is possible. --- It is true that a solution to my problem is to trim what I write to the file: remove my trailing \newlinechar, and leave the automatic newline alone. That's a bit tricky since I don't always have a trailing newline, and accessing the last character of a string is hard. –  Bruno Le Floch Jan 1 '11 at 22:12
add comment

This isn't a direct answer, but since you say you're reading the files back into TeX, you could try appending \relax to the end of what you're writing out: \immediate\write\somefile{foo\relax}. The \relax will swallow all the following space, which sounds like it will solve the symptoms of your problem, though not the source. Obviously, it's not a perfect solution, but it might be good enough. (My guess at an actual solution was \newlinechar=-1, but no change I made to \newlinechar seemed to do anything.)

share|improve this answer
1  
In fact, I am currently (prepending and) appending \relax. But it breaks down when inside a verbatim environment: \relax is typeset. Admittedly, I don't care: given the precise context, there is absolutely no reason that my code would ever be used for inclusion in a verbatim. But still, not completely satisfactory. --- \newlinechar changes what character is translated by TeX to a new line in the output. But the final new line is just created from nowhere by TeX, it seems (I guess the solution is to dive into the TeX source and check ;-)). –  Bruno Le Floch Jan 1 '11 at 23:40
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.