First a note on the copyright. This is the same one used by the X Consortium (fortunately no one has started copyrighting copyrights), so if your lawyers don't mind that one, they shouldn't mind this one. Simply put, you can do what you want with this, although if you are so inclined we'd appreciate it if you sent us back any enhancements, bug fixes, or other related material, so we can make it available to everyone else. But if you don't want to, you don't have to. So be it. So. What's here? It's an implementation of the Message Catalog System, as described in the X/Open Portability Guide (XSI Supplementary Definitions, X/Open Company, Ltd, Prentice Hall, Englewood Cliffs, New Jersey 07632, ISBN: 0-13-685850-3). Included is a version of gencat, to generate message catalogs, as well as the routines catgets, catopen, and catclose. There is also the beginings of an X/Open compliant set of print routines, but we'll talk about those later. I haven't done a man page yet (sorry, but I've got a product to get out the door, the pretty stuff has to come later). However you can use the definitions in the X/Open docs and it should all work. I have, however, added a series of pretty significant enhancements, particularly to gencat. As follows: Use: gencat [-new] [-or] [-lang C|C++|ANSIC] catfile msgfile [-h <header-file>]... This version of gencat accepts a number of flags. -new Erase the msg catalog and start a new one. The default behavior is to update the catalog with the specified msgfile(s). This will instead cause the old one to be deleted and a whole new one started. -h <hfile> Output identifiers to the specified header files. This creates a header file with all of the appropriate #define's in it. Without this it would be up to you to ensure that you keep your code in sync with the catalog file. The header file is created from all of the previous msgfiles on the command line, so the order of the command line is important. This means that if you just put it at the end of the command line, all the defines will go in one file gencat foo.m bar.m zap.m -h all.h If you prefer to keep your dependencies down you can specify one after each message file, and each .h file will receive only the identifiers from the previous message file gencat foo.m -h foo.h bar.m -h bar.h zap.m -h zap.h As an added bonus, if you run the following sequence: gencat foo.m -h foo.h gencat foo.m -h foo.h the file foo.h will NOT be modified the second time. gencat checks to see if the contents have changed before modifying things. This means that you won't get spurious rebuilds of your source everytime you change a message. You can thus use a Makefile rule such as: MSGSRC=foo.m bar.m GENFLAGS=-or -lang C GENCAT=gencat NLSLIB=nlslib/OM/C $(NLSLIB): $(MSGSRC) @for i in $?; do cmd="$(GENCAT) $(GENFLAGS) $@ $$i -h `basename $$i .m`.H"; echo $$cmd; $$cmd; done foo.o: foo.h The for-loop isn't too pretty, but it works. For each .m file that has changed we run gencat on it. foo.o depends on the result of that gencat (foo.h) but foo.h won't actually be modified unless we changed the order (or added new members) to foo.m. (I hope this is clear, I'm in a bit of a rush.) -lang <l> This governs the form of the include file. Currently supported is C, C++ and ANSIC. The latter two are identical in output. This argument is position dependent, you can switch the language back and forth inbetween include files if you care to. -or This is a hack, but a real useful one. MessageIds are ints, and it's not likely that you are going to go too high there if you generate them sequentially. catgets takes a msgId and a setId, since you can have multiple sets in a catalog. What -or does is shift the setId up to the high end of a long, and put the msgId in the low half. Assuming you don't go over half a long (usually 2 bytes nowadays) in either your set or msg ids, this will work great. Along with this are generated several macros for extracting ids and putting them back together. You can then easily define a macro for catgets which uses this single number instead of the two. Note that the form of the generated constants is somewhat different here. Take the file aboutMsgs.m $ aboutMsgs.m $ OmegaMail User Agent About Box Messages $ $set 4 #OmAbout $ About Box message and copyrights $ #Message # Welcome to OmegaMail(tm) $ #Copyright # Copyright (c) 1990 by Alphalpha Software, Inc. $ #CreatedBy # Created by: $ #About # About... # A # # $ #FaceBitmaps # /usr/lib/alphalpha/bitmaps/%s Here is the the output from: gencat foo aboutMsgs.m -h foo.h #define OmAboutSet 0x4 #define OmAboutMessage 0x1 #define OmAboutCopyright 0x2 #define OmAboutCreatedBy 0x3 #define OmAboutAbout 0x4 #define OmAboutFaceBitmaps 0x8 and now from: gencat -or foo aboutMsgs.m -h foo.h /* Use these Macros to compose and decompose setId's and msgId's */ #ifndef MCMakeId # define MCMakeId(s,m) (unsigned long)(((unsigned short)s<<(sizeof(short)*8))\ |(unsigned short)m) # define MCSetId(id) (unsigned int) (id >> (sizeof(short) * 8)) # define MCMsgId(id) (unsigned int) ((id << (sizeof(short) * 8))\ >> (sizeof(short) * 8)) #endif #define OmAboutSet 0x4 #define OmAboutMessage 0x40001 #define OmAboutCopyright 0x40002 #define OmAboutCreatedBy 0x40003 #define OmAboutAbout 0x40004 #define OmAboutFaceBitmaps 0x40008 Okay, by now, if you've read the X/Open docs, you'll see I've made a bunch of other extensions to the format of the msg catalog as well. Note that you don't have to use any of these and, with one exception, they are all compatible with the standard format. $set 4 #OmAbout In the standard the third argument is a comment. Here if the comment begins with a # then it is used to generate the setId constant (with the word "Set" appended). This constant is also prepended onto all of the msgId constants for this set. Anything after the first token is treated as a comment. $ #Message As with set, I've modified the comment to indicate an identifier. There are cleaner ways to do this, but I was trying to retain a modicom of compatibility. The identifier after # will be retained and used as the identifier for the next message (unless overridden before we get there). If a message has no previous identifier then no identifier is generated in the include file (I use this quite a bit myself, the first identifier is a Menu item, the next three are accelerator, accelerator-text and mnemonic - I don't need identifiers for them, I just add 1, 2 and 3). # Welcome to OmegaMail(tm) Finally the one incompatible extension. If a line begins with # a msgId number is automatically generated for it by adding one to the previous msgId. This wouldn't have been useful in the standard, since it didn't generate include files, but it's wonderful for this version. It makes it easy to reorder the message file to put things where they belong and not have to worry about renumber anything (although of course you'll have to recompile). That's about all for that. Now, what about the print routines? These are embarassing. They are a first pass. They support only %[dxo] and %[s], although they support *all* of the modifiers on those arguments (I had no idea there were so many!). They also, most importantly, support the position arguments that allow you to reference arguments out of order. There's a terrible hack macro to handle varargs which I wrote because I wasn't sure if it was okay to pass the address of the stack to a subroutine. I've since seen supposedly portable code that in fact does this, so I guess it's okay. If that's the case the code could become a lot simpler. I welcome anyone who would like to fix it up. I just don't know when I'll get the chance; it works, it's just ugly. One last comment. You probably want to know how reliable it is. I've tested the print routines pretty well. I've used the msgcat routines intensely, but I haven't exercised all of the options in the message catalog file (like all of the \ characters) although I have implemented them all. I'm pretty confident that all the basic stuff works, beyond that it's possible that there are bugs. As for portability, I've run it under BSD4.3 (Apollo) and SYSV-hybrid (SCO). (And I never want to see the words "System V with BSD extensions" again in my life.) I don't believe that there are any heavy dependencies on Unix, although using another system would probably require #ifdef's. I apologize for the state of the documentation, the lack of comments, the lack of testing, and all of the other things. This project is subsidiary to my primary goal (Graphical Email for Unix) and I'm afraid I haven't been able to spend the time on it that I would have liked. However I'll happily answer any questions that you may have, and will be glad to serve as a distribution point for future revisions. So if you make any changes or add more X/Open functions, send me a copy and I'll redistribute them. Best of luck! Kee Hinckley September 12, 1990 Bugs: There is currently one known bug. Updating an existing message catalog sometimes seems to not generate complete header files. If in doubt, delete the catalog and regenerate the whole thing. Kee - 02-12-92 Note, I've just applied a patch from <Jan.Djarv@sa.erisoft.se> that may fix this problem. 03-12-92