libfossil
fossil-cli.h
Go to the documentation of this file.
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 #if !defined(_NET_FOSSIL_SCM_FOSSILAPP_H_INCLUDED_)
4 #define _NET_FOSSIL_SCM_FOSSILAPP_H_INCLUDED_
5 /*
6  Copyright (c) 2013 D. Richard Hipp
7 
8  This program is free software; you can redistribute it and/or
9  modify it under the terms of the Simplified BSD License (also
10  known as the "2-Clause License" or "FreeBSD License".)
11 
12  This program is distributed in the hope that it will be useful,
13  but without any warranty; without even the implied warranty of
14  merchantability or fitness for a particular purpose.
15 
16  Author contact information:
17  drh@hwaci.com
18  http://www.hwaci.com/drh/
19 
20  *****************************************************************************
21  This file provides a basis for basic libfossil-using apps. It is intended
22  to be used as the basis for test/demo apps, and not necessarily full-featured
23  applications.
24 */
25 
26 /* Force assert() to always work... */
27 #if defined(NDEBUG)
28 #undef NDEBUG
29 #define DEBUG 1
30 #endif
31 #include "fossil-scm/fossil.h"
32 #include <assert.h> /* for the benefit of test apps */
33 #include <stdlib.h> /* EXIT_SUCCESS and friends */
34 
35 /** @page page_fcli fcli (formerly FossilApp)
36 
37  ::fcli (formerly FossilApp) provides a small framework for
38  bootstrapping simple libfossil applications which only need a
39  single fsl_cx instance managing a single checkout and/or
40  repository. It is primarily intended for use with CLI apps
41  implementing features similar to those in fossil(1), but can also
42  be used with GUI apps. It provides the following basic services to
43  applications:
44 
45  - The global ::fcli struct holds global state.
46 
47  - fcli_setup() bootstraps the environment. This must be the
48  first call made into the API, as this replaces the libfossil
49  memory allocator with a fail-fast variant to simplify app-level
50  code a bit (by removing the need to check for OOM errors). This
51  also registers an atexit(3) handler to clean up fcli-owned
52  resources at app shutdown.
53 
54  - Automatically tries to open a checkout (and its associated
55  repository) under the current directory, but not finding one
56  is not an error (the app needs to check for this if it should
57  be an error: use fsl_cx_db_repo() and fsl_cx_db_checkout()).
58 
59  - fcli_flag(), fcli_next_arg(), and friends provide uniform access
60  to CLI arguments.
61 
62  - A (very) basic help subsystem, triggered by the --help or -? CLI
63  flags, or if the first non-flag argument is "help". Applications
64  may optionally assign fcli.appHelp to a function which outputs
65  app-specific help. If help is triggered and the --global boolean
66  flag is passed then framework-level help text will be output,
67  otherwise a hint about the --global option will be output instead.
68 
69  - Basic error reporting mechanism. See fcli_err_report().
70 
71  The source tree contains several examples of using fcli
72  in the files named f-*.c.
73 */
74 
75 /**
76  Ouputs the given printf-type expression (2nd arg) to
77  fcli_printf() if fcli.verbose>=N, else it does nothing.
78 
79  Reminder: this uses a while(0) look so that the macro can end
80  with a semicolon without triggering a warning.
81 */
82 #define FCLI_VN(N,pfexp) \
83  if(fcli.verbose>=(N)) do { \
84  fcli_printf("VERBOSE %d: ", (int)(N)); \
85  fcli_printf pfexp; \
86  } while(0)
87 
88 /**
89  Convenience form of FCLI_VN for level-2 verbosity.
90 */
91 #define FCLI_V2(pfexp) FCLI_VN(2,pfexp)
92 
93 /**
94  Convenience form of FCLI_VN for level-1 verbosity. Levels 1
95  and 2 are intended for application-level use.
96 */
97 #define FCLI_V(pfexp) FCLI_VN(1,pfexp)
98 
99 #if defined(__cplusplus)
100 extern "C" {
101 #endif
102 
103 
104 /**
105  Describes help text for an application flag.
106 */
108  /** "Short" flag, with no leading dashes. */
109  char const * flagShort;
110  /** "Long" flag, with no leading dashes. */
111  char const * flagLong;
112  /** Brief description of value type, e.g. "int" or "string",
113  or 0 if the flag is a boolean (has no value).
114  */
115  char const * valType;
116  /**
117  If not 0 and (this->brief!=0) then this is called to render the
118  help text. It should simply render the text to f_out().
119  */
120  void (*callback)();
121 
122  /**
123  Brief description text.
124  */
125  char const * brief;
126 };
128 
129 /**
130  Structure for holding app-level --help info.
131 */
132 struct fcli_help_t {
133  /** Brief description of what the app does. */
134  char const * briefDescription;
135  /** Brief usage text. It will be prefixed by the
136  app's name. e.g. "options file1 ... fileN".
137  */
138  char const * briefUsage;
139  /**
140  If not NULL, it must include a sentry entry at the end of the
141  list with a NULL for both flagShort and flagLong pointer
142  values.
143  */
145  /**
146  If not 0 then this is called after outputing any flags' help.
147  It should output any additional help text using f_out().
148  */
149  void (*callback)();
150 };
151 typedef struct fcli_help_t fcli_help_t;
152 
153 
154 /**
155  The fcli_t type, accessed by clients via the ::fcli
156  global instance, contains various data for managing a basic
157  Fossil SCM application build using libfossil.
158 
159  Usage notes:
160 
161  - All non-const pointer members are owned and managed by the
162  fcli API. Clients may reference them but must be aware of
163  lifetimes if they plan to hold the reference for long.
164 
165 */
166 struct fcli_t {
167  /**
168  Holds a help function callback which should output any
169  app-specific help. Should be set by client applications BEFORE
170  calling fcli_setup() so that the ::fcli help subsystem can
171  integrate the app. fcli.appName will be set by the time this
172  is called.
173  */
174  void (*appHelp)();
175  /**
176  If not NULL, it must be a pointer to fcli_help_t holding help
177  info for the app. It will be formated and output when --help
178  is triggered, in place of appHelp.
179  */
181  /**
182  The shared fsl_cx instance. It gets initialized by
183  fcli_setup() and cleaned up post-main().
184 
185  this->f is owned by this object and will be cleaned up at app
186  shutdown (post-main).
187  */
189  /**
190  A verbosity level counter. Starts at 0 (no verbosity) and goes
191  up for higher verbosity levels. Currently levels 1 and 2 are intended
192  for app-level use and level 3 for library-level use.
193  */
194  int verbose;
195  /**
196  If not NULL then fcli_setup() will attempt to open the
197  checkout for the given dir, including its associated repo
198  db. By default this is "." (the current directory).
199 
200  Applications can set this to NULL _before_ calling
201  fcli_setup() in order to disable the automatic attemp to
202  open a checkout under the current directory. Doing so is
203  equivalent to using the --no-checkout|-C flags.
204  */
205  char const * checkoutDir;
206  /**
207  The current list of CLI arguments. This list gets modified
208  by fcli_flag() and friends.
209  */
210  char ** argv;
211  /**
212  Current number of items in this->argv.
213  */
214  int argc;
215  /**
216  Application's name. Currently argv[0] but needs to be
217  adjusted for Windows platforms.
218  */
219  char const * appName;
220  /**
221  True if the --dry-run flag is seen during initial arguments
222  processing. ::fcli does not use this itself - it is
223  intended as a convenience for applications. Those which want
224  to support a short-form flag can implement that like this:
225 
226  @code
227  char dryRun = fcli.fDryRun ? fcli.fDryRun :
228  fcli_flag("n", NULL);
229  @endcode
230 
231  */
232  char fDryRun;
233  /**
234  Transient settings and flags. These are bits which are used
235  during (or very shortly after) fcli_setup() but have no effect
236  if modified after that.
237  */
238  struct {
239  /**
240  fsl_malloc()'d repo db name string made by args processing.
241  */
242  char * repoDbArg;
243  /**
244  Set to true if fcli_setup() detects -? or --help in the
245  argument list, or if the first non-flag argument is "help".
246  */
248  /**
249  Don't use this - it will likely go away. The following text
250  was at some point true but is currently a lie:
251 
252  If the --gmt flag is found, this is set to true. That
253  causes the FSL_CX_F_LOCALTIME_GMT flag to be set on this->f.
254  */
255  char gmtTime;
256  } transient;
257  /**
258  Holds bits which can/should be configured by clients BEFORE
259  calling fcli_setup().
260  */
261  struct {
262  /**
263  Whether or not to enable fossil's SQL tracing.
264  This should start with a negative value, which helps
265  fcli_process_args() process it. Setting this after
266  initialization has no effect.
267  */
268  int traceSql;
269  /**
270  This output channel is used when initializing this->f. The
271  default implementation uses fsl_outputer_FILE to output to
272  stdout.
273  */
275 
276  } config;
277 };
278 typedef struct fcli_t fcli_t;
279 
280 /** @var fcli
281 
282  This fcli_t instance is intended to act as a singleton. It
283  holds all fcli-related global state. It gets initialized with
284  default/empty state at app startup and gets fully initialized
285  via fcli_setup().
286 
287  Application startup with this API typically looks like:
288 
289  @code
290  // from early on in main():
291  int rc;
292  fcli.appHelp = fcli_local_help;
293  rc = fcli_setup(argv, argc);
294  if(FSL_RC_BREAK==rc) return 0; // --help|-?|help
295  else if(rc) goto end;
296 
297  // Else proceed. At the end do:
298 
299  end:
300  return (fcli_err_report(0)||rc) ? EXIT_FAILURE : EXIT_SUCCESS;
301 
302  @endcode
303 
304  fcli.f holds the API's fsl_cx instance. It will be non-NULL (but
305  might not have an opened checkout/repository) if fsl_setup()
306  succeeds.
307 */
309 
310 /**
311  Should be called early on in main(), passed the arguments passed
312  to main(). Returns 0 on success. Sets up the ::fcli instance
313  and opens a checkout in the current dir by default.
314 
315  MUST BE CALLED BEFORE fsl_malloc() and friends are used, as this
316  swaps out the allocator with one which aborts on OOM.
317 
318  If argument processing finds either of the (--help, -?) flags,
319  or the first non-flag argument is "help", it sets
320  fcli.transient.helpRequested to a true value, calls fcli_help(),
321  and returns FSL_RC_BREAK, in which case the application should
322  exit/return from main with code 0 immediately.
323  fcli.transient.helpRequested is set to 1 if --help or -? are
324  seen, 2 if "help" is the first non-flag argument, so clients can
325  (if they care to) further distinguish between the two, e.g. by
326  checking for a command after "help" in the latter case and
327  showing command-specific help.
328 
329  Returns 0 on success. Errors other than FSL_RC_BREAK should be
330  treated as fatal to the app, and fcli.f's error state
331  _might_ contain info about the error.
332 */
333 FSL_EXPORT int fcli_setup(int argc, char * const * argv );
334 
335 /**
336  Returns the libfossil context associated with the fcli API.
337 */
339 
340 /**
341  Calls fcli_local_help() then displays global help options
342  to stdout.
343 */
344 FSL_EXPORT void fcli_help();
345 
346 /**
347  Works like printf() but sends its output to fsl_outputf() using
348  the fcli.f fossil conext (if set) or fsl_fprintf() (to
349  stdout).
350 */
351 FSL_EXPORT void fcli_printf(char const * fmt, ...)
352 #if 0
353 /* Would be nice, but complains about our custom format options: */
354  __attribute__ ((__format__ (__printf__, 1, 2)))
355 #endif
356  ;
357 
358 /**
359  f_out() is a shorthand for fcli_printf().
360 */
361 #define f_out fcli_printf
362 
363 /**
364  Returns the verbosity level set via CLI args. 0 is no verbosity.
365 */
367 
368 /**
369  Searches fcli.argv for the given flag (pass it without leading
370  dashes). If found, this function returns true, else it returns
371  false. If value is not NULL then the flag, if found, is assumed to
372  have a value, otherwise the flag is assumed to be a boolean. A flag
373  with a value may take either one of these forms:
374 
375  -flag=value
376  -flag value
377 
378  *value gets assigned to a COPY OF value part of the first form or a
379  COPY OF the subsequent argument for the second form (copies are
380  required in order to avoid trickier memory management here). On
381  success it removes the flag (and its value, if any) from
382  fcli.argv. Thus by removing all flags early on, the CLI
383  arguments are left only with non-flag arguments to sift through.
384 
385  If it returns a string, the caller must eventually free it with
386  fsl_free().
387 
388  Flags may start with either one or two dashes - they are
389  equivalent.
390 */
391 FSL_EXPORT char fcli_flag(char const * opt, char ** value);
392 
393 /**
394  Works like fcli_flag() but tries two argument
395  forms, in order. It is intended to be passed short and long
396  forms, but can be passed two aliases or similar.
397 
398  @code
399  char * v = NULL;
400  fcli_flag2("n", "limit", &v);
401  if(v) { ...; fsl_free(v); }
402  @endcode
403 */
404 FSL_EXPORT char fcli_flag2(char const * opt1, char const * opt2,
405  char ** value);
406 
407 /**
408  Works similarly to fcli_flag2(), but if no flag is found and
409  value is not NULL then *value is assigned to the return value of
410  fcli_next_arg(1). In that case:
411 
412  - The return value will specify whether or not fcli_next_arg()
413  returned a value or not.
414 
415  - If it returns true then *value is owned by the caller and it
416  must eventually be freed using fsl_free().
417 
418  - If it returns false, *value is not modified.
419 
420  The opt2 parameter may be NULL, but op1 may not.
421 */
422 FSL_EXPORT char fcli_flag_or_arg(char const * opt1, char const * opt2,
423  char ** value);
424 
425 
426 /**
427  Clears any error state in fcli.f.
428 */
430 
431 /**
432  Sets fcli.f's error state, analog to fsl_cx_err_set().
433  Returns the code argument on success, some other non-0 value on
434  a more serious error (e.g. FSL_RC_OOM when formatting the
435  string).
436 */
437 FSL_EXPORT int fcli_err_set(int code, char const * fmt, ...);
438 
439 /**
440  Returns the internally-used fsl_error instance which is used for
441  propagating errors. The object is owned by ::fcli and MUST NOT
442  be freed or otherwise abused by clients. It may, however, be
443  passed to routines which take a fsl_error parameter to report
444  errors (e.g. fsl_deck_output().
445 
446  Returns NULL if fcli_setup() has not yet been called or after
447  fcli has been cleaned up (post-main()).
448 
449 */
451 
452 
453 /**
454  If ::fcli has any error state, this outputs it and returns the
455  error code, else returns 0. If clear is true the error state is
456  cleared/reset, otherwise it is left in place. Returns 0 if
457  ::fcli has not been initialized. The 2nd and 3rd arguments are
458  assumed to be the __FILE__ and __LINE__ macro values of the call
459  point. See fcli_err_report() for a convenience form of this
460  function.
461 
462  The format of the output depends partially on fcli_is_verbose(). In
463  verbose mode, the file/line info is included, otherwise it is
464  elided.
465 
466  @see fcli_err_report()
467 */
468 FSL_EXPORT int fcli_err_report2(char clear, char const * file, int line);
469 
470 /**
471  Convenience macro for using fcli_err_report2().
472 */
473 #define fcli_err_report(CLEAR) fcli_err_report2((CLEAR), __FILE__, __LINE__)
474 
475 /**
476  Peeks at or takes the next argument from the CLI args.
477  If take is true, it is removed from the args list and transfered
478  to the caller (who must fsl_free() it), else ownership is not
479  modified.
480 */
481 FSL_EXPORT char * fcli_next_arg(char take);
482 
483 /**
484  If fcli.argv contains what looks like any flag arguments,
485  this updates the fossil error state and returns true, else
486  returns false. If outputError is true and an unused flag is
487  found then the error state is immediately output (but not
488  cleared).
489 */
490 FSL_EXPORT char fcli_has_unused_flags(char outputError);
491 
492 /**
493  Typedef for general-purpose fcli call-by-name commands.
494 
495  @see fcli_dispatch_commands()
496 */
497 typedef int (*FossilCommand_f)();
498 
499 /**
500  Describes a named callback command.
501 
502  @see fcli_dispatch_commands()
503 */
505  /** The name of the command. */
506  char const * name;
507  /** The callback for this command. */
509 };
511 
512 /**
513  Expects an array of FossilCommands which contain a trailing
514  sentry entry with a NULL name and callback. It searches the list
515  for a command matching fcli_next_arg(). If found, it
516  removes that argument from the list, calls the callback, and
517  returns its result. If no command is found FSL_RC_NOT_FOUND is
518  returned, the argument list is not modified, and the error state
519  is updated with a description of the problem and a list of all
520  command names in cmdList.
521 
522  If reportErrors is true then on error this function outputs
523  the error result but it keeps the error state in place
524  for the downstream use.
525 */
526 FSL_EXPORT int fcli_dispatch_commands( FossilCommand const * cmdList,
527  char reportErrors);
528 
529 /**
530  A minor helper function intended to be passed the pending result
531  code of the main() routine. This function outputs any pending
532  error state in fcli. Returns one of EXIT_SUCCESS if mainRc is 0
533  and fcli had no pending error report, otherwise it returns
534  EXIT_FAILURE. This function does not clean up fcli - that is
535  handled via an atexit() handler.
536 
537  It is intended to be called once at the very end of main:
538 
539  @code
540  int main(){
541  int rc;
542 
543  ...set up fcli...assign rc...
544 
545  return fcli_end_of_main(rc);
546  }
547  @endcode
548 
549  @see fcli_error()
550  @see fcli_err_set()
551  @see fcli_err_report()
552  @see fcli_err_reset()
553 */
554 FSL_EXPORT int fcli_end_of_main(int mainRc);
555 
556 #if defined(__cplusplus)
557 } /*extern "C"*/
558 #endif
559 
560 
561 #endif
562 /* _NET_FOSSIL_SCM_FOSSILAPP_H_INCLUDED_ */
FSL_EXPORT void fcli_printf(char const *fmt,...)
Works like printf() but sends its output to fsl_outputf() using the fcli.f fossil conext (if set) or ...
char helpRequested
Set to true if fcli_setup() detects -? or –help in the argument list, or if the first non-flag argu...
Definition: fossil-cli.h:247
Structure for holding app-level –help info.
Definition: fossil-cli.h:132
The main Fossil "context" type.
char const * appName
Application's name.
Definition: fossil-cli.h:219
Describes a named callback command.
Definition: fossil-cli.h:504
FSL_EXPORT int fcli_err_report2(char clear, char const *file, int line)
If fcli has any error state, this outputs it and returns the error code, else returns 0...
char const * briefUsage
Brief usage text.
Definition: fossil-cli.h:138
fsl_cx * f
The shared fsl_cx instance.
Definition: fossil-cli.h:188
struct fcli_t::@1 config
Holds bits which can/should be configured by clients BEFORE calling fcli_setup(). ...
An interface which encapsulates data for managing an output destination, primarily intended for use w...
Definition: fossil-util.h:325
A container for storing generic error state.
Definition: fossil-util.h:692
fcli_help_arg_t const * flags
If not NULL, it must include a sentry entry at the end of the list with a NULL for both flagShort and...
Definition: fossil-cli.h:144
FSL_EXPORT char fcli_has_unused_flags(char outputError)
If fcli.argv contains what looks like any flag arguments, this updates the fossil error state and ret...
#define FSL_EXPORT
Definition: fossil-config.h:19
char fDryRun
True if the –dry-run flag is seen during initial arguments processing.
Definition: fossil-cli.h:232
fcli_help_t const * appHelp2
If not NULL, it must be a pointer to fcli_help_t holding help info for the app.
Definition: fossil-cli.h:180
Describes help text for an application flag.
Definition: fossil-cli.h:107
fsl_outputer outputer
This output channel is used when initializing this->f.
Definition: fossil-cli.h:274
FSL_EXPORT void fcli_help()
Calls fcli_local_help() then displays global help options to stdout.
int traceSql
Whether or not to enable fossil's SQL tracing.
Definition: fossil-cli.h:268
FSL_EXPORT int fcli_end_of_main(int mainRc)
A minor helper function intended to be passed the pending result code of the main() routine...
FSL_EXPORT int fcli_dispatch_commands(FossilCommand const *cmdList, char reportErrors)
Expects an array of FossilCommands which contain a trailing sentry entry with a NULL name and callbac...
int verbose
A verbosity level counter.
Definition: fossil-cli.h:194
int argc
Current number of items in this->argv.
Definition: fossil-cli.h:214
char const * name
The name of the command.
Definition: fossil-cli.h:506
FSL_EXPORT int fcli_err_set(int code, char const *fmt,...)
Sets fcli.f's error state, analog to fsl_cx_err_set().
FSL_EXPORT fsl_cx * fcli_cx()
Returns the libfossil context associated with the fcli API.
char * repoDbArg
fsl_malloc()'d repo db name string made by args processing.
Definition: fossil-cli.h:242
FSL_EXPORT int fcli_is_verbose()
Returns the verbosity level set via CLI args.
void(* appHelp)()
Holds a help function callback which should output any app-specific help.
Definition: fossil-cli.h:174
char const * flagShort
"Short" flag, with no leading dashes.
Definition: fossil-cli.h:109
FSL_EXPORT void fcli_err_reset()
Clears any error state in fcli.f.
char const * valType
Brief description of value type, e.g.
Definition: fossil-cli.h:115
int(* FossilCommand_f)()
Typedef for general-purpose fcli call-by-name commands.
Definition: fossil-cli.h:497
char gmtTime
Don't use this - it will likely go away.
Definition: fossil-cli.h:255
void(* callback)()
If not 0 and (this->brief!=0) then this is called to render the help text.
Definition: fossil-cli.h:120
char const * brief
Brief description text.
Definition: fossil-cli.h:125
char const * checkoutDir
If not NULL then fcli_setup() will attempt to open the checkout for the given dir, including its associated repo db.
Definition: fossil-cli.h:205
FSL_EXPORT char fcli_flag2(char const *opt1, char const *opt2, char **value)
Works like fcli_flag() but tries two argument forms, in order.
FSL_EXPORT char fcli_flag(char const *opt, char **value)
Searches fcli.argv for the given flag (pass it without leading dashes).
FSL_EXPORT char fcli_flag_or_arg(char const *opt1, char const *opt2, char **value)
Works similarly to fcli_flag2(), but if no flag is found and value is not NULL then *value is assigne...
char const * flagLong
"Long" flag, with no leading dashes.
Definition: fossil-cli.h:111
FSL_EXPORT fsl_error * fcli_error()
Returns the internally-used fsl_error instance which is used for propagating errors.
The fcli_t type, accessed by clients via the fcli global instance, contains various data for managing...
Definition: fossil-cli.h:166
char const * briefDescription
Brief description of what the app does.
Definition: fossil-cli.h:134
FSL_EXPORT fcli_t fcli
This fcli_t instance is intended to act as a singleton.
Definition: fossil-cli.h:308
char ** argv
The current list of CLI arguments.
Definition: fossil-cli.h:210
void(* callback)()
If not 0 then this is called after outputing any flags' help.
Definition: fossil-cli.h:149
FSL_EXPORT int fcli_setup(int argc, char *const *argv)
Should be called early on in main(), passed the arguments passed to main().
FossilCommand_f f
The callback for this command.
Definition: fossil-cli.h:508
FSL_EXPORT char * fcli_next_arg(char take)
Peeks at or takes the next argument from the CLI args.