#!/usr/local/bin/mk # $Id: Template.m,v 8.3 2000/07/30 16:43:44 ksb Exp $ # $Compile(*): %b %o -mMkcmd %f && %b %o -m%m prog.c && mv -i prog %F # $Mkcmd(*): ${mkcmd-mkcmd} std_version.m std_help.m %f # include some C declarations we must have (kept as a unique list) #from '"file.h"' #from '' # If we require another option description (after us, but before the # next thing on the command line) # We can require "file.mc" (file.mh, file.mi) for meta C code. # a percent in column 1 forces expansion of any line, others are plain text. # The best interface is to require an api.m and let it import meta files. require "std_version.m" "std_help.m" %hi #if !defined(MY_INCLUDE_ONCE_TOKEN) /* copy macros and typedefs to our header file, and our own include sections */ #define MY_INCLUDE_ONCE_TOKEN 1 #endif %% # Change any API keys you need to change. # Some templates we require might use an API key to set variable or # function names, we can override or add to these keys. #key "whatever" 1 initializer { # "value11" 2 "value4" #} #augment key "someother" 2 { # "on" "the" "end" #} # put in all the names we are know be, the `default' name should # have no options forced. This other will force one (or two) options # preferably capital options like: # basename "man" "" # basename "whatis" "-f" # basename "apropos" "-k" # basename otherwise "-X" # # name forced options basename "prog" "" basename "a.out" "-b" # rename some global variables if you need to: #terse "u_terse" #usage "Usage" #vector "u_help" #routine "main" #named "progname" # %b # change how default names are created #template "%s_opt" #prototype "%s_func" # set mutually exclusive options (-i and -j) #exclude "ij" # read options from an environment variable #getenv "PROG" # never do this, allow dash options to mix with arguments (yucky results) #mix # The 4 lines of code below are support for -V from std_version.m, # set rcsid to the string -V should output. # More information could be added with #augment action 'V' { # user "..." #} %i static char rcsid[] = "$Id: Template.m,v 8.3 2000/07/30 16:43:44 ksb Exp $"; %% # Setup any external processors (level 1 to 10) # default is 5, odds for drivers, evens for templates and libs #initializer "tcl_init(...);" #init 'cmd_init(& CSUser, aCMList, sizeof(CMList)/sizeof(CMD), "prompt> ", 3);' init "init_facility();" before { # calls `before_func()' to setup data structs # you get to code this, or leave this out } # for a boolean option `-b' eith select `boolean' or `toggle' # booleans lock to the non-default value, toggle flip every time boolean 'b' { named "fFlag" help "a single letter flag" } # for other data type (integer, letter, char*, buffer(string), file) # select one of these # integer unsigned long letter # string[100] char* double accumulate[":"] # file["r"] fd["open-opts, mode"] integer 'i' { help "set iComamnd to stuff" named "iCommand" # %n %N or named "buffer" "unconverted" parameter "stuff" # %p for usage diagram, as "[name]" for noparam # aborts "C-code-opt" # exit(1), or run C-code to exit # from "include" # buffer is declared in this file # exclude "opts" # options "opts" can't be given in combo. # once # I exclude myself # noparam "C-code" # if no parameter given, must put param in [] # # C-code sets the _unconverted_ value # hidden # don't show in -h # keep "unconverted" # if you don't give a name you can still keep # initializer [style] "value" # set an initial value # # styles are getenv, dynamic, or static # local # use a variable local to main() # global # not local # static # set storage class in C to static # stops # force an imaginary `--' after us # ends "C-opt" # like an abort option, but successful # type 'opt' { ... } # allow this option after me # track "variable-opt" # was this option provided? # update "C-code" # use this code to convert # user "C-code" # after conversion run me # verify "C-code-opt" # check input data on cmd line # before "C-code" # up front init code # after "C-code" # cleanup C code for this option/variable # late # convert option after all dash options # # very useful for files and sockets # key [version] [initializer] { ... } # # %ZK... this trigger's unnamed key # key "name" ['short'] [version] [initializer] { ... } # # %K or %Ks where short is a character # # requires "module" # import module for support # mkcmd-type named "name" ["keep"] { ... } # # declare a local conversion buffer # # if we are a file or fd type we can construct a read routine # if we are a char*, void*, string (or function!) we can construct a # routine that does "stateful inspection" as data is passed to it. # routine "Name" "acBuffer" { # function name (%mI) and buffer (%mG) # for this routine: # static # this function is static # local/global # hide (show) terse and vector # prototype "u_%mW_%mI" # %mP pattern constructs function names # terse "u_terse_%n" # %mT (char *[]) of terse hunk help # track "iNum" # %mN (int) line number of file # vector "u_vector_%n" # %mV (char **[]) of verbose hunk help # usage "name" # %mU (void (fp, hunk)()) to print help # a hunk of 0 is the whole file spec # for each hunk: # aborts "C-code" # %me if line fails conversion run this # after "C-code" # %ma do this after this hunk # before "C-code" # %mb do this before this hunk # comment "string" # %m# comments lead with this (to \n) # help "text" # %mh manual page help # named "func" # %mn C function to call on each line # list "v1" "sep" "v2"... # %m the line has these columns # "sep" might be an INTEGER for fixed widths # a leading "sep" is optional to skip stuff # update # %mc special update to convert line # user # %mu user code after conversion # stops # make a new hunk w/o a clean ends # ends count # hunk is count lines long (> 0) # ends "token" # %mt this string ends the hunk # # (and make a new hunk or return) # computed escapes # # %mf format width for names (in help table) # # %ml converted buffers as paramlist ("pcLogin, acEPass, iUid") # # %mL parameter names as in %ml ("login, epass, uid, gid") # # %mj total cols # # %mJ which column are we in? # # %mw total hunks # # %mW which hunk is current? # # %mz fail here and now! # convenient: # # %mg option that encloses us # # %M... go back to first hunk # # %mx... skip forward one hunk per "x" # # Repeat as needed after a stops (or ends) to read more data from # the file (a user code "%mA" will also pass safely to the next hunk # reader but may miss an "after" action). # Mkcmd passes to the next only on the ends token (or count). # } } # Build your own type. Leave the "base" flag on if the type you are # building a conversion from an unconverted character string to the type. # If it takes the original type (converted) and does something to it # then take the "base" flag off. # #integer type "octal" base key-spec-clients { # # same attributes as above PLUS these type attributes: # type update "C-state" # a conversion statement to set %n from %N # type init "C-expr" # an initial value of the type # type dynamic # static inits won't cut it, use update to set # type comment "text" # insert this in the manual page # type keep # all clients need a keep (%N), please # type help "text" # help for the usage function # type param "word" # mnemonic for the new type # type verify "C-state" # code to check %N for a "good" format/value # # key 1 init { # # force this key to init the client unamed keys # } # Use a @foreach "key-spec-clients" to itterate through the client triggers # thatuse your type to declare/define buffers and such. #} # for special actions that happen _as_ the option is parsed # use on of: # action (for no parameter, like -h, or -V) # function (for things that have a parameter, like cu -s) action 'Q' { # update calls Q_func() # user does nothing aborts "exit(7);" help "quit the program with exit value 7" } # for things like nice(1) that take -integer number { named "iValue" param "value" user 'printf("%%d\\n", %n);' help "what to do with digits" } # for things like `vi +lineno file' # (the "+" is optional as well as the default value) escape "+" char* { named "pcText" param "offset" help "lseek offset as a string" } ### part two -- look at the rest of the line: after { # like before, calls after_func(), or rename it: # named "junk" } # just like init lists (level 1 to 10) after 5 "/* sanity check input to this point */" # stuff about left/right here # put in buffers for all the fix position data (either looking # from the left or right sise of the command line). # a buffer for an integer looks like: #integer variable "iName" { # param "myname" # help "message for -h" #} # then give the order on the command line from the left # note the special escape prefixes "%1" "%2" ... # to get to list in the declaration. (%1 for example) #left "iName" "acName2" [ "acOpt" ] { #} # and/or #right "iOther" { #} # list can convert the rest of the argv array to any type, or pass uncvt'd #list integer { # named "piList" "ppcList" # ... #} # put on one of the types (from the integer hunk above) here # every double { ... # to get a different conversion every function { # calls every_func(char *pcData) for each command line value param "sticky-fruits" help "not bananas" } zero { # calls zero_func() if we didn't have any `every's } # just like init lists (level 10 to 1) cleanup "/* shutdown the stuff we started */" exit { # call exit_func() to clean up #aborts "C-code" # usual exit declaration #after "C-not-reached" # usually a comment "/*NOTREACHED*/" } %c /* now we are in C code... */ /* code the functions you mentioned above, or * extern them (or #include the externs) */ init_facility() { printf("init something\n"); } before_func() { printf("before\n"); } after_func() { printf("after\n"); } every_func(pcData) char *pcData; { static int iCount = 0; printf("every %s (%d)\n", pcData, ++iCount); } exit_func() { printf("exit (cleanup)\n"); } zero_func() { printf("zero args\n"); } Q_func() { printf("Q to you too\n"); } %%