gsch2pcb: Make --m4-file and -m4-pcbdir arguments work again.
[geda-gaf/peter-b.git] / utils / scripts / garchive.py
blob7306fa13bdd2a239ccaa2f2649a64903c4c7042e
1 #! /usr/bin/env python
3 # Copyright (C) 2003 Stuart Brorson <sdb@cloud9.net>
5 #------------------------------------------------------------------
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 #-------------------------------------------------------------------
21 """
22 This program is used to create a gEDA design archive. It operates in two
23 modes: archive mode and extract mode. In archive mode it creates a
24 project archive from a bunch of project files, and in extract mode it
25 extracts the files from the archive and places them in the local dir.
27 Detailed description:
29 Information about program invocation is held in the Usage string below.
31 --- Archive mode algorithm:
32 1. It gets the local directory name, as well as a bunch of info from the
33 command line & stores all the info in the object "Args". Then it
34 cd's into /tmp (ScratchDirectory).
35 2. It gets the symbol library search path from $(GEDADATADIR)/
36 gschemrc-common and ./gschemrc & creates a list of all search
37 paths. Note that the system-dependent search path is hardcoded into
38 the script by "make" when the file is installed.
39 3. It reads the list of files to archive from ./garchiverc (or the -f
40 substitute) and the command line and creates an internal list of files to
41 archive.
42 4. It finds the .sch files among the archive list, and creates an internal
43 list of schem files process/archive.
44 5. For each schem file in the list, it opens the file, runs through it,
45 and builds a list of symbols used in that file. Naturally, duplicate
46 symbols are ignored.
47 6. It opens a directory called /tmp/garchive-symbols. For each symbol
48 file in the symbol file list, it gets the file from $(GEDADATADIR)/sym
49 and sticks it into ./garchive-symbols.
50 7. It takes the local gschemrc, and munges it so that gschem will find
51 all symbols in ./garchive-symbols.
52 8. It then creates a tar archive of the following:
53 * All files listed in garchiverc and/or the command line.
54 * The entire ./garchive-symbols directory
55 * gschemrc
56 * garchiverc
57 9. The prog then gzips the tar archive, renames it to the name
58 specified by the user, and sticks it in the user's directory
59 10. It then cd's back to the local directory & deletes the leftover
60 cruft from /tmp (or ScratchDirectory).
62 Important data structures during creation of archive:
63 * LibraryPathList -- list of paths to symbol libraries.
64 * ArchiveFileList -- list of all files to archive. Includes gschemrc and
65 garchiverc, as well as garchive-symbols/
66 * SchemFileList -- list of schematic files found.
67 * SymbolFileList -- list of symbol files found. Duplicates are ignored.
69 --- Extract mode algorithm:
70 1. Copy archive file into /tmp (or ScratchDirectory).
71 2. Create a list of archive's contents using "tar -t -f ProjectArchive"
72 3. Extract files into /tmp
73 3. Loop on file list. Move each file into user's directory.
74 Before each move, make sure that no overwrite of existing files will occur.
75 After each iteration, clean up the cruft left in /tmp.
77 Note: File names used internally have no / prefix. Dir names have no / suffix.
78 Therefore, must include / manually in each case.
80 --- TBD suggestions from users:
81 None right now . . . .
83 --- Revision history:
84 20031114 -- First (alpha) version by SDB.
85 20031121 -- Incorporated SPICE file archiving. Now get /tmp dir from
86 environment. Presence of RC files is now optional -- user
87 is queried about creating them if they don't exist.
88 20031204 -- Changed program so that it keeps .tar.gz as the archive file
89 suffix due to popular demand.
90 """
91 ############################################################################
92 import sys, copy, string, getopt, re, os, commands
94 ############################################################################
95 # Helper fcns and data structures. #
96 ############################################################################
98 #---------------------------------------------------------------------------
99 # This is the help string.
100 Usage =\
102 garchive -- the gEDA archiving utility. Used to create as well as extract
103 gEDA designs from an archive. The two modes of operation are "archive mode"
104 (archive creation) and "extract mode". Archive mode is the default.
106 Command line switches:
107 -f <filename> -- Optional. Used in archive mode. Read files to archive from
108 <filename> instead of garchiverc.
109 -v -- Optional. Verbose mode. Used in both archive and extract mode.
110 Spews lots of info about what the prog is doing.
111 -e -- Mandatory if you want to extract. Extract mode.
112 Open up the archive specified on the command line.
113 -a -- Optional. Archive mode. Archive mode (i.e. create a project
114 archive) is the default; this flag exists to give people
115 the warm and fuzzies.
116 -o <outfile> -- Optional. Used in archive mode. Specifies the name of
117 the output archive file. The output file extension
118 should be ".tar.gz". If this flag is not specified, the
119 output file name is "ProjectArchive.tar.gz".
121 Example usage:
122 Create an archive named MyArchive.tar.gz (files to store listed in garchiverc):
123 garchive -o MyArchive.tar.gz
125 Verbosely create an archive (archives files listed on cmd line
126 as well as those listed in garchiverc):
127 garchive -v -o MyArchive.tar.gz README Schematic1.sch Schematic2.sch Schematic3.sch
129 Extract an archive:
130 garchive -e ProjectArchive.tar.gz
132 Copyright (C) 2003 by SDB. Released under the GPL.
135 #---------------------------------------------------------------------------
136 def DebugSpew(Args, String):
138 This prints out String when the -v flag is set, otherwise is silent
140 if (Args.VerboseMode == "verbose"):
141 print("---- "+ String)
142 return
144 #---------------------------------------------------------------------------
145 def CheckFilename(Filename):
147 This checks a string to make sure that it is a valid filename.
148 It currently doesn't do very much. . . .
150 if (re.search('\.tar\.gz$', Filename)):
151 return 1
152 else:
153 return 0
156 #----------------------------------------------------------------------
157 def NormalizePath(SearchDir, DirName):
159 This fcn expands environment variables like ${GEDADATA} and ${HOME}
160 in full absolute file/directory names.
161 Environment vars must be in the form ${foo}.
162 It expects to see a directory or file name like "${foo}/bar/baz/".
163 Only one env var may be expanded at a time. SearchDir is the base
164 directory from which all relative paths are normalized.
166 # First replace environment var
167 if re.match('\${[\w]+}', DirName):
168 Match = re.search('(\${)([\w]+)(}/)([\w/]+)', DirName)
169 EnvVar = Match.group(2)
171 # Need to make sure that something exists beyond ${ENVVAR}
172 try:
173 Remainder = Match.group(4)
174 except KeyError:
175 Remainder = ""
177 # Check if env variable is ${GEDADATA}. If so, just replace it with Args.GedaDataDir
178 if (EnvVar == "GEDADATA"):
179 EnvVar = Args.GedaDataDir
180 else:
181 # Try to get environment variable
182 try:
183 EnvVar = os.environ[EnvVar]
184 except KeyError:
185 print ("Env variable "+EnvVar+" not defined in current environment")
186 print ("Try setting it. . . .")
187 sys.exit(1)
189 DirName = EnvVar+"/"+Remainder
191 # Now cd into SearchDir and perform a filename normalization
192 # (to get rid of .. . and other relative file paths . . .)
193 # Then cd back.
194 CurrentDir = os.getcwd()
195 os.chdir(SearchDir)
196 DirName = os.path.abspath(DirName)
197 os.chdir(CurrentDir)
199 return DirName
202 #---------------------------------------------------------------------------
203 class CmdLineArgs:
205 This class holds info about the environment and cmd line args passed to the
206 program. It has only one method: the constructor, which gets the args
207 and fills out the public vars. The public vars are:
209 ProgramMode = "archive", "extract"
210 OutputFileName (archive only) = the name of the archive. default = ProjectArchive.tar.gz
211 InputFileNames = [list of input files on command line]
212 RcFileName (archive only) = name of garchiverc to use (instead of garchiverc)
213 VerboseMode = "quiet", "verbose"
214 FilesToArchiveList (archive only)
215 UserDir = Directory holding files to archive. (Directory where garchive was invoked.)
216 ScratchDir = "/tmp" by default
217 FileArchiveDir = "gschem-files" by default
218 GedaDataDir = directory holding GEDA data & files such as "system-gafrc"
220 def __init__(self):
222 Constructor: parse through cmd line args and fill out vars.
224 self.VerboseMode = "quiet" # default
225 self.ProgramMode = "archive" # default
226 self.GarchiveRcFileName = "garchiverc" # default
227 self.GschemRcFileName = "gschemrc" # default
228 self.OutputFileName = "ProjectArchive.tar.gz" # default
229 self.UserDir = os.path.abspath(os.getcwd())
230 self.FileArchiveDir = "gschem-files" # default
232 # Get GedaDataDir -- this will be set by the Makefile, which does
233 # an sed replacement of GEDADATADIR
234 self.GedaDataDir = "GEDADATADIR"
235 # self.GedaDataDir = "/home/binaries/geda/share/gEDA" # Used for debug
237 # Get ScratchDir, either from environment, or just use /tmp as default.
238 for EnvVar in ["TMP", "TMPVAR", "TEMP"]:
239 try:
240 TempDir = os.environ[EnvVar]
241 except:
242 continue # Not present, continue looping
243 else:
244 self.ScratchDir = TempDir # Got it!
245 break
246 else:
247 self.ScratchDir = "/tmp" # no env var set, use default
249 # Get and process command line args
250 try:
251 OptList, Args = getopt.getopt(sys.argv[1:], 'aef:ho:v')
252 except getopt.error:
253 print Usage # print out usage string if
254 # user uses invalid flag.
255 sys.exit(1)
257 # First pass through args. Get switch settings & set program modes.
258 for Option, Value in OptList:
260 if Option == '-a':
261 self.ProgramMode = "archive"
263 if Option == '-e':
264 self.ProgramMode = "extract"
266 if Option == '-v':
267 self.VerboseMode = "verbose"
269 if Option == '-h':
270 print Usage
271 sys.exit(0)
273 # Second pass. Do sanity checking and get configured filenames.
274 for Option, Value in OptList:
276 if Option == '-a':
277 if self.ProgramMode == "extract": # sanity check
278 raise SyntaxError("Incompatible command line args")
280 if Option == '-e':
281 if self.ProgramMode == "archive": # sanity check
282 raise SyntaxError("Incompatible command line args")
284 if Option == '-f':
285 if self.ProgramMode == "extract": # sanity check
286 raise SyntaxError("Incompatible command line args")
287 try:
288 os.stat(Value)
289 except OSError:
290 print("Resource file "+Value+" doesn't exist. Exiting.")
291 sys.exit(1)
292 else:
293 self.GarchiveRcFileName = Value #strcopy?
295 if Option == '-o':
296 if self.ProgramMode == "extract": # sanity check
297 raise SyntaxError("Incompatible command line args")
298 if CheckFilename(Value):
299 self.OutputFileName = Value #strcopy?
300 else:
301 print("Warning -- output file suffix is not \".tar.gz\" -- the")
302 print("extractor won't know how to deal with your archive.")
303 Input = raw_input("Continue? [y/N] ")
304 if ( (len(Input) == 0) or (Input[0] != "y") ):
305 sys.exit(1)
306 else:
307 self.OutputFileName = Value
309 # Third step: Create list of files remaining on command line, and create output
310 # base file name.
311 self.CmdLineFileList = Args
313 self.OutputFileNameBase = re.sub('\.tar\.gz', '', self.OutputFileName)
315 return
318 #---------------------------------------------------------------------------
319 def GetLibraryPath(Args):
321 This fcn takes the library search path from the local dir and the system
322 gschem-gafrc.
324 DebugSpew(Args, "Now in GetLibraryPath.")
325 LibraryPathList = []
327 LocalRCFileName = Args.UserDir+"/"+Args.GschemRcFileName
328 SystemRCFileName = Args.GedaDataDir+"/system-gafrc"
330 # Now read in system rc file and create sym lib path
331 DebugSpew(Args, "Processing system resource file "+SystemRCFileName)
332 try:
333 SysFile = open(SystemRCFileName, "r")
334 except:
335 print("Unable to find system resource file "+SystemRCFileName+".")
336 sys.exit(1)
338 for line in SysFile.readlines():
339 # Match "(component-library " string. . . .
340 if re.match('^\(component-library ', line):
341 Match = re.search('(")([{$}\w/]+)(")', line)
342 Dir = Match.group(2)
343 Dir = NormalizePath(Args.GedaDataDir, Dir) # Expand any env variables such as ${GEDADATA}. . .
345 # DebugSpew(Args, "Sticking "+Dir+" into LibraryPathList")
346 LibraryPathList.append(Dir)
348 # Match "(component-library-search " string. . . .
349 if re.match('^\(component-library-search', line):
350 Match = re.search('(")([{$}\w/]+)(")', line)
351 Dir = Match.group(2)
352 Dir = NormalizePath(Args.GedaDataDir, Dir) # Expand any env variables such as ${GEDADATA}. . .
354 if Dir in LibraryPathList:
355 pass
356 else:
357 # DebugSpew(Args, "Sticking "+Dir+" into LibraryPathList")
358 LibraryPathList.append(Dir)
360 SysFile.close()
362 # Next read in LocalRCFileName
363 DebugSpew(Args, "Processing local resource file "+LocalRCFileName)
364 try:
365 LocalFile = open(LocalRCFileName, "r")
366 except:
367 Input = raw_input(LocalRCFileName+" doesn't exist. Create empty version in local dir? [Y/n] ")
368 if ( (len(Input) == 0) or (Input[0] != "n") ):
369 os.system("touch "+LocalRCFileName)
370 else:
371 print("You need "+LocalRCFileName+" to create archive. Aborting.")
372 sys.exit(1)
374 for line in LocalFile.readlines():
375 if re.match('^\(component-library ', line):
376 Match = re.search('(")([\S]+)(")', line) # Note additional . to search for
377 Dir = Match.group(2)
378 Dir = NormalizePath(Args.UserDir, Dir) # Expand any env variables and ./
380 if Dir in LibraryPathList:
381 pass
382 else:
383 # DebugSpew(Args, "Sticking "+Dir+" into LibraryPathList")
384 LibraryPathList.append(Dir)
386 LocalFile.close()
388 # Reverse list because that's how it is searched
389 LibraryPathList.reverse()
391 return LibraryPathList
393 #---------------------------------------------------------------------------
394 def CreateArchiveFileList(Args):
396 This creates the list of files in the archive. It starts with
397 known files, and then adds the names of the files to archive,
398 given either at the command line or in the garchiverc file.
400 DebugSpew(Args, "Now in CreateArchiveFileList.")
401 ArchFileList = []
403 # Start with known file names
404 PotentialArchFileList = [Args.UserDir+"/"+Args.GschemRcFileName, Args.UserDir+"/"+Args.GarchiveRcFileName] # Could use map here. . .
406 # Make sure each file exists and can be saved
407 for FileName in PotentialArchFileList:
408 if (os.path.isfile(FileName) or os.path.isdir(FileName)):
409 FileName = NormalizePath(Args.UserDir, FileName) # Just make sure filename is kosher. . . .
410 ArchFileList.append(FileName)
411 else:
412 Input = raw_input(FileName+" doesn't exist. Create empty version in local dir? [Y/n] ")
413 if ( (len(Input) == 0) or (Input[0] != "n") ):
414 print("Creating "+FileName+" in archive.")
415 os.system("touch "+FileName)
416 ArchFileList.append(FileName)
417 else:
418 print("You need "+FileName+" to create archive. Aborting.")
419 sys.exit(1)
421 # Add the gschem-files dir /tmp/gschem-files
422 ArchFileList.append(Args.ScratchDir+"/"+Args.FileArchiveDir) # We build the archive dir in /tmp
424 # Now get names of all schematics and other files to archive.
426 # First get file names from command line
427 DebugSpew(Args, "Examining files listed on command line")
428 for File in Args.CmdLineFileList:
429 File = NormalizePath(Args.UserDir, File)
430 DebugSpew(Args, "Examining "+File+" for inclusion in archive")
431 if (File in ArchFileList):
432 break # Don't include file if it's already there.
433 try:
434 os.stat(File)
435 except OSError:
436 print("File "+File+" listed in command line doesn't exist. Ignoring. . .")
437 continue
438 else:
439 ArchFileList.append(File)
441 # Next get file names from file, if specified.
442 GarchiveRCFile = open(Args.UserDir+"/"+Args.GarchiveRcFileName, "r")
443 DebugSpew(Args, "Examining files listed in "+Args.GarchiveRcFileName)
444 while 1:
445 FileName = GarchiveRCFile.readline()
446 if not FileName:
447 break
449 FileName = re.sub('[\n\s]+', '', FileName) # Strip out \n chars & whitespace
450 FileName = NormalizePath(Args.UserDir, FileName)
451 DebugSpew(Args, "Examining "+FileName+" for inclusion in archive")
453 try:
454 os.stat(FileName)
455 except OSError:
456 print("File "+FileName+" listed in "+Args.GarchiveRcFileName+" doesn't exist. Ignoring. . .")
457 continue
458 else:
459 FileName = NormalizePath(Args.UserDir, FileName)
460 if (FileName in ArchFileList):
461 pass
462 else:
463 ArchFileList.append(FileName)
465 return ArchFileList
467 #---------------------------------------------------------------------------
468 def CreateSchemFileList(Args, FileList):
470 This creates the list of schem files to search. Right now I just
471 run through FileList and pull out all files ending in .sch.
472 Files are saved in list with basename (no path).
474 DebugSpew(Args, "Now in CreateSchemFileList.")
475 SchemFileList = []
476 for File in FileList:
477 # Match *.sch
478 if re.search('\.sch$', File): # re.search matches occurance anywhere
480 # Need to make sure schem file actually exists
481 # There is probably a better way to do this using os.access, but I was
482 # not able to get it to work. . . . .
483 try:
484 TestFile = open(File, "r")
485 except IOError:
486 print("Can't access "+File+" for reading. Exiting . . . .")
487 sys.exit(1)
488 TestFile.close()
490 # Next we need to make sure that this file is not already in the list.
491 if File in SchemFileList:
492 pass
493 else:
494 SchemFileList.append( os.path.basename(File) )
496 return SchemFileList
498 #---------------------------------------------------------------------------
499 def CreateSymbolFileList(SchemFileList, LibraryFileList):
501 This fcn opens each .sch file found and looks for symbol files
502 (typically lurking in lines like "C 32400 53000 1 0 0 resistor-1.sym").
503 When it finds a symbol file, it looks up the file's entire path, and then
504 sticks it in the SymbolFileList.
506 DebugSpew(Args, "Now in CreateSymbolFileList.")
507 SymbolFileList = [] # List starts as empty
509 for SchemFileName in SchemFileList:
510 SchemFile = open(SchemFileName, "r")
511 for line in SchemFile.readlines():
512 # Match component line C 32400 53000 1 . . . . .
513 if re.match('^C ', line):
514 Match = re.match('(C )([\d]+ )([\d]+ )([\d]+ )([\d]+ )([\d]+ )([\d\w\-\./]+)', line)
515 SymFile = Match.group(7)
517 # DebugSpew(Args, "Found "+SymFile+" in schematic "+SchemFileName)
519 # Now find path for symbol file & stick it in list
520 for LibPath in LibraryFileList:
521 AbsSymFileName = os.path.abspath(LibPath+"/"+SymFile)
522 if os.path.isfile(AbsSymFileName):
523 # Insert in list if not already there.
524 if AbsSymFileName in SymbolFileList:
525 pass
526 else:
527 SymbolFileList.append(AbsSymFileName)
529 return SymbolFileList
531 #---------------------------------------------------------------------------
532 def CreateSPICEFileList(Args, SchemFileList):
534 This fcn opens each .sch file found and loops through it.
535 While looping, it looks for SPICE files (typically lurking in lines like
536 "file=/path/to/spice/models/circuit.cir". When it finds a SPICE
537 file, it sticks it in the SPICEFileList.
538 The SPICE file names found are returned as absolute paths.
540 DebugSpew(Args, "Now in CreateSPICEFIleList.")
541 SPICEFileList = [] # List starts as empty
543 SavedLine = []
544 for SchemFileName in SchemFileList:
545 # Open file in user dir.
546 GschemFile = open(Args.UserDir+"/"+os.path.basename(SchemFileName), "r")
547 for Line in GschemFile.readlines():
548 if (re.match('^file=', Line)):
549 Match = re.match('(file=)(\S+)', Line)
550 SPICEFile = Match.group(2)
551 # This needs to be more sophosticated
552 # SPICEFile = os.path.normpath(SPICEFile)
553 SPICEFile = os.path.abspath(SPICEFile)
554 DebugSpew(Args, "Found "+SPICEFile+" in schematic "+SchemFileName)
556 # Next we need to make sure that this file is not already in the list.
557 if SPICEFile in SPICEFileList:
558 pass
559 else:
560 SPICEFileList.append(SPICEFile)
562 return SPICEFileList
564 #---------------------------------------------------------------------------
565 def UpdateSchemFiles(Args, SchemFileList):
567 This fcn opens each .sch file found and loops through it.
568 It stuffs each file line found into a list of lines. While
569 looping, it looks for SPICE files (typically lurking in lines like
570 "file=/path/to/spice/models/circuit.cir". When it finds a SPICE
571 file, it substitutes the line found with "file=./gschem-files/circuit.cir".
572 After running through the file, it closes
573 the file, re-opens it as write-only, and outputs the changed file.
574 Yes, this operation could take place in CreateSPICEFileList, but I thought
575 it better conceptually & architecturally to split it off to a separate fcn.
577 DebugSpew(Args, "Now in UpdateSchemFileList.")
579 SavedLine = []
580 for SchemFileName in SchemFileList:
581 # Open file in user dir.
582 GschemFile = open(Args.ScratchDir+"/"+os.path.basename(SchemFileName), "r")
583 while 1:
584 Line = GschemFile.readline()
585 if not Line:
586 break
587 else:
588 if (re.match('^file=', Line)):
589 Match = re.match('(file=)(\S+)', Line)
590 SPICEFile = Match.group(2)
591 DebugSpew(Args, "Found "+SPICEFile+" in schematic "+SchemFileName)
592 SPICEFile = Args.FileArchiveDir+"/"+os.path.basename(SPICEFile)
593 DebugSpew(Args, "Updating line to point to "+SPICEFile)
594 SavedLine.append("file="+SPICEFile+"\n")
595 else:
596 SavedLine.append(Line)
597 GschemFile.close()
599 # Now write out list in place of file.
600 GschemFile = open(Args.ScratchDir+"/"+os.path.basename(SchemFileName), "w")
601 for Line in SavedLine:
602 GschemFile.write(Line)
604 GschemFile.close()
606 return
608 #---------------------------------------------------------------------------
609 def SaveSymbols(SymFileList, LibraryFileList, ArchiveDirectory):
611 This fcn loops through all symbols in the list,
612 and copies the file into the local
613 archive.
615 DebugSpew(Args, "Now in SaveSymbols.")
616 for SymFileName in SymFileList:
617 DebugSpew(Args, "Saving symbol "+SymFileName+" into archive "+ArchiveDirectory)
618 os.system("cp "+SymFileName+" "+ArchiveDirectory+"/"+os.path.basename(SymFileName) )
620 return
621 #---------------------------------------------------------------------------
622 def SaveSPICEFiles(SPICEFileList, ArchiveDirectory):
624 This fcn loops through all SPICE files in the list, finds the corresponding
625 file somewhere in the directory tree, and then copies the file into the local
626 archive.
628 DebugSpew(Args, "Now in SaveSPICEFiles.")
629 for SPICEFileName in SPICEFileList:
630 DebugSpew(Args, "Saving SPICE file "+SPICEFileName+" into archive "+ArchiveDirectory)
631 os.system("cp "+SPICEFileName+" "+ArchiveDirectory+"/"+os.path.basename(SPICEFileName) )
633 return
635 #---------------------------------------------------------------------------
636 def UpdateRC(Args):
638 This fcn takes the gschemrc and updates it.
639 It runs through the file, and comments out any
640 occurance of (component-library. . . . Then it appends
641 a pointer to the local gschem-files directory
643 DebugSpew(Args, "Now in UpdateRC.")
644 FileName = os.path.basename(Args.GschemRcFileName)
646 # First run through the file, reading the lines and building a list
647 # the lines found.
648 SavedLine = []
649 GschemRCFile = open(FileName, "r")
650 while 1:
651 Line = GschemRCFile.readline()
652 if not Line:
653 break
654 else:
655 if (re.match('^\(component-library', Line)):
656 SavedLine.append(";; "+Line) # Comment out any (component-library lines found
657 else:
658 SavedLine.append(Line)
660 GschemRCFile.close()
662 # Now write out list in place of file.
663 GschemRCFile = open(FileName, "w")
664 for Line in SavedLine:
665 GschemRCFile.write(Line)
667 # Write pointer to new lib into file
668 GschemRCFile.write("(component-library \"./"+Args.FileArchiveDir+"\")\n")
670 GschemRCFile.close()
672 return
674 #---------------------------------------------------------------------------
675 def IsSimpleFile(File):
677 This fcn returns 1 if file is simple file name ("gschemrc"), or is a
678 simple directory name ("gschem-files"). It returns 0 if File is a
679 compound file name ("gschem-files/symbol-1.sym").
681 if (os.path.basename(File) == File):
682 return 1 # Simple file
683 elif (os.path.isdir(File)):
684 return 1 # Directory
685 else:
686 return 0
689 ############################################################################
690 # Body of archiver lives here #
691 ############################################################################
692 def Archive(Args):
694 This is the main archiver. Program algorithm is documented above. Primary
695 data structures are a bunch of lists holding various file names.
697 # First check that ScratchDir is writable by the user. We will CD there
698 # to do real work later.
699 try:
700 TestFile = open(Args.ScratchDir+"/gschem_test", "w")
701 except IOError:
702 print("Can't work in "+Args.ScratchDir+" directory. Check that you have write permission there.")
703 sys.exit(1)
704 else:
705 TestFile.close()
706 os.remove(Args.ScratchDir+"/gschem_test")
708 # Create list of files (and directories) to stick into archive. Returned paths point
709 # to the absolute paths of the files.
710 ArchiveFileList = CreateArchiveFileList(Args)
712 # print
713 # print "ArchiveFileList = ",
714 # print ArchiveFileList
716 # Create list of paths to various library files. Returned paths are absolute path names
717 LibraryPathList = GetLibraryPath(Args)
719 # print
720 # print "LibraryPathList = ",
721 # print LibraryPathList
723 # Create list of schematic files to open and search. Returned paths
724 # give only the base name (i.e. no path)
725 SchemFileList = CreateSchemFileList(Args, ArchiveFileList)
727 # print
728 # print "SchemFileList = ",
729 # print SchemFileList
731 # Now run through SchemFileList and create list of symbols. Symbols are returned
732 # with only base file name (i.e. no path).
733 SymbolFileList = CreateSymbolFileList(SchemFileList, LibraryPathList)
735 # print
736 # print "SymbolFileList = ",
737 # print SymbolFileList
739 # Now run through SchemFileList and create list of pointers to spice files
740 # ("file" attributes). SPICEFiles are returned using absolute paths.
741 SPICEFileList = CreateSPICEFileList(Args, SchemFileList)
743 # print
744 # print "SPICEFileList = ",
745 # print SPICEFileList
747 # Now cd into /tmp dir and copy all files over to /tmp directory for processing.
748 os.chdir(Args.ScratchDir)
750 DebugSpew(Args, "Cd into "+Args.ScratchDir+" for remainder of work.")
751 for File in ArchiveFileList:
752 if (os.path.dirname(File) == Args.UserDir):
753 os.system("cp "+File+" "+Args.ScratchDir)
755 # Now run through SchemFileList and update .sch file by stuffing names
756 # of SPICE files into them. Save the resulting .sch files in the /tmp directory.
757 UpdateSchemFiles(Args,SchemFileList)
759 # Open gschem-files directory and stick symbol & SPICE files into into it
760 try:
761 Dir = NormalizePath(Args.ScratchDir, Args.FileArchiveDir)
762 os.mkdir(Dir)
763 except: # Directory exists.
764 os.system("rm -fR "+Dir) # Remove contents of old dir
765 os.mkdir(Dir) # Replace with new dir.
767 SaveSymbols(SymbolFileList, LibraryPathList, Dir)
768 SaveSPICEFiles(SPICEFileList, Dir)
770 # Now create tar file. We copy remaining files over to /tmp, and then tar them
771 # all up using a local, relative file prefix.
773 # Create string of files to archive
774 ArchiveString = ""
775 for File in ArchiveFileList:
776 # if (os.path.dirname(File) == Args.UserDir):
777 # os.system("cp "+File+" "+Args.ScratchDir)
778 ArchiveString = ArchiveString+" "+os.path.basename(File)
780 DebugSpew(Args, "Files to archive: "+ArchiveString)
782 # Update copy of gschemrc
783 UpdateRC(Args)
785 DebugSpew(Args, "Creating archive in "+Args.ScratchDir+" directory.")
787 # Now use this in tar command.
788 os.system("tar -cf "+Args.OutputFileNameBase+".tar "+ArchiveString)
789 os.system("gzip "+Args.OutputFileNameBase+".tar")
791 # Now try to move completed archive back to user directory.
792 DebugSpew(Args, "Moving archive into local directory.")
793 try:
794 os.stat(Args.UserDir+"/"+Args.OutputFileName)
795 except OSError: # archive is not in user directory yet, no need to force it.
796 os.system("mv "+Args.OutputFileName+" "+Args.UserDir)
797 else: # Directory already exists
798 Input = raw_input(Args.UserDir+"/"+Args.OutputFileName+" already exists. Overwrite? [y/N] ")
799 if ( (len(Input) == 0) or (Input[0] != "y") ):
800 print("Preserving existing archive in local directory.")
801 print("Your new archive lives in "+Args.ScratchDir+"/"+Args.OutputFileName)
802 else:
803 os.system("rm -fR "+Args.UserDir+"/"+Args.OutputFileName) # Remove old archive
804 os.system("mv "+Args.OutputFileName+" "+Args.UserDir)
805 print("gEDA archive "+Args.UserDir+"/"+Args.OutputFileName+" created successfully!")
807 # Clean up remaining mess
808 os.system("rm -fR "+ArchiveString)
809 os.chdir(Args.UserDir)
811 return # End of fcn . . .
813 ############################################################################
814 # Body of extracter lives here #
815 ############################################################################
816 def Extract(Args):
818 This fcn extracts the archive. It tries to do it very carefully, and won't
819 overwrite anything you don't want it to. Algorithm:
820 1. copy archive file into /tmp
821 2. list its contents
822 3. Extract files indivdually, and check that each one is not present in the
823 destination dir before moving it.
825 if (len(Args.CmdLineFileList) == 0):
826 print("Must specify a filename for extraction.")
827 sys.exit(1)
830 for FileName in Args.CmdLineFileList:
831 DebugSpew(Args, "Trying to extract archive "+FileName+".")
833 try:
834 os.stat(FileName)
835 except OSError:
836 print("File "+FileName+" doesn't exist. Ignoring")
837 continue
839 try:
840 os.system("cp -f "+FileName+" "+Args.ScratchDir)
841 except IOError:
842 print("Can't work in the "+Args.ScratchDir+" directory. Check that you have write permisison there.")
843 sys.exit(1)
845 os.chdir(Args.ScratchDir)
847 # Change name of file so it can be gunziped.
848 if not CheckFilename(FileName):
849 print( """
850 Error -- the file suffix is not \".tar.gz\"; garchive can't do extraction.
851 If this archive was created using garchive, you can rename it using
852 .tar.gz as suffix and try again. Otherwise, just gunzip and tar -xvf
853 the file manually.
854 """)
855 sys.exit(1)
857 # Now gunzip the file, then change File name to reflect new status (.tar)
858 os.system("gunzip -f "+FileName)
859 NewFileName = re.sub('\.gz', '', FileName)
861 # Get list of files in archive. Then open up archive
862 ReturnString = commands.getoutput("tar -t -f "+NewFileName)
863 FileList = re.split('\s+', ReturnString)
865 for File in FileList:
866 DebugSpew(Args, "Extracting "+File)
867 os.system("tar -f "+NewFileName+" -x "+File)
869 # We need to treat directories carefully. For each file, check
870 # if it is a simple file, a directory name, or a compound file.
871 for File in FileList:
872 if (IsSimpleFile(File)):
873 try:
874 os.stat(Args.UserDir+"/"+File)
875 except OSError:
876 os.system("mv "+File+" "+Args.UserDir)
877 else:
878 Input = raw_input(Args.UserDir+"/"+File+" already exists. Overwrite? [yN] ")
879 if ( (len(Input) == 0) or (Input[0] != "y") ):
880 print("Preserving existing "+File+" in local directory.")
881 else:
882 os.system("rm -fR "+Args.UserDir+"/"+File)
883 os.system("mv -f "+File+" "+Args.UserDir)
885 # Now clean up /tmp directory
886 os.system("rm -fR "+NewFileName)
887 os.system("rm -fR "+FileName)
888 os.chdir(Args.UserDir)
890 return # End of fcn . . . . .
892 ############################################################################
893 ############################################################################
894 # Main prog begins here #
895 ############################################################################
896 ############################################################################
897 # First get and parse command line args
898 Args = CmdLineArgs() # Creates Args object holding command line args info.
900 if Args.ProgramMode == "archive":
901 Archive(Args)
902 sys.exit(0)
903 elif Args.ProgramMode == "extract":
904 Extract(Args)
905 sys.exit(0)
906 else:
907 raise RuntimeError("Unknown program mode found.")
909 # That's it -- very simple!!