#include #include #include #include "../../libmodules/guruengine/guruengine.h" #include #include #include // #Specbeg: guru samples / basic gurupath /* The guruengine relies on the gurupath component. All other components (gurusteps, guruiter) are built on top of gurupath and present the same logic. A gurus is presented as a graph. Every node in the graph presents some dialog. The dialog may be only informative or may contains some input fields. Each node has a validation function (eval) and a presentation function (dialog). Further, if there are potentially other node to visit after a given one, then the various node are attached to the path1, path2, ..., path6 tags. If there are no path tag used, then the node represents the end of a process, the end of a branch of the graph. If there is a path1 tag and no path2, it means there is only one possible path, so the next node is presented on the same horizontal line as the current one. If there are two or more path tags (path1, path2, ...), then the next nodes represent the start of independant branches in the graph. The following example, presents a graph with two branches */ static void samples_gurupath() { SSTRING str; SSTRING lmethod,umethod; SSTRING lextra,uextra; int ret = ("gurupath" ,"This is samples_gurupath()\n" "It offers two ways of searching a string",help_nil); (mode,status); settitle ("Some name to search"); const char *s = glocal.str.get(); status.is_filled = s[0] != '\0'; status.some_errors = status.is_filled && !isalpha(s[0]); setintro ("Enter a string to search"); dia.newf_str ("A string",glocal.str); edit (); (mode,status); settitle ("Lower case search method"); status.is_filled = !glocal.lmethod.is_empty(); status.is_possible = glocal.str.cmp("Z") > 0; dia.newf_str ("How to search",glocal.lmethod); edit(); (mode,status); settitle ("Lower case extra strategy"); status.is_filled = !glocal.lextra.is_empty(); dia.newf_str ("Lower case extra",glocal.lextra); edit(); (mode,status); settitle ("Upper case search method"); status.is_filled = !glocal.umethod.is_empty(); status.is_possible = glocal.str.cmp("Z") <= 0; dia.newf_str ("How to search",glocal.umethod); edit(); (mode,status); settitle ("Upper case extra strategy"); status.is_filled = !glocal.uextra.is_empty(); dia.newf_str ("Upper case extra",glocal.uextra); edit(); if (ret != -1){ xconf_notice ("Done!"); } } // #Specend: // #Specbeg: guru samples / simple /* This simple gurus is used to send an email. In this case, we have two questions asked one after the other. We are using the gurusteps component. For each step in this gurus (2 steps, one for the address and one for the subject) we have an eval and a dialog node. The gurusteps allows up to 6 steps (so we have eval1, eval2, eval3 ... and dialog1, dialog2, dialog3 ....) */ static void samples_simple() { SSTRING addr; SSTRING subject; int ret = ("simple" ,"This is a samples_simple()\n" "It is a 2 steps guru",help_nil); (mode,status); settitle ("Email address"); status.is_filled = !glocal.addr.is_empty(); status.some_errors = status.is_filled && glocal.addr.strchr('@')==NULL; setintro ("Enter the email address of the recipient\n" "An address generally goes like:\n" "user@some_domain.com"); dia.newf_str ("Address",glocal.addr); edit (); settitle ("Subject"); status.is_filled = glocal.subject.is_empty(); // Subject not validated dia.newf_str ("",glocal.subject,60); edit(); if (ret != -1){ xconf_notice ("Mail %s\nwill be sent to %s",glocal.subject.get() ,glocal.addr.get()); } } // #Specend: // #Specbeg: guru samples / one choice /* This gurus is used to send an email. Like the simple one. Once the address and subject are filled, we must decide how to send it. So there is a decision node allowing us to branch. We go either the SMTP way or using another protocol. Each branch asks a single question. */ static void samples_simple_smtp() { SSTRING addr; SSTRING subject; SSTRING smtpserv; SSTRING planet; char smtp; bool smtp_filled; glocal.smtp = 1; glocal.smtp_filled = false; int ret = ("simple-smtp" ,"This is samples_simple_smtp()\n" "This gurus provide 2 steps to define a mail message\n" "and let you choose 2 ways of sending it",help_nil); (mode,status); settitle ("Email address"); status.is_filled = !glocal.addr.is_empty(); status.some_errors = status.is_filled && glocal.addr.strchr('@')==NULL; setintro ("Enter the email address of the recipient\n" "An address generally goes like:\n" "user@some_domain.com"); dia.newf_str ("Address",glocal.addr); edit (); settitle ("Subject"); status.is_filled = !glocal.subject.is_empty(); // Subject not validated dia.newf_str ("",glocal.subject,60); edit(); settitle ("How to send email"); status.is_filled = glocal.smtp_filled; setintro ("You can send email using two protocols"); dia.newf_radio ("",glocal.smtp,1,"Using smtp"); dia.newline(); dia.newf_radio ("",glocal.smtp,0,"Another protocol"); if (edit()) glocal.smtp_filled = true; (mode,status); settitle ("SMTP server"); // We can't tell if this node is possible // until the protocol selection has been // visited. Since glocal.smtp is either 1 or // 0, we are using a separate variable // to tell if the node was visited. status.is_possible = glocal.smtp == 1 && glocal.smtp_filled; status.is_filled = !glocal.smtpserv.is_empty(); setterminal ("SMTP mailer","Sending using SMTP protocol"); dia.newf_str ("Server (IP or Name)",glocal.smtpserv); edit(); (mode,status); settitle ("On which planet"); status.is_possible = glocal.smtp == 0 && glocal.smtp_filled; status.is_filled = !glocal.planet.is_empty(); setterminal ("ET mailer","Sending using out of space protocol"); dia.newf_str ("Planet",glocal.planet); edit(); if (ret != -1){ if (glocal.smtp){ xconf_notice ("Mail %s\nwill be sent to %s\nusing the SMTP server %s",glocal.subject.get() ,glocal.addr.get(),glocal.smtpserv.get()); }else{ xconf_notice ("Mail %s\nwill be sent to %s\non planet %s",glocal.subject.get() ,glocal.addr.get(),glocal.planet.get()); } } } // #Specend: // #Specbeg: guru samples / combining chunk of code /* A guru may by built by reusing smaller ones. Here is an example where two independant tasks are merged as a larger one. The main goal of a guru is at first, to help someone configure something from scratch. Because of its graphical map, it is also a great way to review a large configuration. If some time is invested in more validation, especially global validation and the status.some_errors is maintained, the map become a powerful way to review a configuration. */ static void samples_config_smtp(GURUPATH_MODE mode, GURUPATH_STATUS &status) { SSTRING gateway; int interval; glocal.interval = -1; // We must read the configuration here (mode,status); settitle ("Email gateway"); status.is_filled = !glocal.gateway.is_empty(); setintro ("Enter the IP address of the SMTP gateway"); dia.newf_str ("IP number",glocal.gateway); edit (); settitle ("Delivery interval"); status.is_filled = glocal.interval != -1; if (glocal.interval == -1) glocal.interval = 15; static int vals[]={ 0, 15 }; static const char *options[]={ "disabled", "default", NULL }; dia.newf_chkm_num ("Interval (minutes)",glocal.interval ,15,vals,options); edit(); // We must write it back here } static void samples_combine () { ("combine" ,"We are combining the same simple guru twice.\n" "This shows how code for one configuration may be connected\n" "to provide a larger one.",help_nil); (mode,status); settitle ("Combining"); setintro ("We are configuring SMTP twice"); edit(); samples_config_smtp(mode,status); // Ok, we are reusing the same code above // This is just a demo after all samples_config_smtp(mode,status); } // #Specend: // #Specbeg: guru samples / repeating a step a few times /* A gurus may have to ask a question several time, for example to configure all available network interface. Instead of using a gurusteps component and repeat the same code again and again, we use the guruiter component. Like the gurupath component, it has an eval and dialog function as well as the path1 to path6 function. But the eval and dialog are providing an extra parameter "step" telling us where we are in the iteration. The following example configure 3 network devices */ static void samples_inter3() { SSTRING network[3]; SSTRING netmask[3]; int ret = ("inter3" ,"This is samples_inter3()\n" "It grabs the configuration of 3 network interfaces\n" "repeating the same guru node 3 times",help_nil); (3,mode,status); SSTRING tmp; tmp.setfromf ("Interface number %d",step+1); settitle (tmp.get()); const char *net = glocal.network[step].get(); status.is_filled = net[0] != '\0'; status.some_errors = status.is_filled && !ipnum_validip (net,false); setintro ("Enter the network definition"); dia.newf_str ("network number",glocal.network[step]); dia.newline(); dia.newf_str ("netmask",glocal.netmask[step]); edit (); if (ret != -1){ xconf_notice ("Interfaces configured"); } } int main (int argc, char *argv[]) { return (argc,argv,"guruengine"); fprintf (stderr,"/tmp/x gurupath\n"); fprintf (stderr,"/tmp/x simple\n"); fprintf (stderr,"/tmp/x simple-smtp\n"); fprintf (stderr,"/tmp/x inter3\n"); fprintf (stderr,"/tmp/x combine\n"); int ret = 0; dialog_clear(); if (argc != 1){ usage(); ret = -1; }else if (strcmp(argv[0],"gurupath")==0){ samples_gurupath(); }else if (strcmp(argv[0],"simple")==0){ samples_simple(); }else if (strcmp(argv[0],"simple-smtp")==0){ samples_simple_smtp(); }else if (strcmp(argv[0],"inter3")==0){ samples_inter3(); }else if (strcmp(argv[0],"combine")==0){ samples_combine(); }else{ ret = -1; } return ret; }