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

9.1-release9.2-releasereleng/10.0releng/10.0.1releng/10.0.2releng/10.0.3releng/10.1
Last change on this file since 1620346 was 1620346, checked in by Kris Moore <kris@…>, 20 months ago

Initial import of PC-BSD /current/ SVN repo

  • Property mode set to 100644
File size: 52.7 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 fileName = modulePath + "/xdg-menu/" + saveMenuFile;
434  //Create the XDG compliant menu entry
435  QStringList contents;
436  contents << "#!/usr/bin/env";
437  contents << "[Desktop Entry]";
438  contents << "Value=1.0";
439  contents << "Type=Application";
440  contents << "Name="+menuStruct[0];
441  contents << "GenericName="+menuStruct[1];
442  contents << "Exec=%%PBI_EXEDIR%%/"+getFilenameFromPath(menuStruct[2]);
443  contents << "Path=%%PBI_APPDIR%%";
444  contents << "Icon=%%PBI_APPDIR%%/"+getFilenameFromPath(menuStruct[3]);
445  contents << "StartupNotify=true";
446  if(menuStruct[4].toLower()=="yes" || menuStruct[4].toLower()=="true"){
447    contents << "NoDisplay=true";
448  }
449  if(menuStruct[5].toLower()=="yes" || menuStruct[5].toLower()=="true"){
450    contents << "Terminal=true";
451  }
452  contents << "Categories="+menuStruct[6];
453  if(!menuStruct[7].isEmpty()){
454    contents << "MimeType="+menuStruct[7];
455  }
456  //Save the contents to the file
457  bool status;
458  status = createFile(fileName, contents);
459  return status;
460}
461
462bool ModBuild::removeMenu(){
463  //Delete the currently selected menu file
464  bool status = QFile::remove(modulePath+"/xdg-menu/"+saveMenuFile);
465  return status;
466}
467
468bool ModBuild::loadDesktop(QString ifile){
469  //Reset the structure
470  desktopStruct.clear();
471  desktopStruct << ""<<""<<""<<""<<""<<""<<"";
472    //[ name, genericname, exec, icon, nodisplay, terminal, mimetypes]
473       
474  if(ifile.isEmpty()){ return TRUE; } //Also allow this to be a "reset" function for the currently loaded file
475  //Check if the file exists
476  saveDesktopFile = ifile; //save the current filename that is loaded
477  QString filePath = modulePath +"/xdg-desktop/"+ ifile;
478  if(!QFile::exists(filePath)){ return TRUE; } //Get ready to write a currently non-existant file
479  //Read the file and save the data into the structure
480  bool status = FALSE;
481  QFile file(filePath);
482    if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
483      qDebug() << "Error: Could not open xdg-desktop entry file:"<<filePath;
484    }else{
485      QTextStream in(&file);
486      while(!in.atEnd()){
487        QString line = in.readLine().simplified();
488        if(line.startsWith("Name=")){
489          desktopStruct[0] = line.section("=",1,3,QString::SectionSkipEmpty);
490        }else if(line.startsWith("GenericName=")){
491          desktopStruct[1] = line.section("=",1,3,QString::SectionSkipEmpty);
492        }else if(line.startsWith("Exec=")){
493          QString exec = line.section("=",1,3,QString::SectionSkipEmpty);
494          desktopStruct[2] = getFilenameFromPath(exec);
495        }else if(line.startsWith("Icon=")){
496          QString icon = line.section("=",1,3,QString::SectionSkipEmpty);
497          desktopStruct[3] = getFilenameFromPath(icon);
498        }else if(line.startsWith("NoDisplay=")){
499          desktopStruct[4] = line.section("=",1,3,QString::SectionSkipEmpty);
500        }else if(line.startsWith("Terminal=")){
501          desktopStruct[5] = line.section("=",1,3,QString::SectionSkipEmpty);
502        }else if(line.startsWith("MimeType=")){
503          desktopStruct[6] = line.section("=",1,3,QString::SectionSkipEmpty);
504        }else{
505          //Do nothing - ignore this line
506        }
507      }
508      //Check for true/false values
509      if(desktopStruct[4].toLower() != "true"){ desktopStruct[4]="false"; }
510      if(desktopStruct[5].toLower() != "true"){ desktopStruct[5]="false"; }
511      status = TRUE;
512      file.close();
513    }
514    return status;
515}
516
517bool ModBuild::writeDesktop(){
518  //Get the path to the file
519  QString fileName = modulePath + "/xdg-desktop/" + saveDesktopFile;
520  //Create the XDG compliant desktop entry
521  QStringList contents;
522  contents << "#!/usr/bin/env";
523  contents << "[Desktop Entry]";
524  contents << "Value=1.0";
525  contents << "Type=Application";
526  contents << "Name="+desktopStruct[0];
527  contents << "GenericName="+desktopStruct[1];
528  contents << "Exec=%%PBI_EXEDIR%%/"+getFilenameFromPath(desktopStruct[2]);
529  contents << "Path=%%PBI_APPDIR%%";
530  contents << "Icon=%%PBI_APPDIR%%/"+getFilenameFromPath(desktopStruct[3]);
531  contents << "StartupNotify=true";
532  if(desktopStruct[4].toLower()=="yes" || desktopStruct[4].toLower()=="true"){
533    contents << "NoDisplay=true";
534  }
535  if(desktopStruct[5].toLower()=="yes" || desktopStruct[5].toLower()=="true"){
536    contents << "Terminal=true";
537  }
538  if(!desktopStruct[6].isEmpty()){
539    contents << "MimeType="+desktopStruct[6];
540  }
541  //Save the contents to the file
542  bool status;
543  status = createFile(fileName, contents);
544  return status;
545}
546
547bool ModBuild::removeDesktop(){
548  //Delete the currently selected menu file
549  bool status = QFile::remove(modulePath+"/xdg-desktop/"+saveDesktopFile);
550  return status;
551}
552
553bool ModBuild::loadMime(QString ifile){
554  //Reset the structure
555  mimeStruct.clear();
556  mimeStruct << "" << "" << "";  //[ info, type, patterns]
557  if(ifile.isEmpty()){ return TRUE; } //Also allow this to be a "reset" function for the currently loaded file
558  //Check if the file exists
559  saveMimeFile = ifile; //save the current filename that is loaded
560  QString filePath = modulePath +"/xdg-mime/"+ ifile;
561  if(!QFile::exists(filePath)){ return TRUE; } //Get ready to write a currently non-existant file
562  //Read the file and save the data into the structure
563  bool status = FALSE;
564  QFile file(filePath);
565    if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
566      qDebug() << "Error: Could not open xdg-mime entry file:"<<filePath;
567    }else{
568      QTextStream in(&file);
569      QStringList patterns;
570      while(!in.atEnd()){
571        QString line = in.readLine().simplified();
572        if(line.startsWith("<mime-info ")){
573          mimeStruct[0] = line.section(" xmlns=",1,1).section("=",0,0).section(">",0,0).remove("\"").remove("\'");
574        }else if(line.startsWith("<mime-type ")){
575          mimeStruct[1] = line.section(" type=",1,1).section("=",0,0).section(">",0,0).remove("\"").remove("\'");
576        }else if(line.startsWith("<glob ") && line.contains("pattern=")){
577          patterns << line.section("pattern=",1,1).section("=",0,0).section("/>",0,0).remove("\"").remove("\'");
578        }else{
579          //Do nothing - ignore this line
580        }
581      }
582      mimeStruct[2] = patterns.join(" "); //make a space-delimited list
583      //Make sure the detected mime type is in the current list
584      currentMimeTypes << mimeStruct[1];
585      currentMimeTypes.removeDuplicates();
586      //Return
587      status = TRUE;
588      file.close();
589    }
590    return status;
591}
592
593bool ModBuild::writeMime(){
594  //Stupid check to make sure there is actually file patterns to be saved
595  if(mimeStruct[2].isEmpty()){ return FALSE; }
596  //Get the path to the file
597  QString fileName = modulePath + "/xdg-mime/" + saveMimeFile;
598  //Set a default mime-info if none given 
599  if(mimeStruct[0].isEmpty()){
600    mimeStruct[0] = "http://www.freedesktop.org/standards/shared-mime-info";
601  }
602  //Set a default mime type based upon the filename if none given
603  if(mimeStruct[1].isEmpty()){
604    mimeStruct[1] = "application/x-"+saveMimeFile.section("-xdg.xml",0,0);
605  }
606  //Make a list of all the patterns to save
607  QStringList patterns = mimeStruct[2].split(" ");
608  //Create the XDG compliant mime type entry
609  QStringList contents;
610  contents << "<?xml version=\"1.0\"?>";
611  contents << "<mime-info xmlns=\'"+mimeStruct[0]+"\'>";
612  contents << "  <mime-type type=\""+mimeStruct[1]+"\">";
613  for(int i=0; i<patterns.length(); i++){
614    contents << "  <glob weight=\"100\" pattern=\""+patterns[i]+"\"/>";
615  }
616  contents << " </mime-type>";
617  contents << "</mime-info>";
618  //Save the contents to the file
619  bool status;
620  status = createFile(fileName, contents);
621  if(status){ 
622    //If successful, add the new mimetype to the current list
623    currentMimeTypes <<mimeStruct[1]; 
624    currentMimeTypes.removeDuplicates(); 
625  }
626  return status;
627}
628
629bool ModBuild::removeMime(){
630  //Delete the currently selected mime file
631  bool status = QFile::remove(modulePath+"/xdg-mime/"+saveMimeFile);
632  //Remove the current Mime entry is successful
633  if(status){
634    int index = currentMimeTypes.indexOf(mimeStruct[1]);
635    if(index >= 0){ currentMimeTypes.removeAt(index); }
636  }
637  return status;
638}
639
640bool ModBuild::loadExternalLinks(){
641    //reset the structure
642    linksStruct.clear();
643    //Read the new file
644    QFile file(modulePath+"/external-links");
645    if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
646      qDebug() << "Error: Could not open external-links file:"<<modulePath+"/external-links";
647      return FALSE;
648    }
649    QTextStream in(&file);
650    QString original, linkto, types;
651    while(!in.atEnd()){
652      QString line = in.readLine();
653      line.replace("\t"," ").simplified();
654      if(!line.startsWith("#") && !line.isEmpty()){
655        original = line.section(" ",0,0,QString::SectionSkipEmpty);
656        linkto =  line.section(" ",1,1,QString::SectionSkipEmpty);
657        types =  line.section(" ",2,2,QString::SectionSkipEmpty);
658        //Add the link to the structure
659        linksStruct << original+"::"+linkto+"::"+types;
660      }else{
661        //Do nothing - ignore this line
662      }
663    }
664    file.close();
665    return TRUE;
666}
667
668bool ModBuild::writeExternalLinks(){
669  //File to write
670  QString fileName = modulePath+"/external-links";
671  //Get the contents to write to the file
672  QStringList contents;
673  //Standard Header for file
674  contents << "# Files to be Sym-Linked into the default LOCALBASE";
675  contents << "# One per-line, relative to %%PBI_APPDIR%% and LOCALBASE";
676  contents << "# Defaults to keeping any existing files in LOCALBASE";
677  contents << "# Use action \"binary\" for binaries that need wrapper functionality\n";
678  contents << "# TARGET             LINK IN LOCALBASE         ACTION";
679  //add the desired binaries
680  for(int i=0; i<linksStruct.length(); i++){
681    QStringList link = linksStruct[i].split("::");
682    qDebug() << "save link:" << link;
683    contents << link[0] +"     "+link[1]+"     "+link[2];
684  }
685  //Create the external-links file
686  bool status = createFile(fileName, contents);
687  return status;
688}
689
690void ModBuild::addExternalLink(QString file, QString linkto, QStringList types){
691  QString chk = file+"::"+linkto+"::"; //try to match the file and link, not types
692  for(int i=0; i<linksStruct.length(); i++){
693    if( linksStruct[i].startsWith(chk) ){
694      //This link already exists -> update the types and return
695      linksStruct[i] = chk+types.join(",");
696      return;
697    }
698  }     
699  //This is a new link, add it to the end
700  linksStruct << chk+types.join(",");
701}
702
703void ModBuild::removeExternalLink(QString file, QString linkto){
704  QString chk = file+"::"+linkto+"::"; //try to match the file and link, not types
705  for(int i=0; i<linksStruct.length(); i++){
706    if( linksStruct[i].startsWith(chk) ){
707      //This link already exists -> remove it
708      linksStruct.removeAt(i);
709      return;
710    }
711  }     
712}
713
714QStringList ModBuild::externalLinks(){
715  //Return the currently available links
716  QStringList output = linksStruct;
717  output.replaceInStrings("::","\t");
718  return output;
719}
720
721bool ModBuild::addResource(bool isNewWrapper, QString resourcePath){
722  if(resourcePath.isEmpty()){
723    qDebug() << "Warning: no resource selected";
724    return TRUE;  // Do not flag this as an error in the function
725  }
726  //Determine the type of file that is being added
727  bool isIcon=FALSE;
728  if(resourcePath.endsWith(".png") && !isNewWrapper){ isIcon = TRUE; }
729  //Create the full path to the location within the module 
730  QString newResourcePath = modulePath + "/resources/";
731  if(isNewWrapper){ 
732    newResourcePath.append("bin/"); 
733    //Make sure the "bin" directory exists
734    QDir dir(newResourcePath);
735    if(!dir.exists()){ dir.cdUp(); dir.mkdir("bin"); } 
736  }else{
737    //resourcePath *MUST* be an absolute path (start with "/")
738    if( !resourcePath.startsWith("/") ){
739      qDebug() << "Error: Path to desired resource is not an absolute path!";
740      return FALSE;
741    }
742    //Check that the desired resource exists
743    if( !QFile::exists(resourcePath) ){
744      qDebug() << "Error: Desired resource does not exist: " << resourcePath;
745      return FALSE;
746    }
747  }
748  QString resource = getFilenameFromPath(resourcePath);
749  newResourcePath.append(resource);
750  //Check if the resource already exists in the module
751  if( QFile::exists(newResourcePath) ){
752    qDebug() << "Overwriting existing module resource:" << resource;
753    QFile::remove(newResourcePath); 
754  }
755  //Copy the resource into the module
756  bool status=FALSE;
757  if(isNewWrapper){
758    //Create a standard wrapper script in the module
759    QStringList form;
760    form << "#!/bin/sh";
761    form << "#This is a sample wrapper script form generated by EasyPBI";
762    form << "# REMINDER: Add this file to the external-links for the PBI as well!";
763    form << "\n#Setup some internal script variables";
764    form << "PROGDIR=`pwd | cut -d / -f 1-4`   #Base PBI directory (/usr/pbi/myapplication-<arch>)";
765    form << "APPBINARY=bin/SAMPLE   #application binary";
766    form << "\n#--- DO SOMETHING HERE ---\n";
767    form << "#Now start the main application";
768    form << "${PROGDIR}/${APPBINARY} $@";
769    status = createFile(newResourcePath,form);
770    //Make sure the new wrapper is executable
771    QFile::setPermissions(newResourcePath, QFile::ReadOwner | QFile::ExeOwner | QFile::WriteOwner  | QFile::ReadGroup | QFile::ExeGroup  | QFile::WriteGroup | QFile::ReadOther | QFile::ExeOther);
772  }else{
773    //Copy the given file into the module
774    if( !QFile::copy(resourcePath,newResourcePath) ){
775      qDebug() << "Error copying resource into the module";
776    }else{
777      status=TRUE;
778    }
779  }
780  //Add the item to the appropriate lists
781  if(status && isIcon){ currentIcons << resource;  currentIcons.removeDuplicates();}
782  else if(status && isNewWrapper){ currentBins << "bin/"+resource; currentBins.removeDuplicates();}
783 
784  return status;
785}
786
787bool ModBuild::removeResource(QString filePath){
788  //filePath is relative to the module's resource directory
789       
790  //Determine if it is a wrapper script, icon, or other
791  bool isWrapper=FALSE; bool isIcon=FALSE;
792  if(filePath.startsWith("bin/")){ isWrapper=TRUE; }
793  else if(filePath.endsWith(".png")){ isIcon=TRUE; }
794  //Check if the file exists
795  bool status=FALSE;
796  QDir dir(modulePath+"/resources");
797  if(dir.exists(filePath)){
798    status = dir.remove(filePath);
799  }else{
800    qDebug() << "Error removing non-existant resource:"+filePath;
801  }
802  //Remove the item from the appropriate lists
803  if(status && isIcon){ currentIcons.removeAll(filePath); }
804  else if(status && isWrapper){ 
805    currentBins.removeAll(filePath); 
806    dir.cd("bin");
807    if(dir.entryList(QDir::Files | QDir::NoDotAndDotDot).isEmpty() ){ 
808      dir.cdUp();
809      dir.rmdir("bin");
810    }       
811  }
812 
813  return status;
814}
815
816bool ModBuild::createFile(QString fileName, QStringList contents){
817//fileName = full path to file (I.E. /home/pcbsd/junk/junk.txt)
818//contents = list of lines to be written (one line per entry in the list - no newline needed at the end of an entry)
819
820  //Remove any existing file with that name/location
821  if( QFile::exists(fileName) ){
822    if( !QFile::remove(fileName) ){
823      qDebug() << fileName+": Error -- Could not overwrite existing file";
824      return FALSE;
825    }
826  }
827  //Open the file with .tmp extension
828  QFile file(fileName+".tmp");
829  if( !file.open(QIODevice::WriteOnly | QIODevice::Text) ){
830    qDebug() << fileName+".tmp: Failure -- Could not open file";
831    return FALSE;
832  }
833  //Write the file
834  QTextStream ofile(&file); //start the output stream
835  for(int i=0; i<contents.length(); i++){
836    ofile << contents[i];
837    ofile << "\n";
838  }
839  //Close the File
840  file.close();
841  //Move the temporary file into its final location
842  if( !file.rename(fileName) ){
843    qDebug() << fileName+": Error: Could not rename "+fileName+".tmp as "+fileName;
844    return FALSE;
845  }
846  //Return success
847  QString extra = QDir::homePath() + "/EasyPBI/Modules/"; //remove this from the filename display
848  qDebug() << "Created:" << fileName.remove(extra);
849  return TRUE;
850}
851
852QStringList ModBuild::readFile(QString filePath){
853  QStringList contents;
854  //Check that the file exists first
855  if(!QFile::exists(filePath)){ 
856    qDebug() << "Error: file to read does not exist:" << filePath;
857    return contents; 
858  }
859  //Open the file for reading
860  QFile file(filePath);
861  if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){ 
862    qDebug() << "Error: file could not be opened for reading:"<< filePath;
863    return contents; 
864  }
865  QTextStream in(&file);
866  //Save the contents as a QStringList
867  while(!in.atEnd()){
868    contents << in.readLine();
869  }
870  //Return the contents (one entry per line)
871  return contents;
872}
873
874QStringList ModBuild::filesAvailable(QString group){
875  QStringList fileList; 
876  //Check to make sure that a module has been selected first
877  if(modulePath.isEmpty()){ return fileList; }
878  QDir dir(modulePath);
879  group = group.toLower();
880  if(group=="resources"){
881    dir.cd(group);
882    //Get all the files in the main directory
883    fileList = dir.entryList(QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden);
884    //Get all the files in any sub-directories
885    QStringList subdirs = dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
886    for(int i=0; i<subdirs.length(); i++){
887      dir.cd(subdirs[i]);
888      QStringList dirL = dir.entryList(QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden);
889      for(int j=0; j<dirL.length(); j++){
890        fileList << subdirs[i]+"/"+dirL[j];
891      }
892      dir.cdUp();
893    }
894  }else if(group=="xdg-desktop"){
895    dir.cd(group);
896    //Get all the files in the main directory
897    fileList = dir.entryList(QStringList("*.desktop") , QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden);
898  }else if(group=="xdg-menu"){
899    dir.cd(group);
900    //Get all the files in the main directory
901    fileList = dir.entryList(QStringList("*.desktop"), QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden);       
902  }else if(group=="xdg-mime"){
903    dir.cd(group);
904    //Get all the files in the main directory
905    fileList = dir.entryList(QStringList("*.xml"), QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden);   
906  }else if(group=="scripts"){
907    dir.cd(group);
908    //Get all the files in the main directory
909    fileList = dir.entryList(QStringList("*.sh"), QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden);     
910  }
911  return fileList; //This will return nothing if an invalid group is given
912}
913
914QString ModBuild::readValue(QString variable){
915//progStruct=[ name, website, author, icon, version, packageDir]
916//mkStruct=[ makeport, portbefore, portafter, makeoptions]
917//serverStruct=[ needroot, buildkey, priority, noTMPFS]
918//menuStruct=[ name, genericname, exec, icon, nodisplay, terminal, categories, mimetype]
919//desktopStruct=[ name, genericname, exec, icon, nodisplay, terminal, mimetype]
920//mimeStruct=[ info, type, patterns] (patterns is a " "-delimited list)
921//portStruct=[ name, author, website, options, binaries, menu-category, isGraphical ]   
922  QString val;
923  QString var = variable.toLower();
924  if(var=="progname"){ val = progStruct[0] ;}
925  else if(var=="progweb"){ val = progStruct[1]; }
926  else if(var=="progauthor"){ val = progStruct[2]; }
927  else if(var=="progicon"){ val = progStruct[3]; }
928  else if(var=="progversion"){ val = progStruct[4]; }
929  else if(var=="packagedir"){ val = progStruct[5]; }
930  else if(var=="makeport"){ val = mkStruct[0]; }
931  else if(var=="makeportbefore"){ val = mkStruct[1]; }
932  else if(var=="makeportafter"){ val = mkStruct[2]; }
933  else if(var=="makeoptions"){ val = mkStruct[3]; }
934  else if(var=="requiresroot"){ val = serverStruct[0]; }
935  else if(var=="buildkey"){ val = serverStruct[1]; }
936  else if(var=="priority"){ val = serverStruct[2]; }
937  else if(var=="notmpfs"){ val = serverStruct[3]; }
938  else if(var=="menuname"){ val = menuStruct[0]; }
939  else if(var=="menugenericname"){ val = menuStruct[1]; }
940  else if(var=="menuexec"){ val = menuStruct[2]; }
941  else if(var=="menuicon"){ val = menuStruct[3]; }
942  else if(var=="menunodisplay"){ val = menuStruct[4] ;}
943  else if(var=="menuterminal"){ val = menuStruct[5]; }
944  else if(var=="menucategories"){ val = menuStruct[6]; }
945  else if(var=="menumimetype"){ val = menuStruct[7]; }
946  else if(var=="desktopname"){ val = desktopStruct[0]; }
947  else if(var=="desktopgenericname"){ val = desktopStruct[1]; }
948  else if(var=="desktopexec"){ val = desktopStruct[2]; }
949  else if(var=="desktopicon"){ val = desktopStruct[3]; }
950  else if(var=="desktopnodisplay"){ val = desktopStruct[4]; }
951  else if(var=="desktopterminal"){ val = desktopStruct[5]; }
952  else if(var=="desktopmimetype"){ val = desktopStruct[6]; }
953  else if(var=="mimeinfo"){ val = mimeStruct[0]; }
954  else if(var=="mimetype"){ val = mimeStruct[1]; }
955  else if(var=="mimepatterns"){ val = mimeStruct[2]; }
956  else if(var=="portname"){ val = portStruct[0]; }
957  else if(var=="portauthor"){ val = portStruct[1]; }
958  else if(var=="portwebsite"){ val = portStruct[2]; }
959  else if(var=="portoptions"){ val = portStruct[3]; }
960  else if(var=="portbinaries"){ val = portStruct[4]; }
961  else if(var=="portmenucategory"){ val = portStruct[5]; }
962  else if(var=="portisgraphical"){ val = portStruct[6]; }
963  else{
964    qDebug() << "Error: Invalid variable name to read";
965  }
966  return val;
967}
968
969bool ModBuild::writeValue(QString variable, QString value){
970//progStruct=[ name, website, author, icon, version, packageDir]
971//mkStruct=[ makeport, portbefore, portafter, makeoptions]
972//serverStruct=[ needroot, buildkey, priority, noTMPFS]
973//menuStruct=[ name, genericname, exec, icon, nodisplay, terminal, categories, mimetype]
974//desktopStruct=[ name, genericname, exec, icon, nodisplay, terminal, mimetype]
975//mimeStruct=[ info, type, patterns] (patterns is a " "-delimited list)
976  QString var = variable.toLower();
977  if(var=="progname"){ progStruct[0] = value; }
978  else if(var=="progweb"){ progStruct[1] = value; }
979  else if(var=="progauthor"){ progStruct[2] = value; }
980  else if(var=="progicon"){ progStruct[3] = value; }
981  else if(var=="progversion"){ progStruct[4] = value; }
982  else if(var=="packagedir"){ progStruct[5] = value; }
983  else if(var=="makeport"){ mkStruct[0] = value; }
984  else if(var=="makeportbefore"){ mkStruct[1] = value; }
985  else if(var=="makeportafter"){ mkStruct[2] = value; }
986  else if(var=="makeoptions"){ mkStruct[3] = value; }
987  else if(var=="requiresroot"){ serverStruct[0] = value; }
988  else if(var=="buildkey"){ serverStruct[1] = value; }
989  else if(var=="priority"){ serverStruct[2] = value; }
990  else if(var=="notmpfs"){ serverStruct[3] = value; }
991  else if(var=="menuname"){ menuStruct[0] = value; }
992  else if(var=="menugenericname"){ menuStruct[1] = value; }
993  else if(var=="menuexec"){ menuStruct[2] = value; }
994  else if(var=="menuicon"){ menuStruct[3] = value; }
995  else if(var=="menunodisplay"){ menuStruct[4] = value; }
996  else if(var=="menuterminal"){ menuStruct[5] = value; }
997  else if(var=="menucategories"){ menuStruct[6] = value; }
998  else if(var=="menumimetype"){ menuStruct[7] = value; }
999  else if(var=="desktopname"){ desktopStruct[0] = value; }
1000  else if(var=="desktopgenericname"){ desktopStruct[1] = value; }
1001  else if(var=="desktopexec"){ desktopStruct[2] = value; }
1002  else if(var=="desktopicon"){ desktopStruct[3] = value; }
1003  else if(var=="desktopnodisplay"){ desktopStruct[4] = value; }
1004  else if(var=="desktopterminal"){ desktopStruct[5] = value; }
1005  else if(var=="desktopmimetype"){ desktopStruct[6] = value; }
1006  else if(var=="mimeinfo"){ mimeStruct[0] = value; }
1007  else if(var=="mimetype"){ mimeStruct[1] = value; }
1008  else if(var=="mimepatterns"){ mimeStruct[2] = value; }
1009  else{
1010    qDebug() << "Error: Invalid variable name to read";
1011    return FALSE;
1012  }
1013  return TRUE;
1014}
1015
1016void ModBuild::compressModule(){
1017  //Compress the module directory
1018  QString localDir = getFilenameFromPath(modulePath);
1019  QString cmd = "cd "+modulePath+"/..; tar czf "+localDir+".tar.gz "+localDir;
1020  qDebug() << "Compressing module:" << modulePath+".tar.gz";
1021  system( cmd.toUtf8() );
1022  return;
1023}
1024
1025QString ModBuild::getFilenameFromPath(QString fullPath){
1026  if(fullPath.isEmpty()){return "";}
1027  return fullPath.section("/",-1);
1028}
1029
1030int ModBuild::readMakeFile(QString makefile){
1031  bool variableBinFound=FALSE;
1032  QStringList bins, actualopts, listopts;
1033
1034    QFile mkfile(makefile);
1035    if(!mkfile.open(QIODevice::ReadOnly) ){
1036      return -1;
1037    }
1038    QTextStream mkin(&mkfile);
1039    while( !mkin.atEnd() ){
1040      QString line = mkin.readLine().simplified();
1041      // ---- Program Name -----
1042      if( line.startsWith("PORTNAME") ){
1043        portStruct[0] = line.replace("\t"," ").section(" ",1,1,QString::SectionSkipEmpty);
1044        qDebug() << " - Found portname:" << portStruct[0];
1045      // ---- Port Maintainer -----
1046      }else if( line.startsWith("MAINTAINER") ){
1047        portStruct[1] = line.replace("\t"," ").section(" ",1,1,QString::SectionSkipEmpty);
1048        qDebug() << " - Found maintainer:"<< portStruct[1];
1049      // ---- OptionsNG Framework --------
1050      }else if( line.startsWith("OPTIONS_DEFINE") || line.startsWith("OPTIONS_SINGLE_") || line.startsWith("OPTIONS_MULTI_") ){
1051        listopts << line.replace("\t"," ").section("=",1,50,QString::SectionSkipEmpty).simplified().split(" "); 
1052        while( line.simplified().endsWith("\\")){
1053          line = mkin.readLine().replace("\t"," ");
1054          listopts << line.split(" ");
1055        }
1056      }else if( line.startsWith("OPTIONS_DEFAULT") ){
1057        QStringList defaults = line.replace("\t"," ").section("=",1,50,QString::SectionSkipEmpty).simplified().split(" ");     
1058        for(int i=0; i<listopts.length(); i++){
1059          if(defaults.contains(listopts[i].simplified()) ){ 
1060            actualopts << "WITHOUT_"+listopts[i].simplified();
1061          }else{
1062            actualopts << "WITH_"+listopts[i].simplified();
1063          }
1064        }
1065      // ---- Legacy Options Framework -------
1066      }else if( line.startsWith("OPTIONS") && !line.startsWith("OPTIONS_") ){
1067        listopts << line.replace("\t"," ").section(" ",1,1,QString::SectionSkipEmpty); 
1068        while( line.simplified().endsWith("\\")){
1069          line = mkin.readLine().replace("\t"," ");
1070          listopts << line.section(" ",0,0,QString::SectionSkipEmpty).simplified();
1071        }
1072      }else if( line.startsWith(".if defined(") || line.startsWith(".if !defined(") ){
1073        //Get the variable used (we need WITH_* or WITHOUT_*)
1074        QString var = line.section("(",1,1).section(")",0,0).simplified();
1075        //qDebug() << line << var << var.section("_",1,1);
1076        if( var.startsWith("WITH") && listopts.contains( var.section("_",1,1) ) ){
1077          actualopts << var;
1078        }
1079      // ---- Program Website -----
1080      }else if( line.startsWith("WWW") ){
1081          portStruct[2] = line.section(" ",1,1,QString::SectionSkipEmpty);
1082          qDebug() << " - Found website:"<< portStruct[2];
1083      // ---- Program Binaries/Files ----
1084      }else if( line.startsWith("PLIST_FILES") ){ //catch PLIST_FILES= and PLIST_FILES+=
1085        QStringList tmpL = line.section(" ",1,60).split(" ",QString::SkipEmptyParts);
1086        while( line.simplified().endsWith("\\") ){
1087          line = mkin.readLine().replace("\t"," ");
1088          tmpL.append( line.split(" ",QString::SkipEmptyParts) );
1089        }
1090        for(int i=0; i<tmpL.length(); i++){
1091          if(tmpL[i].endsWith("\\")){ tmpL[i].chop(1); }
1092          tmpL[i].simplified();
1093          if( tmpL[i].startsWith("bin/") || tmpL[i].startsWith("sbin/") ){
1094            bins << tmpL[i];
1095            if(tmpL[i].contains("$")){ variableBinFound=TRUE; }
1096          } 
1097          if(tmpL[i].endsWith(".png") || line.endsWith(".jpg") || line.endsWith(".svg")){
1098            //Images contained in the port, tag it as a GUI app
1099            if(portStruct[6]=="FALSE"){ qDebug() << " - Detected Graphical Application"; }
1100            portStruct[6] = "TRUE";
1101          }
1102        }
1103      }else{
1104        //do nothing - skip this line
1105      }
1106    }
1107    //qDebug() << "lo:" << listopts << "\nao:" << actualopts;
1108    mkfile.close();
1109
1110    if(!portStruct[3].isEmpty()){portStruct[3].append("###");}
1111    if(!portStruct[4].isEmpty()){portStruct[4].append("###");}
1112    portStruct[3].append(actualopts.join("###"));
1113    portStruct[4].append(bins.join("###"));
1114
1115    if(variableBinFound){return 1;}
1116    else{return 0;}
1117}
1118
1119QString ModBuild::assignPortMenuCategory(QString portDir){
1120  //Assign a menu category based upon the port category
1121 
1122  //Get the port category
1123  QString pc = portDir.section("ports/",1,1).section("/",0,0);
1124  QString mc;
1125  //qDebug() << portDir << pc;
1126  //Create the port -> menu category conversions
1127  QStringList avcat, devcat, edcat, gamecat, gracat,netcat,offcat,setcat,syscat,scicat;
1128  avcat << "audio"<<"multimedia";
1129  devcat << "devel" << "lang" << "java";
1130  edcat << "astro" << "cad" <<"math";
1131  scicat <<"biology"<<"science";
1132  gamecat << "games";
1133  gracat << "graphics";
1134  netcat << "ftp"<<"irc"<<"mail"<<"net"<<"net-im"<<"net-p2p"<<"net-mgmt"<<"www";
1135  offcat << "editors" << "finance" << "news" << "textproc";
1136  setcat << "accessibility"<<"print";
1137  syscat << "sysutils" << "benchmarks" << "emulators" << "ports-mgmt";
1138 
1139  //Assign the menu category
1140  if( avcat.contains(pc) ){ mc="AudioVideo"; }
1141  else if( devcat.contains(pc) ){ mc="Development"; }
1142  else if( edcat.contains(pc) ){ mc="Education"; }
1143  else if( scicat.contains(pc) ){ mc ="Science"; }
1144  else if( gamecat.contains(pc) ){ mc="Game"; }
1145  else if( gracat.contains(pc) ){ mc="Graphics"; }
1146  else if( netcat.contains(pc) ){ mc="Network"; }
1147  else if( offcat.contains(pc) ){ mc="Office"; }
1148  else if( setcat.contains(pc) ){ mc="Settings"; }
1149  else if( syscat.contains(pc) ){ mc="System"; }
1150  else{ mc = "Utility"; }
1151 
1152  return mc;
1153}
1154
1155bool ModBuild::emptyDirectory(QString fullPath){
1156  if(fullPath.endsWith("/")){ fullPath.chop(1); }
1157  QDir rDir(fullPath);
1158  bool success = TRUE;
1159  //Remove files
1160  QStringList cleanFiles = rDir.entryList(QDir::Files | QDir::NoDotAndDotDot);
1161  for(int j=0; j<cleanFiles.length(); j++){ 
1162    bool done = rDir.remove(cleanFiles[j]); 
1163    if(!done) { 
1164      qDebug() << " - File could not be deleted:" << fullPath+"/"+cleanFiles[j]; 
1165      success=FALSE;
1166    } 
1167  }
1168  //Remove Directories
1169  cleanFiles = rDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
1170  for(int j=0; j<cleanFiles.length(); j++){ 
1171  bool isempty = emptyDirectory(fullPath+"/"+cleanFiles[j]); //This is recursive - be careful!!
1172  if(isempty){ rDir.rmdir(cleanFiles[j]); } //only try to remove it if it is empty
1173    else{ success=FALSE; }
1174  }
1175  //Return the result status
1176  return success;
1177}
1178
1179QString ModBuild::checkYesNo(QString chk){
1180  QString ret = "NO";
1181  chk = chk.toLower();
1182  if( (chk=="true") || (chk=="yes") ){ ret = "YES"; }
1183  return ret;
1184}
1185
1186bool ModBuild::writeSampleFreenasControl(){
1187    //convert the program name to the proper format
1188    QString progname = progStruct[0];
1189    progname.toLower().remove(" ").remove("\t").simplified();
1190    //Now create the sample file contents
1191    QStringList contents;
1192    contents << "#!/bin/python";
1193    contents << "import os";
1194    contents << "import platform";
1195    contents << "import re";
1196    contents << "import sys";
1197    contents << "import stat";
1198    contents << "import signal" + QString("\n"); //add an extra line
1199    contents << "from flup.server.fcgi import WSGIServer" + QString("\n"); //add extra line
1200    contents << "HERE = os.path.abspath(os.path.dirname(__file__))";
1201    contents << "sys.path.insert(0, os.path.join(HERE, \"lib/python2.7/site-packages\"))" +QString("\n"); //add extra line
1202    contents << "#Define useful variables for this script";
1203    contents << progname+"_fcgi_pidfile = \"/var/run/"+progname+"_fcgi_server.pid\"";
1204    contents << progname+"_pbi_path = \"/usr/pbi/"+progname+"-\" + platform.machine()";
1205    contents << progname+"_etc_path = os.path.join("+progname+"_pbi_path, \"etc\")";
1206    contents << progname+"_mnt_path = os.path.join("+progname+"_pbi_path, \"mnt\")";
1207    contents << progname+"_www_path = os.path.join("+progname+"_pbi_path, \"www\")";
1208    contents << progname+"_opts = os.path.join("+progname+"_etc_path, \"options\")";
1209    contents << progname+"_control = \"/usr/local/etc/rc.d/"+progname+"\"" + "\n"; //add extra line
1210    contents << "#Define Start function";
1211    contents << "def "+progname+"_fcgi_start(args):";
1212    contents << "  if len(args) < 2:";
1213    contents << "     return False" + QString("\n"); //add extra line
1214    contents << "  ip = args[0]";
1215    contents << "  port = long(args[1])" + QString("\n"); //add extra line
1216    contents << "  pid = os.fork()";
1217    contents << "  if pid < 0:";
1218    contents << "     return False";
1219    contents << "  if pid != 0:";
1220    contents << "     sys.exit(0)" +QString("\n"); //add extra line
1221    contents << "  os.setsid()";
1222    contents << "  os.environ['DJANGO_SETTINGS_MODULE'] = '"+progname+"UI.settings'";
1223    contents << "  import django.core.handlers.wsgi";
1224    contents << "  app = django.core.handlers.wsgi.WSGIHandler()"+QString("\n"); //add extra line
1225    contents << "  res = False";
1226    contents << "  with open("+progname+"_fcgi_pidfile, \"wb\") as fp:";
1227    contents << "     fp.write(str(os.getpid()))";
1228    contents << "     fp.close()"+QString("\n"); //add extra line
1229    contents << "     res = WSGIServer(app, bindAddress=(ip, port)).run()" + QString("\n"); //add extra line
1230    contents << "  return res" + QString("\n"); //add extra line
1231    contents << "#Define Stop function";
1232    contents << "def "+progname+"_fcgi_stop(args):";
1233    contents << "  res = False";
1234    contents << "  if os.access("+progname+"_fcgi_pidfile, os.F_OK):";
1235    contents << "     with open("+progname+"_fcgi_pidfile, \"r\") as fp:";
1236    contents << "        pid = long(fp.read())";
1237    contents << "        fp.close()" +QString("\n"); //add extra line
1238    contents << "        os.kill(pid, signal.SIGHUP)";
1239    contents << "        res = True"+QString("\n"); //add extra line
1240    contents << "  if os.access("+progname+"_fcgi_pidfile, os.F_OK):";
1241    contents << "     os.unlink("+progname+"_fcgi_pidfile)"+"\n";//add extra line
1242    contents << "  return res"+QString("\n");//add extra line
1243    contents << "#Define Status function";
1244    contents << "def "+progname+"_fcgi_status(args):";
1245    contents << "  res = False";
1246    contents << "  if os.access("+progname+"_fcgi_pidfile, os.F_OK):";
1247    contents << "     with open("+progname+"_fcgi_pidfile, \"r\") as fp:";
1248    contents << "        pid = long(fp.read())";
1249    contents << "        fp.close()";
1250    contents << "        res = True"+QString("\n");//add extra line
1251    contents << "  return res"+QString("\n");//add extra line
1252    contents << "#Define Configure function";
1253    contents << "def "+progname+"_fcgi_configure(args):";
1254    contents << "  return True"+QString("\n");//add extra line
1255    contents << "#Define Main function";       
1256    contents << "def main(argc, argv):";
1257    contents << "  if argc < 2:";
1258    contents << "     sys.exit(1)"+QString("\n");//add extra line
1259    contents << "  commands = {";
1260    contents << "     'start': "+progname+"_fcgi_start,";
1261    contents << "     'stop': "+progname+"_fcgi_stop,";
1262    contents << "     'status': "+progname+"_fcgi_status,";
1263    contents << "     'configure': "+progname+"_fcgi_configure";
1264    contents << "  }"+QString("\n");//add extra line   
1265    contents << "  if not commands.has_key(argv[0]):";
1266    contents << "     sys.exit(1)"+QString("\n");//add extra line       
1267    contents << "  if not commands[argv[0]](argv[1:]):";
1268    contents << "     sys.exit(1)"+QString("\n");//add extra line
1269    contents << "  sys.exit(0)"+QString("\n");
1270    contents << "if __name__ == '__main__':";
1271    contents << "  main(len(sys.argv), sys.argv[1:])";
1272    //now save the sample file
1273    QString filepath = this->path()+"/resources/control";
1274    bool status = createFile(filepath, contents);
1275    return status;
1276 
1277       
1278}
1279
1280bool ModBuild::writeSampleFreenasTweakRC(){
1281    //convert the program name to the proper format
1282    QString progname = progStruct[0];
1283    progname.toLower().remove(" ").remove("\t").simplified();
1284    //Now create the sample file contents
1285    QStringList contents;
1286    contents << "#!/bin/sh\n"; //add an extra line after this
1287    contents << "#Setup the temporary program variables";
1288    contents << "program_name="+progname;
1289    contents << "program_path=/usr/pbi/${program_name}-$(uname -m)"+QString("\n"); //add an extra line
1290    contents << "#Perform the modification to /etc/rc.conf";
1291    contents << "tmpfile=$(mktemp /tmp/.XXXXXX)";
1292    contents << "grep -v '${program_name}_' /etc/rc.conf > ${tmpfile}";
1293    contents << "cat ${program_path}/etc/rc.conf >> ${tmpfile}";
1294    contents << "mv ${tmpfile} /etc/rc.conf";
1295    //now save the sample file
1296    QString filepath = this->path()+"/resources/tweak-rcconf";
1297    bool status = createFile(filepath, contents);
1298    return status;
1299}
Note: See TracBrowser for help on using the repository browser.