source: src-qt4/EasyPBI/modBuild.cpp @ eaddb04

9.1-release9.2-releasereleng/10.0releng/10.0.1releng/10.0.2releng/10.0.3
Last change on this file since eaddb04 was eaddb04, checked in by Ken Moore <ken@…>, 18 months ago

Make sure that EasyPBI checks/creates any of a PBI modules subdirectories as needed whenever a new file needs to be created.

  • Property mode set to 100644
File size: 53.2 KB
Line 
1#include "modBuild.h"
2
3ModBuild::ModBuild(){
4  // --Clear the internal variables--
5  isPortPBI=FALSE; isLocalPBI=FALSE;
6  progStruct.clear(); mkStruct.clear(); serverStruct.clear(); portStruct.clear();
7  menuStruct.clear(); desktopStruct.clear(); mimeStruct.clear(); linksStruct.clear();
8  // --Setup the structures with the proper sizes--
9  progStruct <<""<<""<<""<<""<<""<<"";
10    //progStruct=[ name, website, author, icon, version, packageDir]
11  mkStruct <<""<<""<<""<<"";
12    //mkStruct=[ makeport, portbefore, portafter, makeoptions]
13  serverStruct <<"NO"<<"00"<<"00"<<"NO";
14    //serverStruct=[ needroot, buildkey, priority, noTMPFS]
15  menuStruct << ""<<""<<""<<""<<""<<""<<""<<"";
16    //menuStruct=[ name, genericname, exec, icon, nodisplay, terminal, categories, mimetype]
17  desktopStruct << ""<<""<<""<<""<<""<<""<<"";
18    //desktopStruct=[ name, genericname, exec, icon, nodisplay, terminal, mimetype]
19  mimeStruct << "" << "" << "";
20    //mimeStruct=[ info, type, patterns] (patterns is a " "-delimited list)
21  portStruct << "" << "" << "" << "" << "" << "" << "FALSE";
22    //portStruct=[ name, author, website, options, binaries, menu-category, isGraphical ]
23  validMenuCategories <<"AudioVideo"<<"Development"<<"Education"<<"Game"<<"Graphics"<<"Network"<<"Office"<<"Science"<<"Settings"<<"System"<<"Utility"; 
24}
25
26ModBuild::~ModBuild(){
27}
28
29bool ModBuild::createNewModule(QString modPath, QString modType, QString modBase){
30  //Set the module path for all the functions
31  modulePath = modPath; 
32  //Create the module directory structure if possible
33  bool ok = createModuleDir(); //this will check for existing dir and overwrite it if possible
34  if(!ok){return FALSE; }       
35  //Get the type of PBI this module creates
36  isPortPBI=FALSE; isLocalPBI=FALSE;
37  if(modType.toLower() == "port"){ isPortPBI=TRUE; }
38  else if(modType.toLower() == "local"){ isLocalPBI=TRUE; }
39  else{
40    qDebug() << "Cannot create module: Invalid module type - "+modType;
41    return FALSE;
42  }
43  //Clear out all the module structures
44  currentBins.clear(); currentIcons.clear(); currentMimeTypes.clear();
45  loadDesktop(""); loadMenu(""); loadMime("");
46  linksStruct.clear();  loadPBIconf();  readPortInformation("");
47  //Try to auto-populate the module structures if possible
48  if(isPortPBI){
49    mkStruct[0] = modBase; //pbi_makeport
50  }
51  if(isLocalPBI){
52    progStruct[0] = modBase.section("/",-1); //pbi_progname
53    progStruct[5] = modBase;  //pbi_packagedir
54  }
55  //Now create the module contents
56  writePBIconf();
57  writeExternalLinks();
58  return TRUE;
59}
60
61bool ModBuild::loadModule(QString modPath){
62  //verify that the module exists
63  QDir dir(modPath);
64  if( !( dir.exists() && dir.exists("pbi.conf") ) ){
65    qDebug() << "Cannot load module: not a valid module directory - "+modPath;
66    return FALSE;
67  }
68  //Save which module this is
69  modulePath = modPath;
70  //Load pbi.conf
71  loadPBIconf();
72  //Determine the type of module by the pbi.conf entries
73  isPortPBI=FALSE; isLocalPBI=FALSE;
74  if(!progStruct[5].isEmpty()){ isLocalPBI=TRUE; } //check for PBI_PACKAGEDIR
75  if(!mkStruct[0].isEmpty()){ isPortPBI=TRUE; } //check for PBI_MAKEPORT
76  //Reset the current lists
77  currentBins.clear(); currentIcons.clear(); currentMimeTypes.clear();
78  //Reset the XDG file structures       
79  loadDesktop(""); loadMenu(""); //Just clear these structures
80  QStringList tmp = filesAvailable("xdg-mime");
81  for(int i=0; i<tmp.length(); i++){
82    loadMime(tmp[i]); //This will fill up the currentMimeTypes structure;
83  }
84  //Get the current resources
85  tmp = filesAvailable("resources");
86  for(int i=0; i<tmp.length(); i++){
87    if(tmp[i].endsWith(".png")){ currentIcons << tmp[i]; }
88    if(tmp[i].startsWith("bin/")){ currentBins << tmp[i]; }
89  }
90  //Load the external-links file
91  if(!dir.exists("external-links")){
92    linksStruct.clear();
93    writeExternalLinks(); //Create the external-links file if it is missing
94  }else{
95    loadExternalLinks();
96  }
97  //Reset the ports structure
98  readPortInformation(""); //should fill up the currentBins structure
99  return TRUE;
100}
101
102bool ModBuild::readPortInformation(QString portPath){
103    //Clear the data structure and initialize it
104    portStruct.clear();
105    portStruct << "" << "" << "" << "" << "" << "" << "FALSE";
106    //portStruct=[ programName, author, website, options, binaries, menu-category, isGraphical ]
107    if(portPath.isEmpty()){ return TRUE; } //catch to just reset the ports structure;
108    qDebug() << "Reading port information for:" << portPath;
109    //Read the Port files and save the info to the data structure
110    QStringList actualopts, bins, listopts; //initialize lists
111    bool variableBinFound = FALSE;
112    //Load the Makefile(s)
113    QDir pDir(portPath);
114    QStringList mL = pDir.entryList(QStringList("Make*"),QDir::Files | QDir::NoDotAndDotDot | QDir::Readable);
115    for(int i=0; i<mL.length(); i++){
116      if( !mL[i].contains(".old") ){ //make sure to ignore any files labelled *.old
117        int ok = readMakeFile(portPath+"/"+mL[i]);
118        if(ok==-1){ return FALSE; }
119        else if(ok==1){ variableBinFound=TRUE;}
120      }
121    }
122   
123    //Load pkg-descr file
124    if(QFile::exists(portPath+"/pkg-descr")){
125      QFile desfile(portPath+"/pkg-descr");
126      if(!desfile.open(QIODevice::ReadOnly) ){
127        return FALSE;
128      }
129      QTextStream desin(&desfile);
130      while( !desin.atEnd() ){
131        QString line = desin.readLine().simplified();
132        // ---- Program Website -----
133        if( line.startsWith("WWW:") ){
134          portStruct[2] = line.replace("\t"," ").section(" ",1,1,QString::SectionSkipEmpty);
135          qDebug() << " - Found website:" << portStruct[2];
136        // ---- Program Author -----
137        }else if(line.startsWith("Author:") ){
138          portStruct[1] = line.replace("\t"," ").section(" ",1,4,QString::SectionSkipEmpty);
139          qDebug() << " - Found author:" << portStruct[1];
140        }
141      }
142      desfile.close();
143    }
144   
145    //Load the pkg-plist file
146    if(QFile::exists(portPath+"/pkg-plist")){
147      QFile pfile(portPath+"/pkg-plist");
148      if(!pfile.open(QIODevice::ReadOnly) ){
149        return FALSE;
150      }
151      QTextStream pin(&pfile);
152      while( !pin.atEnd() ){
153        QString line = pin.readLine().simplified();
154        if( line.startsWith("bin/") || line.startsWith("sbin/") ){
155          bins << line;
156          if(line.contains("%%")){ variableBinFound=TRUE; }
157        }
158        if(line.endsWith(".png") || line.endsWith(".jpg") || line.endsWith(".svg")){
159          //Images contained in the port, tag it as a GUI app
160          if(portStruct[6]=="FALSE"){ qDebug() << " - Detected Graphical Application"; }
161          portStruct[6] = "TRUE";
162        }
163      }
164      pfile.close();
165      if(!portStruct[4].isEmpty()){ portStruct[4].append("###"); }
166      portStruct[4].append( bins.join("###") );
167    }
168
169    //Replace any common variables that might be in the bin list if any were found
170    if( variableBinFound ){
171      qDebug() << " - Attempting to fix binary names";
172      QStringList tmpbins = portStruct[4].split("###");
173      //Try to replace common variables (add more later)
174      tmpbins.replaceInStrings("%%PORTNAME%%",portStruct[0]);
175      tmpbins.replaceInStrings("$PORTNAME",portStruct[0]);
176      tmpbins.replaceInStrings("${PORTNAME}",portStruct[0]);
177     
178      //If there are any bins that still have a variable in them, remove it from the list
179      bins.clear();
180      for(int i=0; i<tmpbins.length(); i++){
181        if(!tmpbins[i].contains("%%") && !tmpbins[i].contains("$") ){
182          bins << tmpbins[i];
183        }
184      }
185      //Save the binaries back into the struct
186      portStruct[4] = bins.join("###");
187    }
188
189
190
191    //Assign the program a menu category based on the port category
192    portStruct[5] = assignPortMenuCategory(portPath);
193    qDebug() << " - Assigned menu category:"<<portStruct[5];
194
195    //Remove any duplicate entries
196    QStringList tmp = portStruct[3].split("###");
197    tmp.removeDuplicates();
198    portStruct[3] = tmp.join(" ");
199    tmp = portStruct[4].split("###", QString::SkipEmptyParts);
200    tmp.removeDuplicates();
201    currentBins << tmp;     //Add the detected binaries to the currently available binaries
202    currentBins.removeDuplicates();
203    portStruct[4] = tmp.join(" ");
204
205    //For debugging display all info
206    qDebug() << " - Make Options:" << portStruct[3].split(" ");
207    qDebug() << " - Binaries:" << portStruct[4].split(" ");
208    qDebug() << " - Finished Detecting port information";
209    savePortDir = portPath;
210 
211  return TRUE;
212}
213
214bool ModBuild::loadPBIconf(){
215  //Clear the current structures
216  progStruct.clear(); mkStruct.clear(); serverStruct.clear();
217  //Fill the structures with empty strings (to initialize them)
218  progStruct << "" << "" << "" << "" << "" << "";
219  mkStruct << "" << "" << "" << "";
220  serverStruct << "NO" << "00" << "00" << "NO";
221  if(!QFile::exists(modulePath+"/pbi.conf")){ return FALSE; } //only reset the structures if the file does not exist
222  //Read the designated pbi.conf and store the variables
223  QFile file(modulePath+"/pbi.conf");
224  if( !file.open(QIODevice::ReadOnly | QIODevice::Text)){
225    qDebug() << "Error: Unable to load "+modulePath+"/pbi.conf";
226    return FALSE;
227  }
228  QTextStream in(&file);
229  while(!in.atEnd()){
230    //see if the current line conains a desired variable and save it
231    QString line = in.readLine();
232    if(!line.startsWith("#") && !line.startsWith("export")){ //Ignore commented out lines
233      //Pull apart the variable and the value
234      QString var,val;
235      var = line.section("=",0,0,QString::SectionSkipEmpty).trimmed();
236      val = line.section("=",1,50).section(";",0,0,QString::SectionSkipEmpty).trimmed();
237      //might need to add check for values that cross multiple lines later (mkStructure entries especially)
238      if(var=="PBI_PROGNAME"){ progStruct[0] = val.remove("\""); }
239      else if(var=="PBI_PROGWEB"){ progStruct[1] = val.remove("\""); }
240      else if(var=="PBI_PROGAUTHOR"){ progStruct[2] = val.remove("\""); }
241      else if(var=="PBI_PROGICON"){ progStruct[3] = val.remove("\""); }
242      else if(var=="PBI_PROGVERSION"){ progStruct[4] = val.remove("\""); }
243      else if(var=="PBI_PACKAGEDIR"){ progStruct[5] = val.remove("\""); }
244      else if(var=="PBI_MAKEPORT"){ mkStruct[0] = val.remove("\""); }
245      else if(var=="PBI_MKPORTBEFORE"){ 
246        QStringList tmpL = val.split(" ");
247        while( !val.simplified().endsWith("\"")){
248          val = in.readLine().section(";",0,0,QString::SectionSkipEmpty).simplified();
249          tmpL.append(val.split(" "));
250        }
251        val = tmpL.join(" ").remove("\""); //put the list back together
252        mkStruct[1] = val; 
253      }
254      else if(var=="PBI_MKPORTAFTER"){ 
255        QStringList tmpL = val.split(" ");
256        while( !val.simplified().endsWith("\"")){
257          val = in.readLine().section(";",0,0,QString::SectionSkipEmpty).simplified();
258          tmpL.append(val.split(" "));
259        }
260        val = tmpL.join(" ").remove("\""); //put the list back together
261        mkStruct[2] = val; 
262      }
263      else if(var=="PBI_MAKEOPTS"){ 
264        QString tmp = val;
265        while( !val.simplified().endsWith("\"")){
266          val = in.readLine().section(";",0,0,QString::SectionSkipEmpty).simplified();
267          tmp.append("\n"+val); //keep the same format for each line
268        }
269        mkStruct[3] = tmp.remove("\""); //Make sure to remove the quotes
270      }
271      else if(var=="PBI_REQUIRESROOT"){ serverStruct[0] = val.remove("\""); }
272      else if(var=="PBI_BUILDKEY"){ serverStruct[1] = val.remove("\""); }
273      else if(var=="PBI_AB_PRIORITY"){ serverStruct[2] = val.remove("\""); }
274      else if(var=="PBI_AB_NOTMPFS"){ serverStruct[3] = val.remove("\""); }
275      else{} //do nothing for extra lines
276    }
277  }
278  file.close();
279  return TRUE;
280}
281
282bool ModBuild::createModuleDir(){
283//Creates the directory structure for a complete PBI module
284  QString modName = modulePath.section("/",-1);
285  //Check the base Directory
286  QDir dir(modulePath);
287  if(dir.exists()){ 
288    bool validModule = dir.exists("resources") && dir.exists("xdg-desktop") \
289                    && dir.exists("xdg-menu") && dir.exists("xdg-mime") && dir.exists("scripts");
290    if( !validModule ){
291      qDebug() << "Error: Directory exists, but is not a valid module";
292      return FALSE;
293    }else{
294      qDebug() << "Valid Module directory already exists - overwriting it";
295      bool success = emptyDirectory(modulePath); //Remove the module
296      if(!success){
297        qDebug() << " - Could not remove all old module contents. Please remove them by hand";
298        return FALSE;
299      }else{
300        dir.rmdir(modulePath);
301      }
302    }
303  }
304  //Create new module directory
305  dir.cd(modulePath);
306  dir.cdUp();
307  if(!dir.mkdir(modName)){
308    qDebug() << "Error: Could not create module directory";
309    // return empty path
310  }else{
311    //If main directory could be created, should be no problem with sub-directories
312    dir.cd(modName);
313    dir.mkdir("resources");
314    dir.mkdir("xdg-desktop");
315    dir.mkdir("xdg-menu");
316    dir.mkdir("xdg-mime");
317    dir.mkdir("scripts");
318    qDebug() << "Module directory successfully created";
319  }
320
321  return TRUE;
322}
323
324bool ModBuild::writePBIconf(){
325//Returns TRUE if successful
326
327  //Check if the module directory exists
328  QDir testDir(modulePath);
329  if(testDir.exists() == FALSE){
330    qDebug() << " - pbi.conf creation: Failure -- Module directory does not exist";
331    return FALSE;
332  }
333
334  //Get the path to the module's pbi.conf
335  QString fileName = modulePath+"/pbi.conf";
336
337 //Create the contents of the file to write
338  QStringList contents;
339  QString exportLine = "export";
340  contents << "#!/bin/sh";
341  contents << "# PBI Build Configuration\n";
342  // -- Program Info
343  contents << "# -- Program Information --";
344  contents << "PBI_PROGNAME=\""+progStruct[0]+"\"";
345  contents << "PBI_PROGWEB=\""+progStruct[1]+"\"";
346  contents << "PBI_PROGAUTHOR=\""+progStruct[2]+"\"";
347  contents << "PBI_PROGICON=\""+getFilenameFromPath(progStruct[3])+"\"";
348  exportLine.append(" PBI_PROGNAME PBI_PROGWEB PBI_PROGAUTHOR PBI_PROGICON");
349  if(isLocalPBI){
350    contents << "PBI_PROGVERSION=\""+progStruct[4]+"\"";
351    contents << "PBI_PACKAGEDIR=\""+progStruct[5]+"\"";
352    exportLine.append(" PBI_PROGVERSION PBI_PACKAGEDIR");
353  }
354  contents << ""; //Add a blank line between sections
355  if(isPortPBI){
356    // -- Port Info (only add this section if the PBI uses FreeBSD ports)
357    contents << "# -- Port Information --";
358    contents << "PBI_MAKEPORT=\""+mkStruct[0]+"\"";
359    contents << "PBI_MKPORTBEFORE=\""+mkStruct[1]+"\"";
360    contents << "PBI_MKPORTAFTER=\""+mkStruct[2]+"\"";
361    contents << "PBI_MAKEOPTS=\""+mkStruct[3]+"\"\n"; //adds a blank line after the section
362    exportLine.append(" PBI_MAKEPORT PBI_MKPORTBEFORE PBI_MKPORTAFTER PBI_MAKEOPTS");
363  }
364  // -- Server & Other Info
365  contents << "# -- Require Root Permissions to Install PBI --";
366  contents << "PBI_REQUIRESROOT=\""+checkYesNo(serverStruct[0].remove("\""))+"\"";
367  contents << "# -- Auto-build Configuration Options --";
368  contents << "PBI_BUILDKEY=\""+serverStruct[1].remove("\"")+"\"";
369  contents << "PBI_AB_PRIORITY=\""+serverStruct[2].remove("\"")+"\"";
370  contents << "PBI_AB_NOTMPFS=\""+checkYesNo(serverStruct[3].remove("\""))+"\"\n";
371  exportLine.append(" PBI_REQUIRESROOT PBI_BUILDKEY PBI_AB_PRIORITY PBI_NOTMPFS");
372  // --Export all the variables used
373  contents << exportLine;
374
375  //Create the File
376  bool status = createFile(fileName, contents);
377  return status;
378}
379
380bool ModBuild::loadMenu(QString ifile){
381  //Reset the structure
382  menuStruct.clear();
383  menuStruct << ""<<""<<""<<""<<""<<""<<""<<""; 
384  //[ name, genericname, exec, icon, nodisplay, terminal, categories, mimetype]
385       
386  if(ifile.isEmpty()){ return TRUE; } //Also allow this to be a "reset" function for the currently loaded file
387  //Check if the file exists
388  saveMenuFile = ifile; //save the current filename that is loaded
389  QString filePath = modulePath +"/xdg-menu/"+ ifile;
390  if(!QFile::exists(filePath)){ return TRUE; } //Get ready to write a currently non-existant file
391  //Read the file and save the data into the structure
392  bool status = FALSE;
393  QFile file(filePath);
394    if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
395      qDebug() << "Error: Could not open xdg-menu entry file:"<<filePath;
396    }else{
397      QTextStream in(&file);
398      while(!in.atEnd()){
399        QString line = in.readLine().simplified();
400        if(line.startsWith("Name=")){
401          menuStruct[0] = line.section("=",1,3,QString::SectionSkipEmpty);
402        }else if(line.startsWith("GenericName=")){
403          menuStruct[1] = line.section("=",1,3,QString::SectionSkipEmpty);
404        }else if(line.startsWith("Exec=")){
405          QString exec = line.section("=",1,3,QString::SectionSkipEmpty);
406          menuStruct[2] = getFilenameFromPath(exec);
407        }else if(line.startsWith("Icon=")){
408          QString icon = line.section("=",1,3,QString::SectionSkipEmpty);
409          menuStruct[3] = getFilenameFromPath(icon);
410        }else if(line.startsWith("NoDisplay=")){
411          menuStruct[4] = line.section("=",1,3,QString::SectionSkipEmpty);
412        }else if(line.startsWith("Terminal=")){
413          menuStruct[5] = line.section("=",1,3,QString::SectionSkipEmpty);
414        }else if(line.startsWith("Categories=")){
415          menuStruct[6] = line.section("=",1,3,QString::SectionSkipEmpty);
416        }else if(line.startsWith("MimeType=")){
417          menuStruct[7] = line.section("=",1,3,QString::SectionSkipEmpty);
418        }else{
419          //Do nothing - ignore this line
420        }
421      }
422      //Check true/false values
423      if(menuStruct[4].toLower() != "true"){ menuStruct[4]="false"; }
424      if(menuStruct[5].toLower() != "true"){ menuStruct[5]="false"; }
425      status = TRUE;
426      file.close();
427    }
428    return status;
429}
430
431bool ModBuild::writeMenu(){
432  //Get the path to the file
433  QString dir = modulePath + "/xdg-menu/";
434  createDir(dir); //make sure the xdg-menu directory exists
435  QString fileName = dir + saveMenuFile;
436  //Create the XDG compliant menu entry
437  QStringList contents;
438  contents << "#!/usr/bin/env";
439  contents << "[Desktop Entry]";
440  contents << "Value=1.0";
441  contents << "Type=Application";
442  contents << "Name="+menuStruct[0];
443  contents << "GenericName="+menuStruct[1];
444  contents << "Exec=%%PBI_EXEDIR%%/"+getFilenameFromPath(menuStruct[2]);
445  contents << "Path=%%PBI_APPDIR%%";
446  contents << "Icon=%%PBI_APPDIR%%/"+getFilenameFromPath(menuStruct[3]);
447  contents << "StartupNotify=true";
448  if(menuStruct[4].toLower()=="yes" || menuStruct[4].toLower()=="true"){
449    contents << "NoDisplay=true";
450  }
451  if(menuStruct[5].toLower()=="yes" || menuStruct[5].toLower()=="true"){
452    contents << "Terminal=true";
453  }
454  contents << "Categories="+menuStruct[6];
455  if(!menuStruct[7].isEmpty()){
456    contents << "MimeType="+menuStruct[7];
457  }
458  //Save the contents to the file
459  bool status;
460  status = createFile(fileName, contents);
461  return status;
462}
463
464bool ModBuild::removeMenu(){
465  //Delete the currently selected menu file
466  bool status = QFile::remove(modulePath+"/xdg-menu/"+saveMenuFile);
467  return status;
468}
469
470bool ModBuild::loadDesktop(QString ifile){
471  //Reset the structure
472  desktopStruct.clear();
473  desktopStruct << ""<<""<<""<<""<<""<<""<<"";
474    //[ name, genericname, exec, icon, nodisplay, terminal, mimetypes]
475       
476  if(ifile.isEmpty()){ return TRUE; } //Also allow this to be a "reset" function for the currently loaded file
477  //Check if the file exists
478  saveDesktopFile = ifile; //save the current filename that is loaded
479  QString filePath = modulePath +"/xdg-desktop/"+ ifile;
480  if(!QFile::exists(filePath)){ return TRUE; } //Get ready to write a currently non-existant file
481  //Read the file and save the data into the structure
482  bool status = FALSE;
483  QFile file(filePath);
484    if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
485      qDebug() << "Error: Could not open xdg-desktop entry file:"<<filePath;
486    }else{
487      QTextStream in(&file);
488      while(!in.atEnd()){
489        QString line = in.readLine().simplified();
490        if(line.startsWith("Name=")){
491          desktopStruct[0] = line.section("=",1,3,QString::SectionSkipEmpty);
492        }else if(line.startsWith("GenericName=")){
493          desktopStruct[1] = line.section("=",1,3,QString::SectionSkipEmpty);
494        }else if(line.startsWith("Exec=")){
495          QString exec = line.section("=",1,3,QString::SectionSkipEmpty);
496          desktopStruct[2] = getFilenameFromPath(exec);
497        }else if(line.startsWith("Icon=")){
498          QString icon = line.section("=",1,3,QString::SectionSkipEmpty);
499          desktopStruct[3] = getFilenameFromPath(icon);
500        }else if(line.startsWith("NoDisplay=")){
501          desktopStruct[4] = line.section("=",1,3,QString::SectionSkipEmpty);
502        }else if(line.startsWith("Terminal=")){
503          desktopStruct[5] = line.section("=",1,3,QString::SectionSkipEmpty);
504        }else if(line.startsWith("MimeType=")){
505          desktopStruct[6] = line.section("=",1,3,QString::SectionSkipEmpty);
506        }else{
507          //Do nothing - ignore this line
508        }
509      }
510      //Check for true/false values
511      if(desktopStruct[4].toLower() != "true"){ desktopStruct[4]="false"; }
512      if(desktopStruct[5].toLower() != "true"){ desktopStruct[5]="false"; }
513      status = TRUE;
514      file.close();
515    }
516    return status;
517}
518
519bool ModBuild::writeDesktop(){
520  //Get the path to the file
521  QString dir = modulePath + "/xdg-desktop/";
522  createDir(dir); //make sure the xdg-desktop directory exists
523  QString fileName =  dir + saveDesktopFile;
524  //Create the XDG compliant desktop entry
525  QStringList contents;
526  contents << "#!/usr/bin/env";
527  contents << "[Desktop Entry]";
528  contents << "Value=1.0";
529  contents << "Type=Application";
530  contents << "Name="+desktopStruct[0];
531  contents << "GenericName="+desktopStruct[1];
532  contents << "Exec=%%PBI_EXEDIR%%/"+getFilenameFromPath(desktopStruct[2]);
533  contents << "Path=%%PBI_APPDIR%%";
534  contents << "Icon=%%PBI_APPDIR%%/"+getFilenameFromPath(desktopStruct[3]);
535  contents << "StartupNotify=true";
536  if(desktopStruct[4].toLower()=="yes" || desktopStruct[4].toLower()=="true"){
537    contents << "NoDisplay=true";
538  }
539  if(desktopStruct[5].toLower()=="yes" || desktopStruct[5].toLower()=="true"){
540    contents << "Terminal=true";
541  }
542  if(!desktopStruct[6].isEmpty()){
543    contents << "MimeType="+desktopStruct[6];
544  }
545  //Save the contents to the file
546  bool status;
547  status = createFile(fileName, contents);
548  return status;
549}
550
551bool ModBuild::removeDesktop(){
552  //Delete the currently selected menu file
553  bool status = QFile::remove(modulePath+"/xdg-desktop/"+saveDesktopFile);
554  return status;
555}
556
557bool ModBuild::loadMime(QString ifile){
558  //Reset the structure
559  mimeStruct.clear();
560  mimeStruct << "" << "" << "";  //[ info, type, patterns]
561  if(ifile.isEmpty()){ return TRUE; } //Also allow this to be a "reset" function for the currently loaded file
562  //Check if the file exists
563  saveMimeFile = ifile; //save the current filename that is loaded
564  QString filePath = modulePath +"/xdg-mime/"+ ifile;
565  if(!QFile::exists(filePath)){ return TRUE; } //Get ready to write a currently non-existant file
566  //Read the file and save the data into the structure
567  bool status = FALSE;
568  QFile file(filePath);
569    if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
570      qDebug() << "Error: Could not open xdg-mime entry file:"<<filePath;
571    }else{
572      QTextStream in(&file);
573      QStringList patterns;
574      while(!in.atEnd()){
575        QString line = in.readLine().simplified();
576        if(line.startsWith("<mime-info ")){
577          mimeStruct[0] = line.section(" xmlns=",1,1).section("=",0,0).section(">",0,0).remove("\"").remove("\'");
578        }else if(line.startsWith("<mime-type ")){
579          mimeStruct[1] = line.section(" type=",1,1).section("=",0,0).section(">",0,0).remove("\"").remove("\'");
580        }else if(line.startsWith("<glob ") && line.contains("pattern=")){
581          patterns << line.section("pattern=",1,1).section("=",0,0).section("/>",0,0).remove("\"").remove("\'");
582        }else{
583          //Do nothing - ignore this line
584        }
585      }
586      mimeStruct[2] = patterns.join(" "); //make a space-delimited list
587      //Make sure the detected mime type is in the current list
588      currentMimeTypes << mimeStruct[1];
589      currentMimeTypes.removeDuplicates();
590      //Return
591      status = TRUE;
592      file.close();
593    }
594    return status;
595}
596
597bool ModBuild::writeMime(){
598  //Stupid check to make sure there is actually file patterns to be saved
599  if(mimeStruct[2].isEmpty()){ return FALSE; }
600  //Get the path to the file
601  QString dir = modulePath + "/xdg-mime/";
602  createDir(dir); //make sure the mime directory exists
603  QString fileName = dir + saveMimeFile;
604 
605  //Set a default mime-info if none given 
606  if(mimeStruct[0].isEmpty()){
607    mimeStruct[0] = "http://www.freedesktop.org/standards/shared-mime-info";
608  }
609  //Set a default mime type based upon the filename if none given
610  if(mimeStruct[1].isEmpty()){
611    mimeStruct[1] = "application/x-"+saveMimeFile.section("-xdg.xml",0,0);
612  }
613  //Make a list of all the patterns to save
614  QStringList patterns = mimeStruct[2].split(" ");
615  //Create the XDG compliant mime type entry
616  QStringList contents;
617  contents << "<?xml version=\"1.0\"?>";
618  contents << "<mime-info xmlns=\'"+mimeStruct[0]+"\'>";
619  contents << "  <mime-type type=\""+mimeStruct[1]+"\">";
620  for(int i=0; i<patterns.length(); i++){
621    contents << "  <glob weight=\"100\" pattern=\""+patterns[i]+"\"/>";
622  }
623  contents << " </mime-type>";
624  contents << "</mime-info>";
625  //Save the contents to the file
626  bool status;
627  status = createFile(fileName, contents);
628  if(status){ 
629    //If successful, add the new mimetype to the current list
630    currentMimeTypes <<mimeStruct[1]; 
631    currentMimeTypes.removeDuplicates(); 
632  }
633  return status;
634}
635
636bool ModBuild::removeMime(){
637  //Delete the currently selected mime file
638  bool status = QFile::remove(modulePath+"/xdg-mime/"+saveMimeFile);
639  //Remove the current Mime entry is successful
640  if(status){
641    int index = currentMimeTypes.indexOf(mimeStruct[1]);
642    if(index >= 0){ currentMimeTypes.removeAt(index); }
643  }
644  return status;
645}
646
647bool ModBuild::loadExternalLinks(){
648    //reset the structure
649    linksStruct.clear();
650    //Read the new file
651    QFile file(modulePath+"/external-links");
652    if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
653      qDebug() << "Error: Could not open external-links file:"<<modulePath+"/external-links";
654      return FALSE;
655    }
656    QTextStream in(&file);
657    QString original, linkto, types;
658    while(!in.atEnd()){
659      QString line = in.readLine();
660      line.replace("\t"," ").simplified();
661      if(!line.startsWith("#") && !line.isEmpty()){
662        original = line.section(" ",0,0,QString::SectionSkipEmpty);
663        linkto =  line.section(" ",1,1,QString::SectionSkipEmpty);
664        types =  line.section(" ",2,2,QString::SectionSkipEmpty);
665        //Add the link to the structure
666        linksStruct << original+"::"+linkto+"::"+types;
667      }else{
668        //Do nothing - ignore this line
669      }
670    }
671    file.close();
672    return TRUE;
673}
674
675bool ModBuild::writeExternalLinks(){
676  //File to write
677  QString fileName = modulePath+"/external-links";
678  //Get the contents to write to the file
679  QStringList contents;
680  //Standard Header for file
681  contents << "# Files to be Sym-Linked into the default LOCALBASE";
682  contents << "# One per-line, relative to %%PBI_APPDIR%% and LOCALBASE";
683  contents << "# Defaults to keeping any existing files in LOCALBASE";
684  contents << "# Use action \"binary\" for binaries that need wrapper functionality\n";
685  contents << "# TARGET             LINK IN LOCALBASE         ACTION";
686  //add the desired binaries
687  for(int i=0; i<linksStruct.length(); i++){
688    QStringList link = linksStruct[i].split("::");
689    qDebug() << "save link:" << link;
690    contents << link[0] +"     "+link[1]+"     "+link[2];
691  }
692  //Create the external-links file
693  bool status = createFile(fileName, contents);
694  return status;
695}
696
697void ModBuild::addExternalLink(QString file, QString linkto, QStringList types){
698  QString chk = file+"::"+linkto+"::"; //try to match the file and link, not types
699  for(int i=0; i<linksStruct.length(); i++){
700    if( linksStruct[i].startsWith(chk) ){
701      //This link already exists -> update the types and return
702      linksStruct[i] = chk+types.join(",");
703      return;
704    }
705  }     
706  //This is a new link, add it to the end
707  linksStruct << chk+types.join(",");
708}
709
710void ModBuild::removeExternalLink(QString file, QString linkto){
711  QString chk = file+"::"+linkto+"::"; //try to match the file and link, not types
712  for(int i=0; i<linksStruct.length(); i++){
713    if( linksStruct[i].startsWith(chk) ){
714      //This link already exists -> remove it
715      linksStruct.removeAt(i);
716      return;
717    }
718  }     
719}
720
721QStringList ModBuild::externalLinks(){
722  //Return the currently available links
723  QStringList output = linksStruct;
724  output.replaceInStrings("::","\t");
725  return output;
726}
727
728bool ModBuild::addResource(bool isNewWrapper, QString resourcePath){
729  if(resourcePath.isEmpty()){
730    qDebug() << "Warning: no resource selected";
731    return TRUE;  // Do not flag this as an error in the function
732  }
733  //Determine the type of file that is being added
734  bool isIcon=FALSE;
735  if(resourcePath.endsWith(".png") && !isNewWrapper){ isIcon = TRUE; }
736  //Create the full path to the location within the module 
737  QString newResourcePath = modulePath + "/resources/";
738  createDir(newResourcePath); //make sure the resources directory exists
739  if(isNewWrapper){ 
740    newResourcePath.append("bin/"); 
741    //Make sure the "bin" directory exists
742    QDir dir(newResourcePath);
743    if(!dir.exists()){ dir.cdUp(); dir.mkdir("bin"); } 
744  }else{
745    //resourcePath *MUST* be an absolute path (start with "/")
746    if( !resourcePath.startsWith("/") ){
747      qDebug() << "Error: Path to desired resource is not an absolute path!";
748      return FALSE;
749    }
750    //Check that the desired resource exists
751    if( !QFile::exists(resourcePath) ){
752      qDebug() << "Error: Desired resource does not exist: " << resourcePath;
753      return FALSE;
754    }
755  }
756  QString resource = getFilenameFromPath(resourcePath);
757  newResourcePath.append(resource);
758  //Check if the resource already exists in the module
759  if( QFile::exists(newResourcePath) ){
760    qDebug() << "Overwriting existing module resource:" << resource;
761    QFile::remove(newResourcePath); 
762  }
763  //Copy the resource into the module
764  bool status=FALSE;
765  if(isNewWrapper){
766    //Create a standard wrapper script in the module
767    QStringList form;
768    form << "#!/bin/sh";
769    form << "#This is a sample wrapper script form generated by EasyPBI";
770    form << "# REMINDER: Add this file to the external-links for the PBI as well!";
771    form << "\n#Setup some internal script variables";
772    form << "PROGDIR=`pwd | cut -d / -f 1-4`   #Base PBI directory (/usr/pbi/myapplication-<arch>)";
773    form << "APPBINARY=bin/SAMPLE   #application binary";
774    form << "\n#--- DO SOMETHING HERE ---\n";
775    form << "#Now start the main application";
776    form << "${PROGDIR}/${APPBINARY} $@";
777    status = createFile(newResourcePath,form);
778    //Make sure the new wrapper is executable
779    QFile::setPermissions(newResourcePath, QFile::ReadOwner | QFile::ExeOwner | QFile::WriteOwner  | QFile::ReadGroup | QFile::ExeGroup  | QFile::WriteGroup | QFile::ReadOther | QFile::ExeOther);
780  }else{
781    //Copy the given file into the module
782    if( !QFile::copy(resourcePath,newResourcePath) ){
783      qDebug() << "Error copying resource into the module";
784    }else{
785      status=TRUE;
786    }
787  }
788  //Add the item to the appropriate lists
789  if(status && isIcon){ currentIcons << resource;  currentIcons.removeDuplicates();}
790  else if(status && isNewWrapper){ currentBins << "bin/"+resource; currentBins.removeDuplicates();}
791 
792  return status;
793}
794
795bool ModBuild::removeResource(QString filePath){
796  //filePath is relative to the module's resource directory
797       
798  //Determine if it is a wrapper script, icon, or other
799  bool isWrapper=FALSE; bool isIcon=FALSE;
800  if(filePath.startsWith("bin/")){ isWrapper=TRUE; }
801  else if(filePath.endsWith(".png")){ isIcon=TRUE; }
802  //Check if the file exists
803  bool status=FALSE;
804  QDir dir(modulePath+"/resources");
805  if(dir.exists(filePath)){
806    status = dir.remove(filePath);
807  }else{
808    qDebug() << "Error removing non-existant resource:"+filePath;
809  }
810  //Remove the item from the appropriate lists
811  if(status && isIcon){ currentIcons.removeAll(filePath); }
812  else if(status && isWrapper){ 
813    currentBins.removeAll(filePath); 
814    dir.cd("bin");
815    if(dir.entryList(QDir::Files | QDir::NoDotAndDotDot).isEmpty() ){ 
816      dir.cdUp();
817      dir.rmdir("bin");
818    }       
819  }
820 
821  return status;
822}
823
824bool ModBuild::createDir(QString dirPath){
825  if(dirPath.endsWith("/")){ dirPath.chop(1); }
826  QDir dir(dirPath);
827  if(dir.exists()){ return TRUE; }
828  else{ return dir.mkdir(dirPath); }
829}
830
831bool ModBuild::createFile(QString fileName, QStringList contents){
832//fileName = full path to file (I.E. /home/pcbsd/junk/junk.txt)
833//contents = list of lines to be written (one line per entry in the list - no newline needed at the end of an entry)
834
835  //Remove any existing file with that name/location
836  if( QFile::exists(fileName) ){
837    if( !QFile::remove(fileName) ){
838      qDebug() << fileName+": Error -- Could not overwrite existing file";
839      return FALSE;
840    }
841  }
842  //Open the file with .tmp extension
843  QFile file(fileName+".tmp");
844  if( !file.open(QIODevice::WriteOnly | QIODevice::Text) ){
845    qDebug() << fileName+".tmp: Failure -- Could not open file";
846    return FALSE;
847  }
848  //Write the file
849  QTextStream ofile(&file); //start the output stream
850  for(int i=0; i<contents.length(); i++){
851    ofile << contents[i];
852    ofile << "\n";
853  }
854  //Close the File
855  file.close();
856  //Move the temporary file into its final location
857  if( !file.rename(fileName) ){
858    qDebug() << fileName+": Error: Could not rename "+fileName+".tmp as "+fileName;
859    return FALSE;
860  }
861  //Return success
862  QString extra = QDir::homePath() + "/EasyPBI/Modules/"; //remove this from the filename display
863  qDebug() << "Created:" << fileName.remove(extra);
864  return TRUE;
865}
866
867QStringList ModBuild::readFile(QString filePath){
868  QStringList contents;
869  //Check that the file exists first
870  if(!QFile::exists(filePath)){ 
871    qDebug() << "Error: file to read does not exist:" << filePath;
872    return contents; 
873  }
874  //Open the file for reading
875  QFile file(filePath);
876  if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){ 
877    qDebug() << "Error: file could not be opened for reading:"<< filePath;
878    return contents; 
879  }
880  QTextStream in(&file);
881  //Save the contents as a QStringList
882  while(!in.atEnd()){
883    contents << in.readLine();
884  }
885  //Return the contents (one entry per line)
886  return contents;
887}
888
889QStringList ModBuild::filesAvailable(QString group){
890  QStringList fileList; 
891  //Check to make sure that a module has been selected first
892  if(modulePath.isEmpty()){ return fileList; }
893  QDir dir(modulePath);
894  group = group.toLower();
895  if(group=="resources"){
896    dir.cd(group);
897    //Get all the files in the main directory
898    fileList = dir.entryList(QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden);
899    //Get all the files in any sub-directories
900    QStringList subdirs = dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
901    for(int i=0; i<subdirs.length(); i++){
902      dir.cd(subdirs[i]);
903      QStringList dirL = dir.entryList(QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden);
904      for(int j=0; j<dirL.length(); j++){
905        fileList << subdirs[i]+"/"+dirL[j];
906      }
907      dir.cdUp();
908    }
909  }else if(group=="xdg-desktop"){
910    dir.cd(group);
911    //Get all the files in the main directory
912    fileList = dir.entryList(QStringList("*.desktop") , QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden);
913  }else if(group=="xdg-menu"){
914    dir.cd(group);
915    //Get all the files in the main directory
916    fileList = dir.entryList(QStringList("*.desktop"), QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden);       
917  }else if(group=="xdg-mime"){
918    dir.cd(group);
919    //Get all the files in the main directory
920    fileList = dir.entryList(QStringList("*.xml"), QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden);   
921  }else if(group=="scripts"){
922    dir.cd(group);
923    //Get all the files in the main directory
924    fileList = dir.entryList(QStringList("*.sh"), QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden);     
925  }
926  return fileList; //This will return nothing if an invalid group is given
927}
928
929QString ModBuild::readValue(QString variable){
930//progStruct=[ name, website, author, icon, version, packageDir]
931//mkStruct=[ makeport, portbefore, portafter, makeoptions]
932//serverStruct=[ needroot, buildkey, priority, noTMPFS]
933//menuStruct=[ name, genericname, exec, icon, nodisplay, terminal, categories, mimetype]
934//desktopStruct=[ name, genericname, exec, icon, nodisplay, terminal, mimetype]
935//mimeStruct=[ info, type, patterns] (patterns is a " "-delimited list)
936//portStruct=[ name, author, website, options, binaries, menu-category, isGraphical ]   
937  QString val;
938  QString var = variable.toLower();
939  if(var=="progname"){ val = progStruct[0] ;}
940  else if(var=="progweb"){ val = progStruct[1]; }
941  else if(var=="progauthor"){ val = progStruct[2]; }
942  else if(var=="progicon"){ val = progStruct[3]; }
943  else if(var=="progversion"){ val = progStruct[4]; }
944  else if(var=="packagedir"){ val = progStruct[5]; }
945  else if(var=="makeport"){ val = mkStruct[0]; }
946  else if(var=="makeportbefore"){ val = mkStruct[1]; }
947  else if(var=="makeportafter"){ val = mkStruct[2]; }
948  else if(var=="makeoptions"){ val = mkStruct[3]; }
949  else if(var=="requiresroot"){ val = serverStruct[0]; }
950  else if(var=="buildkey"){ val = serverStruct[1]; }
951  else if(var=="priority"){ val = serverStruct[2]; }
952  else if(var=="notmpfs"){ val = serverStruct[3]; }
953  else if(var=="menuname"){ val = menuStruct[0]; }
954  else if(var=="menugenericname"){ val = menuStruct[1]; }
955  else if(var=="menuexec"){ val = menuStruct[2]; }
956  else if(var=="menuicon"){ val = menuStruct[3]; }
957  else if(var=="menunodisplay"){ val = menuStruct[4] ;}
958  else if(var=="menuterminal"){ val = menuStruct[5]; }
959  else if(var=="menucategories"){ val = menuStruct[6]; }
960  else if(var=="menumimetype"){ val = menuStruct[7]; }
961  else if(var=="desktopname"){ val = desktopStruct[0]; }
962  else if(var=="desktopgenericname"){ val = desktopStruct[1]; }
963  else if(var=="desktopexec"){ val = desktopStruct[2]; }
964  else if(var=="desktopicon"){ val = desktopStruct[3]; }
965  else if(var=="desktopnodisplay"){ val = desktopStruct[4]; }
966  else if(var=="desktopterminal"){ val = desktopStruct[5]; }
967  else if(var=="desktopmimetype"){ val = desktopStruct[6]; }
968  else if(var=="mimeinfo"){ val = mimeStruct[0]; }
969  else if(var=="mimetype"){ val = mimeStruct[1]; }
970  else if(var=="mimepatterns"){ val = mimeStruct[2]; }
971  else if(var=="portname"){ val = portStruct[0]; }
972  else if(var=="portauthor"){ val = portStruct[1]; }
973  else if(var=="portwebsite"){ val = portStruct[2]; }
974  else if(var=="portoptions"){ val = portStruct[3]; }
975  else if(var=="portbinaries"){ val = portStruct[4]; }
976  else if(var=="portmenucategory"){ val = portStruct[5]; }
977  else if(var=="portisgraphical"){ val = portStruct[6]; }
978  else{
979    qDebug() << "Error: Invalid variable name to read";
980  }
981  return val;
982}
983
984bool ModBuild::writeValue(QString variable, QString value){
985//progStruct=[ name, website, author, icon, version, packageDir]
986//mkStruct=[ makeport, portbefore, portafter, makeoptions]
987//serverStruct=[ needroot, buildkey, priority, noTMPFS]
988//menuStruct=[ name, genericname, exec, icon, nodisplay, terminal, categories, mimetype]
989//desktopStruct=[ name, genericname, exec, icon, nodisplay, terminal, mimetype]
990//mimeStruct=[ info, type, patterns] (patterns is a " "-delimited list)
991  QString var = variable.toLower();
992  if(var=="progname"){ progStruct[0] = value; }
993  else if(var=="progweb"){ progStruct[1] = value; }
994  else if(var=="progauthor"){ progStruct[2] = value; }
995  else if(var=="progicon"){ progStruct[3] = value; }
996  else if(var=="progversion"){ progStruct[4] = value; }
997  else if(var=="packagedir"){ progStruct[5] = value; }
998  else if(var=="makeport"){ mkStruct[0] = value; }
999  else if(var=="makeportbefore"){ mkStruct[1] = value; }
1000  else if(var=="makeportafter"){ mkStruct[2] = value; }
1001  else if(var=="makeoptions"){ mkStruct[3] = value; }
1002  else if(var=="requiresroot"){ serverStruct[0] = value; }
1003  else if(var=="buildkey"){ serverStruct[1] = value; }
1004  else if(var=="priority"){ serverStruct[2] = value; }
1005  else if(var=="notmpfs"){ serverStruct[3] = value; }
1006  else if(var=="menuname"){ menuStruct[0] = value; }
1007  else if(var=="menugenericname"){ menuStruct[1] = value; }
1008  else if(var=="menuexec"){ menuStruct[2] = value; }
1009  else if(var=="menuicon"){ menuStruct[3] = value; }
1010  else if(var=="menunodisplay"){ menuStruct[4] = value; }
1011  else if(var=="menuterminal"){ menuStruct[5] = value; }
1012  else if(var=="menucategories"){ menuStruct[6] = value; }
1013  else if(var=="menumimetype"){ menuStruct[7] = value; }
1014  else if(var=="desktopname"){ desktopStruct[0] = value; }
1015  else if(var=="desktopgenericname"){ desktopStruct[1] = value; }
1016  else if(var=="desktopexec"){ desktopStruct[2] = value; }
1017  else if(var=="desktopicon"){ desktopStruct[3] = value; }
1018  else if(var=="desktopnodisplay"){ desktopStruct[4] = value; }
1019  else if(var=="desktopterminal"){ desktopStruct[5] = value; }
1020  else if(var=="desktopmimetype"){ desktopStruct[6] = value; }
1021  else if(var=="mimeinfo"){ mimeStruct[0] = value; }
1022  else if(var=="mimetype"){ mimeStruct[1] = value; }
1023  else if(var=="mimepatterns"){ mimeStruct[2] = value; }
1024  else{
1025    qDebug() << "Error: Invalid variable name to read";
1026    return FALSE;
1027  }
1028  return TRUE;
1029}
1030
1031void ModBuild::compressModule(){
1032  //Compress the module directory
1033  QString localDir = getFilenameFromPath(modulePath);
1034  QString cmd = "cd "+modulePath+"/..; tar czf "+localDir+".tar.gz "+localDir;
1035  qDebug() << "Compressing module:" << modulePath+".tar.gz";
1036  system( cmd.toUtf8() );
1037  return;
1038}
1039
1040QString ModBuild::getFilenameFromPath(QString fullPath){
1041  if(fullPath.isEmpty()){return "";}
1042  return fullPath.section("/",-1);
1043}
1044
1045int ModBuild::readMakeFile(QString makefile){
1046  bool variableBinFound=FALSE;
1047  QStringList bins, actualopts, listopts;
1048
1049    QFile mkfile(makefile);
1050    if(!mkfile.open(QIODevice::ReadOnly) ){
1051      return -1;
1052    }
1053    QTextStream mkin(&mkfile);
1054    while( !mkin.atEnd() ){
1055      QString line = mkin.readLine().simplified();
1056      // ---- Program Name -----
1057      if( line.startsWith("PORTNAME") ){
1058        portStruct[0] = line.replace("\t"," ").section(" ",1,1,QString::SectionSkipEmpty);
1059        qDebug() << " - Found portname:" << portStruct[0];
1060      // ---- Port Maintainer -----
1061      }else if( line.startsWith("MAINTAINER") ){
1062        portStruct[1] = line.replace("\t"," ").section(" ",1,1,QString::SectionSkipEmpty);
1063        qDebug() << " - Found maintainer:"<< portStruct[1];
1064      // ---- OptionsNG Framework --------
1065      }else if( line.startsWith("OPTIONS_DEFINE") || line.startsWith("OPTIONS_SINGLE_") || line.startsWith("OPTIONS_MULTI_") ){
1066        listopts << line.replace("\t"," ").section("=",1,50,QString::SectionSkipEmpty).simplified().split(" "); 
1067        while( line.simplified().endsWith("\\")){
1068          line = mkin.readLine().replace("\t"," ");
1069          listopts << line.split(" ");
1070        }
1071      }else if( line.startsWith("OPTIONS_DEFAULT") ){
1072        QStringList defaults = line.replace("\t"," ").section("=",1,50,QString::SectionSkipEmpty).simplified().split(" ");     
1073        for(int i=0; i<listopts.length(); i++){
1074          if(defaults.contains(listopts[i].simplified()) ){ 
1075            actualopts << "WITHOUT_"+listopts[i].simplified();
1076          }else{
1077            actualopts << "WITH_"+listopts[i].simplified();
1078          }
1079        }
1080      // ---- Legacy Options Framework -------
1081      }else if( line.startsWith("OPTIONS") && !line.startsWith("OPTIONS_") ){
1082        listopts << line.replace("\t"," ").section(" ",1,1,QString::SectionSkipEmpty); 
1083        while( line.simplified().endsWith("\\")){
1084          line = mkin.readLine().replace("\t"," ");
1085          listopts << line.section(" ",0,0,QString::SectionSkipEmpty).simplified();
1086        }
1087      }else if( line.startsWith(".if defined(") || line.startsWith(".if !defined(") ){
1088        //Get the variable used (we need WITH_* or WITHOUT_*)
1089        QString var = line.section("(",1,1).section(")",0,0).simplified();
1090        //qDebug() << line << var << var.section("_",1,1);
1091        if( var.startsWith("WITH") && listopts.contains( var.section("_",1,1) ) ){
1092          actualopts << var;
1093        }
1094      // ---- Program Website -----
1095      }else if( line.startsWith("WWW") ){
1096          portStruct[2] = line.section(" ",1,1,QString::SectionSkipEmpty);
1097          qDebug() << " - Found website:"<< portStruct[2];
1098      // ---- Program Binaries/Files ----
1099      }else if( line.startsWith("PLIST_FILES") ){ //catch PLIST_FILES= and PLIST_FILES+=
1100        QStringList tmpL = line.section(" ",1,60).split(" ",QString::SkipEmptyParts);
1101        while( line.simplified().endsWith("\\") ){
1102          line = mkin.readLine().replace("\t"," ");
1103          tmpL.append( line.split(" ",QString::SkipEmptyParts) );
1104        }
1105        for(int i=0; i<tmpL.length(); i++){
1106          if(tmpL[i].endsWith("\\")){ tmpL[i].chop(1); }
1107          tmpL[i].simplified();
1108          if( tmpL[i].startsWith("bin/") || tmpL[i].startsWith("sbin/") ){
1109            bins << tmpL[i];
1110            if(tmpL[i].contains("$")){ variableBinFound=TRUE; }
1111          } 
1112          if(tmpL[i].endsWith(".png") || line.endsWith(".jpg") || line.endsWith(".svg")){
1113            //Images contained in the port, tag it as a GUI app
1114            if(portStruct[6]=="FALSE"){ qDebug() << " - Detected Graphical Application"; }
1115            portStruct[6] = "TRUE";
1116          }
1117        }
1118      }else{
1119        //do nothing - skip this line
1120      }
1121    }
1122    //qDebug() << "lo:" << listopts << "\nao:" << actualopts;
1123    mkfile.close();
1124
1125    if(!portStruct[3].isEmpty()){portStruct[3].append("###");}
1126    if(!portStruct[4].isEmpty()){portStruct[4].append("###");}
1127    portStruct[3].append(actualopts.join("###"));
1128    portStruct[4].append(bins.join("###"));
1129
1130    if(variableBinFound){return 1;}
1131    else{return 0;}
1132}
1133
1134QString ModBuild::assignPortMenuCategory(QString portDir){
1135  //Assign a menu category based upon the port category
1136 
1137  //Get the port category
1138  QString pc = portDir.section("ports/",1,1).section("/",0,0);
1139  QString mc;
1140  //qDebug() << portDir << pc;
1141  //Create the port -> menu category conversions
1142  QStringList avcat, devcat, edcat, gamecat, gracat,netcat,offcat,setcat,syscat,scicat;
1143  avcat << "audio"<<"multimedia";
1144  devcat << "devel" << "lang" << "java";
1145  edcat << "astro" << "cad" <<"math";
1146  scicat <<"biology"<<"science";
1147  gamecat << "games";
1148  gracat << "graphics";
1149  netcat << "ftp"<<"irc"<<"mail"<<"net"<<"net-im"<<"net-p2p"<<"net-mgmt"<<"www";
1150  offcat << "editors" << "finance" << "news" << "textproc";
1151  setcat << "accessibility"<<"print";
1152  syscat << "sysutils" << "benchmarks" << "emulators" << "ports-mgmt";
1153 
1154  //Assign the menu category
1155  if( avcat.contains(pc) ){ mc="AudioVideo"; }
1156  else if( devcat.contains(pc) ){ mc="Development"; }
1157  else if( edcat.contains(pc) ){ mc="Education"; }
1158  else if( scicat.contains(pc) ){ mc ="Science"; }
1159  else if( gamecat.contains(pc) ){ mc="Game"; }
1160  else if( gracat.contains(pc) ){ mc="Graphics"; }
1161  else if( netcat.contains(pc) ){ mc="Network"; }
1162  else if( offcat.contains(pc) ){ mc="Office"; }
1163  else if( setcat.contains(pc) ){ mc="Settings"; }
1164  else if( syscat.contains(pc) ){ mc="System"; }
1165  else{ mc = "Utility"; }
1166 
1167  return mc;
1168}
1169
1170bool ModBuild::emptyDirectory(QString fullPath){
1171  if(fullPath.endsWith("/")){ fullPath.chop(1); }
1172  QDir rDir(fullPath);
1173  bool success = TRUE;
1174  //Remove files
1175  QStringList cleanFiles = rDir.entryList(QDir::Files | QDir::NoDotAndDotDot);
1176  for(int j=0; j<cleanFiles.length(); j++){ 
1177    bool done = rDir.remove(cleanFiles[j]); 
1178    if(!done) { 
1179      qDebug() << " - File could not be deleted:" << fullPath+"/"+cleanFiles[j]; 
1180      success=FALSE;
1181    } 
1182  }
1183  //Remove Directories
1184  cleanFiles = rDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
1185  for(int j=0; j<cleanFiles.length(); j++){ 
1186  bool isempty = emptyDirectory(fullPath+"/"+cleanFiles[j]); //This is recursive - be careful!!
1187  if(isempty){ rDir.rmdir(cleanFiles[j]); } //only try to remove it if it is empty
1188    else{ success=FALSE; }
1189  }
1190  //Return the result status
1191  return success;
1192}
1193
1194QString ModBuild::checkYesNo(QString chk){
1195  QString ret = "NO";
1196  chk = chk.toLower();
1197  if( (chk=="true") || (chk=="yes") ){ ret = "YES"; }
1198  return ret;
1199}
1200
1201bool ModBuild::writeSampleFreenasControl(){
1202    //convert the program name to the proper format
1203    QString progname = progStruct[0];
1204    progname.toLower().remove(" ").remove("\t").simplified();
1205    //Now create the sample file contents
1206    QStringList contents;
1207    contents << "#!/bin/python";
1208    contents << "import os";
1209    contents << "import platform";
1210    contents << "import re";
1211    contents << "import sys";
1212    contents << "import stat";
1213    contents << "import signal" + QString("\n"); //add an extra line
1214    contents << "from flup.server.fcgi import WSGIServer" + QString("\n"); //add extra line
1215    contents << "HERE = os.path.abspath(os.path.dirname(__file__))";
1216    contents << "sys.path.insert(0, os.path.join(HERE, \"lib/python2.7/site-packages\"))" +QString("\n"); //add extra line
1217    contents << "#Define useful variables for this script";
1218    contents << progname+"_fcgi_pidfile = \"/var/run/"+progname+"_fcgi_server.pid\"";
1219    contents << progname+"_pbi_path = \"/usr/pbi/"+progname+"-\" + platform.machine()";
1220    contents << progname+"_etc_path = os.path.join("+progname+"_pbi_path, \"etc\")";
1221    contents << progname+"_mnt_path = os.path.join("+progname+"_pbi_path, \"mnt\")";
1222    contents << progname+"_www_path = os.path.join("+progname+"_pbi_path, \"www\")";
1223    contents << progname+"_opts = os.path.join("+progname+"_etc_path, \"options\")";
1224    contents << progname+"_control = \"/usr/local/etc/rc.d/"+progname+"\"" + "\n"; //add extra line
1225    contents << "#Define Start function";
1226    contents << "def "+progname+"_fcgi_start(args):";
1227    contents << "  if len(args) < 2:";
1228    contents << "     return False" + QString("\n"); //add extra line
1229    contents << "  ip = args[0]";
1230    contents << "  port = long(args[1])" + QString("\n"); //add extra line
1231    contents << "  pid = os.fork()";
1232    contents << "  if pid < 0:";
1233    contents << "     return False";
1234    contents << "  if pid != 0:";
1235    contents << "     sys.exit(0)" +QString("\n"); //add extra line
1236    contents << "  os.setsid()";
1237    contents << "  os.environ['DJANGO_SETTINGS_MODULE'] = '"+progname+"UI.settings'";
1238    contents << "  import django.core.handlers.wsgi";
1239    contents << "  app = django.core.handlers.wsgi.WSGIHandler()"+QString("\n"); //add extra line
1240    contents << "  res = False";
1241    contents << "  with open("+progname+"_fcgi_pidfile, \"wb\") as fp:";
1242    contents << "     fp.write(str(os.getpid()))";
1243    contents << "     fp.close()"+QString("\n"); //add extra line
1244    contents << "     res = WSGIServer(app, bindAddress=(ip, port)).run()" + QString("\n"); //add extra line
1245    contents << "  return res" + QString("\n"); //add extra line
1246    contents << "#Define Stop function";
1247    contents << "def "+progname+"_fcgi_stop(args):";
1248    contents << "  res = False";
1249    contents << "  if os.access("+progname+"_fcgi_pidfile, os.F_OK):";
1250    contents << "     with open("+progname+"_fcgi_pidfile, \"r\") as fp:";
1251    contents << "        pid = long(fp.read())";
1252    contents << "        fp.close()" +QString("\n"); //add extra line
1253    contents << "        os.kill(pid, signal.SIGHUP)";
1254    contents << "        res = True"+QString("\n"); //add extra line
1255    contents << "  if os.access("+progname+"_fcgi_pidfile, os.F_OK):";
1256    contents << "     os.unlink("+progname+"_fcgi_pidfile)"+"\n";//add extra line
1257    contents << "  return res"+QString("\n");//add extra line
1258    contents << "#Define Status function";
1259    contents << "def "+progname+"_fcgi_status(args):";
1260    contents << "  res = False";
1261    contents << "  if os.access("+progname+"_fcgi_pidfile, os.F_OK):";
1262    contents << "     with open("+progname+"_fcgi_pidfile, \"r\") as fp:";
1263    contents << "        pid = long(fp.read())";
1264    contents << "        fp.close()";
1265    contents << "        res = True"+QString("\n");//add extra line
1266    contents << "  return res"+QString("\n");//add extra line
1267    contents << "#Define Configure function";
1268    contents << "def "+progname+"_fcgi_configure(args):";
1269    contents << "  return True"+QString("\n");//add extra line
1270    contents << "#Define Main function";       
1271    contents << "def main(argc, argv):";
1272    contents << "  if argc < 2:";
1273    contents << "     sys.exit(1)"+QString("\n");//add extra line
1274    contents << "  commands = {";
1275    contents << "     'start': "+progname+"_fcgi_start,";
1276    contents << "     'stop': "+progname+"_fcgi_stop,";
1277    contents << "     'status': "+progname+"_fcgi_status,";
1278    contents << "     'configure': "+progname+"_fcgi_configure";
1279    contents << "  }"+QString("\n");//add extra line   
1280    contents << "  if not commands.has_key(argv[0]):";
1281    contents << "     sys.exit(1)"+QString("\n");//add extra line       
1282    contents << "  if not commands[argv[0]](argv[1:]):";
1283    contents << "     sys.exit(1)"+QString("\n");//add extra line
1284    contents << "  sys.exit(0)"+QString("\n");
1285    contents << "if __name__ == '__main__':";
1286    contents << "  main(len(sys.argv), sys.argv[1:])";
1287    //now save the sample file
1288    QString filepath = this->path()+"/resources/control";
1289    bool status = createFile(filepath, contents);
1290    return status;
1291 
1292       
1293}
1294
1295bool ModBuild::writeSampleFreenasTweakRC(){
1296    //convert the program name to the proper format
1297    QString progname = progStruct[0];
1298    progname.toLower().remove(" ").remove("\t").simplified();
1299    //Now create the sample file contents
1300    QStringList contents;
1301    contents << "#!/bin/sh\n"; //add an extra line after this
1302    contents << "#Setup the temporary program variables";
1303    contents << "program_name="+progname;
1304    contents << "program_path=/usr/pbi/${program_name}-$(uname -m)"+QString("\n"); //add an extra line
1305    contents << "#Perform the modification to /etc/rc.conf";
1306    contents << "tmpfile=$(mktemp /tmp/.XXXXXX)";
1307    contents << "grep -v '${program_name}_' /etc/rc.conf > ${tmpfile}";
1308    contents << "cat ${program_path}/etc/rc.conf >> ${tmpfile}";
1309    contents << "mv ${tmpfile} /etc/rc.conf";
1310    //now save the sample file
1311    QString filepath = this->path()+"/resources/tweak-rcconf";
1312    bool status = createFile(filepath, contents);
1313    return status;
1314}
Note: See TracBrowser for help on using the repository browser.