4
4
"bytes"
5
5
"errors"
6
6
"fmt"
7
+ "io"
7
8
"io/ioutil"
8
9
"os"
9
10
"os/exec"
@@ -13,6 +14,10 @@ import (
13
14
"strings"
14
15
"syscall"
15
16
"time"
17
+ "unicode"
18
+
19
+ "golang.org/x/text/transform"
20
+ "golang.org/x/text/unicode/norm"
16
21
17
22
"github.com/fsnotify/fsnotify"
18
23
flag "github.com/spf13/pflag"
@@ -110,9 +115,11 @@ var (
110
115
clearFlag string
111
116
mustClear bool
112
117
auxExtensions string
118
+ mustNoNormalize bool
113
119
// global variables
114
120
texDistro string
115
121
texVersionStr string
122
+ inBaseOriginal string
116
123
inBase string
117
124
outBase string
118
125
isRecompiling bool
@@ -160,6 +167,19 @@ func setDistro() {
160
167
compileOptions = []string {"-interaction=batchmode" , "-halt-on-error" }
161
168
}
162
169
170
+ // used in normalizeName
171
+ func isMn (r rune ) bool {
172
+ return unicode .Is (unicode .Mn , r ) // Mn: nonspacing marks
173
+ }
174
+
175
+ // normalizeName remove accents and spaces
176
+ // borrowed from https://stackoverflow.com/a/26722698
177
+ func normalizeName (fileName string ) string {
178
+ t := transform .Chain (norm .NFD , transform .RemoveFunc (isMn ), norm .NFC )
179
+ result , _ , _ := transform .String (t , fileName )
180
+ return strings .ReplaceAll (result , " " , "" )
181
+ }
182
+
163
183
// Set the configuration variables from the command line flags
164
184
func SetParameters () {
165
185
// set the distro based on the pdftex version
@@ -173,12 +193,10 @@ func SetParameters() {
173
193
flag .StringVar (& infoLevelFlag , "info" , "actions" , "The info level [no|errors|errors+log|actions|debug]." )
174
194
flag .StringVar (& logSanitize , "log-sanitize" , `(?m)^(?:! |l\.|<recently read> ).*$` , "Match the log against this regex before display, or display all if empty.\n " )
175
195
flag .StringVar (& splitPattern , "split" , `(?m)^\s*(?:%\s*end\s*preamble|\\begin{document})` , "Match the log against this regex before display, or display all if empty.\n " )
176
- if texDistro == "miktex" {
177
- tempFolderName = "temp_files"
178
- }
179
- flag .StringVar (& tempFolderName , "temp-folder" , tempFolderName , "Folder to store all temp files, .fmt included." )
196
+ flag .StringVar (& tempFolderName , "temp-folder" , "" , "Folder to store all temp files, .fmt included." )
180
197
flag .StringVar (& clearFlag , "clear" , "auto" , "Clear auxiliary files and .fmt at end [auto|yes|no].\n When watching auto=true, else auto=false.\n In debug mode clear is false." )
181
198
flag .StringVar (& auxExtensions , "aux-extensions" , "aux,bbl,blg,fmt,fff,glg,glo,gls,idx,ilg,ind,lof,lot,nav,out,ptc,snm,sta,stp,toc" , "Extensions to remove in clear at the end procedure.\n " )
199
+ flag .BoolVar (& mustNoNormalize , "no-normalize" , false , "Keep accents and spaces in intermediate files." )
182
200
flag .BoolVarP (& mustShowVersion , "version" , "v" , false , "Print the version number." )
183
201
flag .BoolVarP (& mustShowHelp , "help" , "h" , false , "Print this help message." )
184
202
// keep the flags order
@@ -211,7 +229,12 @@ func SetParameters() {
211
229
check (errors .New ("You should provide a .tex file to compile." ))
212
230
}
213
231
214
- inBase = strings .TrimSuffix (flag .Arg (0 ), ".tex" )
232
+ inBaseOriginal = strings .TrimSuffix (flag .Arg (0 ), ".tex" )
233
+ if mustNoNormalize {
234
+ inBase = inBaseOriginal
235
+ } else {
236
+ inBase = normalizeName (inBaseOriginal )
237
+ }
215
238
216
239
// synctex or not?
217
240
if ! mustNotSync {
@@ -252,28 +275,30 @@ func SetParameters() {
252
275
mustCompileAll = true
253
276
}
254
277
// set temp folder?
255
- outBase = inBase
278
+ if ! mustNoNormalize {
279
+ tempFolderName = normalizeName (tempFolderName )
280
+ }
256
281
if len (tempFolderName ) > 0 {
257
- if texDistro == "miktex" {
258
- compileOptions = append (compileOptions , "-aux-directory=" + tempFolderName )
282
+ if inBase == inBaseOriginal && texDistro == "miktex" {
259
283
precompileOptions = append (precompileOptions , "-aux-directory=" + tempFolderName )
284
+ compileOptions = append (compileOptions , "-aux-directory=" + tempFolderName )
260
285
} else {
261
- compileOptions = append (compileOptions , "-output-directory=" + tempFolderName )
262
286
precompileOptions = append (precompileOptions , "-output-directory=" + tempFolderName )
287
+ compileOptions = append (compileOptions , "-output-directory=" + tempFolderName )
263
288
}
264
-
265
289
outBase = filepath .Join (tempFolderName , inBase )
290
+ } else {
291
+ outBase = inBase
266
292
}
267
293
268
294
// set the source filename
269
- var compileName string
295
+ precompileName := "&pdflatex " + inBase + ".preamble.tex"
296
+ precompileOptions = append (precompileOptions , "-jobname=" + inBase , precompileName )
297
+ compileName := "&" + inBase + " " + inBase + ".body.tex"
270
298
if mustCompileAll {
271
- compileName = inBase + ".tex"
272
- } else {
273
- compileName = "&" + inBase + " " + inBase + ".body.tex"
299
+ compileName = "&pdflatex " + inBase + ".tex"
274
300
}
275
301
compileOptions = append (compileOptions , "-jobname=" + inBase , compileName )
276
- precompileOptions = append (precompileOptions , "-jobname=" + inBase , "&pdflatex " + inBase + ".preamble.tex" )
277
302
278
303
// clear or not
279
304
mustClear = (infoLevel < infoDebug ) && (clearFlag == "yes" || clearFlag == "auto" && ! mustNoWatch )
@@ -337,7 +362,7 @@ func run(info, command string, args ...string) {
337
362
// print action?
338
363
if infoLevel >= infoActions {
339
364
startTime = time .Now ()
340
- fmt .Print (info + "..." )
365
+ fmt .Print ("::::::: " , info + "..." )
341
366
}
342
367
// run command
343
368
err = cmd .Run ()
@@ -365,17 +390,57 @@ func info(message ...interface{}) {
365
390
}
366
391
}
367
392
393
+ // Borrowed from https://stackoverflow.com/a/21067803
394
+ func copyFile (src , dst string ) (ok bool ) {
395
+ defer func () {
396
+ if err == nil {
397
+ ok = true
398
+ } else {
399
+ check (errors .New ("Error while copy " + src + " to " + dst + "." ))
400
+ }
401
+ }()
402
+
403
+ info (" copy" , src , "to" , dst )
404
+
405
+ in , err := os .Open (src )
406
+ if err != nil {
407
+ return
408
+ }
409
+ defer in .Close ()
410
+ out , err := os .Create (dst )
411
+ if err != nil {
412
+ return
413
+ }
414
+ defer func () {
415
+ cerr := out .Close ()
416
+ if err == nil {
417
+ err = cerr
418
+ }
419
+ }()
420
+ if _ , err = io .Copy (out , in ); err != nil {
421
+ return
422
+ }
423
+ err = out .Sync ()
424
+ return
425
+ }
426
+
368
427
// splitTeX split the `.tex` file to two files `.preamble.tex` and `.body.tex`.
369
428
// it also append `\dump` to the preamble and perpend `%&...` to the body.
370
429
// both files are saved in the same folder (not in the temporary one) as the original source.
371
430
func splitTeX () (ok bool ) {
372
- sourceName := inBase + ".tex"
373
- // do I have to do something?
431
+ sourceName := inBaseOriginal + ".tex"
432
+ if isFileMissing (sourceName ) {
433
+ check (errors .New ("File " + sourceName + " is missing." ))
434
+ }
435
+ // we hope that...
436
+ ok = true
437
+ // copy the original?
438
+ if mustCompileAll && inBaseOriginal != inBase {
439
+ ok = copyFile (inBaseOriginal + ".tex" , inBase + ".tex" )
440
+ }
441
+ // is the split necessary?
374
442
if ! mustBuildFormat && mustCompileAll {
375
- if isFileMissing (sourceName ) {
376
- check (errors .New ("File " + sourceName + " is missing." ))
377
- }
378
- return true
443
+ return
379
444
}
380
445
// read the file
381
446
var texdata []byte
@@ -402,12 +467,14 @@ func splitTeX() (ok bool) {
402
467
}
403
468
// important to first get body because textdata is polluted by \dump in the next line
404
469
bodyName := inBase + ".body.tex"
470
+ info (" create" , bodyName )
405
471
texBody := append ([]byte ("\n %&" + inBase + "\n " ), texdata [loc [0 ]:]... )
406
472
err = ioutil .WriteFile (bodyName , texBody , 0644 )
407
473
check (err , "Problem while writing" , bodyName )
408
474
ok = (err == nil )
409
475
410
476
preambleName := inBase + ".preamble.tex"
477
+ info (" create" , preambleName )
411
478
texPreamble := append (texdata [:loc [0 ]], []byte ("\\ dump" )... )
412
479
err = ioutil .WriteFile (preambleName , texPreamble , 0644 )
413
480
check (err , "Problem while writing" , preambleName )
@@ -425,7 +492,7 @@ func clearFiles(base, extensions string) {
425
492
continue
426
493
}
427
494
if infoLevel >= infoActions {
428
- fmt . Println ( "Remove " , fileToDelete )
495
+ info ( " remove " , fileToDelete )
429
496
}
430
497
os .Remove (fileToDelete )
431
498
}
@@ -446,6 +513,8 @@ func precompile() {
446
513
if mustBuildFormat || ! mustCompileAll && isFileMissing (outBase + ".fmt" ) {
447
514
run ("Precompile" , "pdftex" , precompileOptions ... )
448
515
}
516
+ // we tel to splitTex that the preamble is not needed any more
517
+ mustBuildFormat = false
449
518
}
450
519
451
520
// compile produce the `.pdf` file based on the `.body.tex` part.
@@ -465,14 +534,18 @@ func compile(draft bool) {
465
534
} else {
466
535
run (msg , "pdftex" , compileOptions ... )
467
536
}
468
- if len (tempFolderName ) > 0 && texDistro != "miktex" {
537
+ // move/rename .pdf and .synctex to the original source
538
+ if ! draft && inBaseOriginal != outBase && (texDistro != "miktex" || inBaseOriginal != inBase ) {
469
539
if ! isFileMissing (outBase + ".pdf" ) {
470
- info ("Move pdf from temp folder." )
471
- os .Rename (outBase + ".pdf" , inBase + ".pdf" )
540
+ if copyFile (outBase + ".pdf" , inBaseOriginal + ".pdf" ) {
541
+ info (" delete" , outBase + ".pdf" )
542
+ os .Remove (outBase + ".pdf" )
543
+ }
472
544
}
473
545
if ! mustNotSync && ! isFileMissing (outBase + ".synctex" ) {
474
- info ("Move synctex from temp folder." )
475
- os .Rename (outBase + ".synctex" , inBase + ".synctex" )
546
+ info (" move" , outBase + ".synctex" , "to" , inBaseOriginal + ".synctex" )
547
+ err = os .Rename (outBase + ".synctex" , inBaseOriginal + ".synctex" )
548
+ check (err , "Error while copy " + outBase + ".synctex to " + inBaseOriginal + ".synctex." )
476
549
}
477
550
}
478
551
@@ -540,7 +613,7 @@ func main() {
540
613
}
541
614
// watching ?
542
615
if ! mustNoWatch {
543
- info ("Watching for files changes...(to exit press Ctrl/Cmd-C)." )
616
+ info ("Watching for file changes...(to exit press Ctrl/Cmd-C)." )
544
617
// creates a new file watcher
545
618
watcher , err := fsnotify .NewWatcher ()
546
619
check (err , "Problem creating the file watcher" )
@@ -577,8 +650,8 @@ func main() {
577
650
}()
578
651
579
652
// out of the box fsnotify can watch a single file, or a single directory
580
- err = watcher .Add (inBase + ".tex" )
581
- check (err , "Problem watching" , inBase + ".tex" )
653
+ err = watcher .Add (inBaseOriginal + ".tex" )
654
+ check (err , "Problem watching" , inBaseOriginal + ".tex" )
582
655
583
656
<- done
584
657
}
0 commit comments