source: src-qt4/EasyPBI/modBuild.cpp @ 2aa342f

9.2-releasereleng/10.0releng/10.0.1releng/10.0.2
Last change on this file since 2aa342f was 2aa342f, checked in by Ken Moore <ken@…>, 10 months ago

Fix building EasyPBI on 10.x

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