Blob Blame History Raw
/* -*- Mode: C++; c-default-style: "k&r"; indent-tabs-mode: nil; tab-width: 2; c-basic-offset: 2 -*- */

/* libmwaw: tools
* Version: MPL 2.0 / LGPLv2+
*
* The contents of this file are subject to the Mozilla Public License Version
* 2.0 (the "License"); you may not use this file except in compliance with
* the License or as specified alternatively below. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* Major Contributor(s):
* Copyright (C) 2011, 2012 Alonso Laurent (alonso@loria.fr)
*
*
* All Rights Reserved.
*
* For minor contributions see the git repository.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
* in which case the provisions of the LGPLv2+ are applicable
* instead of those above.
*/

#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <iomanip>
#include <iostream>
#include <memory>
#include <string>
#include <sstream>

#include "file_internal.h"
#include "input.h"
#include "ole.h"
#include "rsrc.h"
#include "xattr.h"

#include <sys/stat.h>

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifndef VERSION
#define VERSION "UNKNOWN VERSION"
#endif

namespace libmwaw_tools
{
class Exception
{
};

struct File {
  //! the constructor
  explicit File(char const *path)
    : m_fName(path ? path : "")
    , m_fInfoCreator("")
    , m_fInfoType("")
    , m_fInfoResult("")
    , m_fileVersion()
    , m_appliVersion()
    , m_rsrcMissingMessage("")
    , m_rsrcResult("")
    , m_dataResult()
    , m_printFileName(false)
  {
    if (m_fName.empty()) {
      std::cerr << "File::File: call without path\n";
      throw libmwaw_tools::Exception();
    }

    // check if it is a regular file
    struct stat status;
    if (!path || stat(path, &status) == -1) {
      std::cerr << "File::File: the file " << m_fName << " cannot be read\n";
      throw libmwaw_tools::Exception();
    }
    if (!S_ISREG(status.st_mode)) {
      std::cerr << "File::File: the file " << m_fName << " is a not a regular file\n";
      throw libmwaw_tools::Exception();
    }
  }

  //! operator<<
  friend std::ostream &operator<<(std::ostream &o, File const &info)
  {
    if (info.m_printFileName)
      o << info.m_fName << ":\n";
    if (info.m_fInfoCreator.length() || info.m_fInfoType.length()) {
      o << "------- fileInfo -------\n";
      if (info.m_fInfoCreator.length())
        o << "\tcreator=" << info.m_fInfoCreator << "\n";
      if (info.m_fInfoType.length())
        o << "\ttype=" << info.m_fInfoType << "\n";
      if (info.m_fInfoResult.length())
        o << "\t\t=>" << info.m_fInfoResult << "\n";
    }
    if (info.m_fileVersion.ok() || info.m_appliVersion.ok() ||
        info.m_rsrcMissingMessage.length() || info.m_rsrcResult.length()) {
      o << "------- resource fork -------\n";
      if (info.m_fileVersion.ok())
        o << "\tFile" << info.m_fileVersion << "\n";
      if (info.m_appliVersion.ok())
        o << "\tAppli" << info.m_appliVersion << "\n";
      if (info.m_rsrcMissingMessage.length())
        o << "\tmissingString=\"" << info.m_rsrcMissingMessage << "\"\n";
      if (info.m_rsrcResult.length())
        o << "\t\t=>" << info.m_rsrcResult << "\n";
    }
    if (info.m_dataResult.size()) {
      o << "------- data fork -------\n";
      for (auto const &res : info.m_dataResult)
        o << "\t\t=>" << res << "\n";
    }
    return o;
  }

  //! try to read the file information
  bool readFileInformation();
  //! try to read the data fork
  bool readDataInformation();
  //! try to read the resource version data
  bool readRSRCInformation();

  //! can type the file
  bool canPrintResult(int verbose) const
  {
    if (m_fInfoResult.length() || m_dataResult.size() || m_rsrcResult.length()) return true;
    if (verbose <= 0) return false;
    if (m_fInfoCreator.length() || m_fInfoType.length()) return true;
    if (verbose <= 1) return false;
    return m_fileVersion.ok() || m_appliVersion.ok();
  }
  //! print the file type
  bool printResult(std::ostream &o, int verbose) const;

  bool checkFInfoType(char const *type, char const *result)
  {
    if (m_fInfoType != type) return false;
    m_fInfoResult=result;
    return true;
  }
  bool checkFInfoType(char const *result)
  {
    m_fInfoResult=result;
    if (m_fInfoType=="AAPL")
      m_fInfoResult+="[Application]";
    else if (m_fInfoType=="AIFF" || m_fInfoType=="AIFC")
      m_fInfoResult+="[sound]";
    else
      m_fInfoResult+="["+m_fInfoType+"]";
    return true;
  }
  bool checkFInfoCreator(char const *result)
  {
    m_fInfoResult=result;
    if (m_fInfoCreator.length())
      m_fInfoResult+="["+m_fInfoCreator+"]";
    return true;
  }

  //! the file name
  std::string m_fName;
  //! the file info creator
  std::string m_fInfoCreator;
  //! the file info type
  std::string m_fInfoType;
  //! the result of the finfo
  std::string m_fInfoResult;

  //! the file version (extracted from the resource fork )
  RSRC::Version m_fileVersion;
  //! the application version (extracted from the resource fork )
  RSRC::Version m_appliVersion;
  //! the application missing message
  std::string m_rsrcMissingMessage;
  //! the result of the resource fork
  std::string m_rsrcResult;

  //! the result of the data analysis
  std::vector<std::string> m_dataResult;

  //! print or not the filename
  bool m_printFileName;
};

bool File::readFileInformation()
{
  if (!m_fName.length())
    return false;

  XAttr xattr(m_fName.c_str());
  std::unique_ptr<InputStream> input{xattr.getStream("com.apple.FinderInfo")};
  if (!input) return false;

  if (input->length() < 8) {
    return false;
  }

  input->seek(0, libmwaw_tools::InputStream::SK_SET);
  m_fInfoType = "";
  for (int i = 0; i < 4; i++) {
    char c= input->read8();
    if (c==0) break;
    m_fInfoType+= c;
  }
  m_fInfoCreator="";
  for (int i = 0; i < 4; i++) {
    char c= input->read8();
    if (c==0) break;
    m_fInfoCreator+= c;
  }

  if (m_fInfoCreator=="" || m_fInfoType=="")
    return true;
  if (m_fInfoCreator=="2CTY") {
    checkFInfoType("SPUB", "PublishIt") || checkFInfoType("PublishIt");
  }
  else if (m_fInfoCreator=="AB65") {
    checkFInfoType("AD65", "Pagemaker6.5") ||
    checkFInfoType("AT65", "Pagemaker6.5[template]") ||
    checkFInfoType("Pagemaker6.5");
  }
  else if (m_fInfoCreator=="ACTA") {
    checkFInfoType("OTLN", "Acta") || checkFInfoType("otln", "Acta") || checkFInfoType("Acta");
  }
  else if (m_fInfoCreator=="ALB3") {
    checkFInfoType("ALD3", "Pagemaker3") || checkFInfoType("Pagemaker3");
  }
  else if (m_fInfoCreator=="ALB4") {
    checkFInfoType("ALD4", "Pagemaker4") || checkFInfoType("Pagemaker4");
  }
  else if (m_fInfoCreator=="ALB5") {
    checkFInfoType("ALD5", "Pagemaker5") || checkFInfoType("Pagemaker5");
  }
  else if (m_fInfoCreator=="ALB6") {
    checkFInfoType("ALD6", "Pagemaker6") || checkFInfoType("Pagemaker6");
  }
  else if (m_fInfoCreator=="AOqc") {
    checkFInfoType("TEXT","America Online") || checkFInfoType("ttro","America Online[readOnly]") ||
    checkFInfoType("America Online");
  }
  else if (m_fInfoCreator=="AOS1") {
    checkFInfoType("TEXT","eWorld") || checkFInfoType("ttro","eWorld[readOnly]") ||
    checkFInfoType("eWorld");
  }
  else if (m_fInfoCreator=="BOBO") {
    checkFInfoType("CWDB","ClarisWorks/AppleWorks[Database]")||
    checkFInfoType("CWD2","ClarisWorks/AppleWorks 2.0-3.0[Database]")||
    checkFInfoType("sWDB","ClarisWorks/AppleWorks 2.0-3.0[Database]")||
    checkFInfoType("CWGR","ClarisWorks/AppleWorks[Draw]")||
    checkFInfoType("sWGR","ClarisWorks/AppleWorks 2.0-3.0[Draw]")||
    checkFInfoType("CWSS","ClarisWorks/AppleWorks[SpreadSheet]")||
    checkFInfoType("CWS2","ClarisWorks/AppleWorks 2.0-3.0[SpreadSheet]")||
    checkFInfoType("sWSS","ClarisWorks/AppleWorks 2.0-3.0[SpreadSheet]")||
    checkFInfoType("CWPR","ClarisWorks/AppleWorks[Presentation]")||
    checkFInfoType("CWPT","ClarisWorks/AppleWorks[Paint]")||
    checkFInfoType("CWWP","ClarisWorks/AppleWorks")||
    checkFInfoType("CWW2","ClarisWorks/AppleWorks 2.0-3.0")||
    checkFInfoType("sWWP","ClarisWorks/AppleWorks 2.0-3.0")||
    checkFInfoType("ClarisWorks/AppleWorks");
  }
  else if (m_fInfoCreator=="BWks") {
    checkFInfoType("BWwp","BeagleWorks/WordPerfect Works") ||
    checkFInfoType("BWdb","BeagleWorks/WordPerfect Works[Database]") ||
    checkFInfoType("BWss","BeagleWorks/WordPerfect Works[SpreadSheet]") ||
    checkFInfoType("BWpt","BeagleWorks/WordPerfect Works[Paint]") ||
    checkFInfoType("BWdr","BeagleWorks/WordPerfect Works[Draw]") ||
    checkFInfoType("BeagleWorks/WordPerfect Works");
  }
  else if (m_fInfoCreator=="CARO") {
    checkFInfoType("PDF ", "Acrobat PDF");
  }
  else if (m_fInfoCreator=="C#+A") {
    checkFInfoType("C#+D","RagTime 5") || checkFInfoType("C#+F","RagTime 5[form]") ||
    checkFInfoType("RagTime 5");
  }
  else if (m_fInfoCreator=="CDrw") {
    checkFInfoType("dDrw", "ClarisDraw") || checkFInfoType("dDst", "ClarisDraw[stationary]") ||
    checkFInfoType("iLib", "ClarisDraw[library]") || checkFInfoType("ClarisDraw");
  }
  else if (m_fInfoCreator=="CRDW") {
    checkFInfoType("CKDT","CricketDraw") || checkFInfoType("CricketDraw");
  }
  else if (m_fInfoCreator=="DkmR") {
    checkFInfoType("TEXT","Basic text(created by DOCMaker)") || checkFInfoType("DOCMaker");
  }
  else if (m_fInfoCreator=="Dc@P" || m_fInfoCreator=="Dk@P") {
    checkFInfoType("APPL","DOCMaker") || checkFInfoType("DOCMaker");
  }
  else if (m_fInfoCreator=="DDAP") {
    checkFInfoType("DDFL+","DiskDoubler") || checkFInfoType("DiskDoubler");
  }
  else if (m_fInfoCreator=="FH50") {
    checkFInfoType("AGD1","FreeHand 5") || checkFInfoType("FreeHand 5");
  }
  else if (m_fInfoCreator=="FHA2") {
    checkFInfoType("FHD2","FreeHand 2") || checkFInfoType("FHT2","FreeHand 2[template]") || checkFInfoType("FreeHand 2");
  }
  else if (m_fInfoCreator=="FHA3") {
    checkFInfoType("FHD3","FreeHand 3") || checkFInfoType("FreeHand 3");
  }
  else if (m_fInfoCreator=="FS03") {
    checkFInfoType("WRT+","WriterPlus") || checkFInfoType("WriterPlus");
  }
  else if (m_fInfoCreator=="Fram") {
    checkFInfoType("FASL","FrameMaker") || checkFInfoType("MIF2","FrameMaker MIF2.0") ||
    checkFInfoType("MIF3","FrameMaker MIF3.0") || checkFInfoType("MIF ","FrameMaker MIF") ||
    checkFInfoType("FrameMaker");
  }
  else if (m_fInfoCreator=="FWRT") {
    checkFInfoType("FWRT","FullWrite 1.0") || checkFInfoType("FWRM","FullWrite 1.0") ||
    checkFInfoType("FWRI","FullWrite 2.0") || checkFInfoType("FullWrite");
  }
  else if (m_fInfoCreator=="F#+A") {
    checkFInfoType("F#+D","RagTime Classic") || checkFInfoType("F#+F","RagTime Classic[form]") ||
    checkFInfoType("RagTime Classic");
  }
  else if (m_fInfoCreator=="GM01") {
    checkFInfoType("GfMt","MouseWrite") || checkFInfoType("MouseWrite");
  }
  else if (m_fInfoCreator=="JWrt") {
    checkFInfoType("TEXT","JoliWrite") || checkFInfoType("ttro","JoliWrite[readOnly]") ||
    checkFInfoType("JoliWrite");
  }
  else if (m_fInfoCreator=="HMiw") {
    checkFInfoType("IWDC","HanMac Word-J") || checkFInfoType("HanMac Word-J");
  }
  else if (m_fInfoCreator=="HMdr") {
    checkFInfoType("DRD2","HanMac Word-K") || checkFInfoType("HanMac Word-K");
  }
  else if (m_fInfoCreator=="L123") {
    checkFInfoType("LWKS","Lotus123") || checkFInfoType("Lotus123");
  }
  else if (m_fInfoCreator=="LibW") {
    checkFInfoType("Chnk","Microspot Media Assistant") || checkFInfoType("Microspot Media Assistant");
  }
  else if (m_fInfoCreator=="LETR") {
    checkFInfoType("APPL","Take A Letter[auto]") || checkFInfoType("Take A Letter");
  }
  else if (m_fInfoCreator=="LWTE") {
    checkFInfoType("TEXT","LightWayText") || checkFInfoType("MACR","LightWayText[MACR]") ||
    checkFInfoType("pref","LightWayText[Preferences]") ||
    checkFInfoType("ttro","LightWayText[Tutorial]") || checkFInfoType("LightWayText");
  }
  else if (m_fInfoCreator=="LWTR") {
    checkFInfoType("APPL","LightWayText[appli]");
  }
  else if (m_fInfoCreator=="MACA") {
    checkFInfoType("WORD","MacWrite") || checkFInfoType("MacWrite");
  }
  else if (m_fInfoCreator=="MACD") {
    checkFInfoType("DRWG","MacDraft 1.0") || checkFInfoType("MacDraft 1.0");
  }
  else if (m_fInfoCreator=="MACW") {
    checkFInfoType("MWCT","MaxWrite 1.0") || checkFInfoType("MaxWrite 1.0");
  }
  else if (m_fInfoCreator=="MD40") {
    checkFInfoType("MDDC","MacDraft 4-5") || checkFInfoType("MSYM","MacDraft 4-5[lib]") ||
    checkFInfoType("MacDraft 4-5");
  }
  else if (m_fInfoCreator=="MDsr") {
    checkFInfoType("APPL","MacDoc(appli)");
  }
  else if (m_fInfoCreator=="MDvr") {
    checkFInfoType("MDdc","MacDoc") || checkFInfoType("MacDoc");
  }
  else if (m_fInfoCreator=="MDFT") {
    checkFInfoType("DRWG","MacDraft 1.2") || checkFInfoType("MacDraft 1.2");
  }
  else if (m_fInfoCreator=="MDRW") {
    checkFInfoType("DRWG","MacDraw") || checkFInfoType("MacDraw");
  }
  else if (m_fInfoCreator=="MDPL") {
    checkFInfoType("DRWG","MacDraw II") || checkFInfoType("STAT","MacDraw II(template)") || checkFInfoType("MacDraw II");
  }
  else if (m_fInfoCreator=="MMBB") {
    checkFInfoType("MBBT","Mariner Write") || checkFInfoType("Mariner Write");
  }
  else if (m_fInfoCreator=="MORE") {
    checkFInfoType("MORE","More") || checkFInfoType("More");
  }
  else if (m_fInfoCreator=="MOR2") {
    checkFInfoType("MOR2","More 2") || checkFInfoType("MOR3","More 3") ||
    checkFInfoType("More 2-3");
  }
  else if (m_fInfoCreator=="MPNT") {
    checkFInfoType("PNTG","MacPaint") || checkFInfoType("MacPaint");
  }
  else if (m_fInfoCreator=="MSWD") {
    checkFInfoType("WDBN","Microsoft Word 3-5") ||
    checkFInfoType("GLOS","Microsoft Word 3-5[glossary]") ||
    checkFInfoType("W6BN", "Microsoft Word 6") ||
    checkFInfoType("W8BN", "Microsoft Word 8") ||
    checkFInfoType("W8TN", "Microsoft Word 8[W8TN]") || // ?
    checkFInfoType("WXBN", "Microsoft Word 97-2004") || // Office X ?
    checkFInfoType("Microsoft Word");
  }
  else if (m_fInfoCreator=="MSWK") {
    checkFInfoType("AWWP","Microsoft Works 3") ||
    checkFInfoType("AWDB","Microsoft Works 3-4[database]") ||
    checkFInfoType("AWDR","Microsoft Works 3-4[draw]") ||
    checkFInfoType("AWSS","Microsoft Works 3-4[spreadsheet]") ||
    checkFInfoType("RLRB","Microsoft Works 4") ||
    checkFInfoType("sWRB","Microsoft Works 4[template]") ||
    checkFInfoType("Microsoft Works 3-4");
  }
  else if (m_fInfoCreator=="MWII") {
    checkFInfoType("MW2D","MacWrite II") || checkFInfoType("MacWrite II");
  }
  else if (m_fInfoCreator=="MWPR") {
    checkFInfoType("MWPd","MacWrite Pro") || checkFInfoType("MacWrite Pro");
  }
  else if (m_fInfoCreator=="NISI") {
    checkFInfoType("TEXT","Nisus") || checkFInfoType("GLOS","Nisus[glossary]") ||
    checkFInfoType("SMAC","Nisus[macros]") || checkFInfoType("edtt","Nisus[lock]") ||
    checkFInfoType("Nisus");
  }
  else if (m_fInfoCreator=="PaPy") {
    checkFInfoType("PAPD","Papyrus") || checkFInfoType("Papyrus");
  }
  else if (m_fInfoCreator=="PANT") {
    checkFInfoType("PANT","FullPaint") || checkFInfoType("FullPaint");
  }
  else if (m_fInfoCreator=="PIXR") {
    checkFInfoType("PX01","Pixel Paint") || checkFInfoType("Pixel Paint");
  }
  else if (m_fInfoCreator=="PLAN") {
    checkFInfoType("MPBN","MultiPlan") || checkFInfoType("MultiPlan");
  }
  else if (m_fInfoCreator=="PPNT") {
    checkFInfoType("SLDS","Microsoft PowerPoint") || checkFInfoType("Microsoft PowerPoint v1/2");
  }
  else if (m_fInfoCreator=="PPT3") {
    checkFInfoType("SLD3","Microsoft PowerPoint v3.0") || checkFInfoType("SLD8","Microsoft PowerPoint 97-2004") ||
    checkFInfoType("Microsoft PowerPoint");
  }
  else if (m_fInfoCreator=="PSIP") {
    checkFInfoType("AWWP","Microsoft Works 1.0") || checkFInfoType("Microsoft Works 1.0");
  }
  else if (m_fInfoCreator=="PSI2") {
    checkFInfoType("AWWP","Microsoft Works 2.0") || checkFInfoType("AWDB","Microsoft Works 2.0[database]") ||
    checkFInfoType("AWSS","Microsoft Works 2.0[spreadsheet]") || checkFInfoType("Microsoft Works 2.0");
  }
  else if (m_fInfoCreator=="PWRI") {
    checkFInfoType("OUTL","MindWrite") || checkFInfoType("MindWrite");
  }
  else if (m_fInfoCreator=="R#+A") {
    checkFInfoType("R#+D","RagTime") || checkFInfoType("R#+F","RagTime[form]") ||
    checkFInfoType("RagTime");
  }
  else if (m_fInfoCreator=="RTF ") {
    checkFInfoType("RTF ","RTF ") || checkFInfoType("RTF");
  }
  else if (m_fInfoCreator=="Rslv") {
    checkFInfoType("RsWs","Claris Resolve") || checkFInfoType("Claris Resolve");
  }
  else if (m_fInfoCreator=="SIT!") {
    checkFInfoType("SIT5", "archive SIT") ||
    checkFInfoType("SITD", "archive SIT") ||
    checkFInfoType("SIT!", "archive SIT") || checkFInfoType("SIT");
  }
  else if (m_fInfoCreator=="SPNT") {
    checkFInfoType("SPTG","SuperPaint 1.") || checkFInfoType("PICT","SuperPaint 2.[pict]") ||
    checkFInfoType("DTXR","SuperPaint 3.[texture,pict]") || /* pict without 512 header*/
    checkFInfoType("PNTG","SuperPaint 3.[macpaint]") || /* MacPaint format*/
    checkFInfoType("PTXR","SuperPaint 3.[texture,pict]") || /* pict without 512 header*/
    checkFInfoType("SPn3","SuperPaint 3.[pict]") || checkFInfoType("SPSt","SuperPaint 3.[pict,stationary]") ||
    checkFInfoType("SuperPaint");
  }
  else if (m_fInfoCreator=="SSIW") {   // check me
    checkFInfoType("WordPerfect 1.0");
  }
  else if (m_fInfoCreator==std::string("St")+char(0xd8)+"l") { // argh, not standart character
    std::string type1=std::string("TEd")+char(0xb6); // argh, not standart character
    checkFInfoType(type1.c_str(), "Style") || checkFInfoType("Style");
  }
  else if (m_fInfoCreator=="StAV") {
    checkFInfoType("APPL", "Style[auto]") || checkFInfoType("Style");
  }
  else if (m_fInfoCreator=="SVsc") {
    checkFInfoType("SVsc", "StarCalc 3.0") || checkFInfoType("StarCalc 3.0");
  }
  else if (m_fInfoCreator=="SVsd") {
    checkFInfoType("SVsd", "StarDraw 3.0") || checkFInfoType("StarDraw 3.0");
  }
  else if (m_fInfoCreator==std::string("SW/")+char(0xa9)) {
    std::string type1=std::string("SW/")+char(0xa9);
    checkFInfoType(type1.c_str(), "StarWriter 3.0") ||  checkFInfoType("StarWriter 3.0");
  }
  else if (m_fInfoCreator=="TBB5") {
    checkFInfoType("TEXT","Tex-Edit") || checkFInfoType("ttro","Tex-Edit[readOnly]") ||
    checkFInfoType("Tex-Edit");
  }
  else if (m_fInfoCreator=="WILD") {
    checkFInfoType("STAK", "HyperCard") || checkFInfoType("HyperCard");
  }
  else if (m_fInfoCreator=="WNGZ") {
    checkFInfoType("WZSS","Wingz[spreadsheet]") || checkFInfoType("WZSC","Wingz[script]") ||
    checkFInfoType("Wingz");
  }
  else if (m_fInfoCreator=="WORD") {
    checkFInfoType("WDBN","Microsoft Word 1") || checkFInfoType("Microsoft Word 1");
  }
  else if (m_fInfoCreator=="WPC2") {
    checkFInfoType("WordPerfect");
  }
  else if (m_fInfoCreator=="XCEL") {
    checkFInfoType("XCEL","Microsoft Excel 1") ||
    checkFInfoType("XLS3","Microsoft Excel 3") ||
    checkFInfoType("XLS4","Microsoft Excel 4") ||
    checkFInfoType("XLS5","Microsoft Excel 5") ||
    checkFInfoType("XLS8","Microsoft Excel 97-2004") ||
    checkFInfoType("TEXT","Microsoft Excel[text export]") ||
    checkFInfoType("Microsoft Excel");
  }
  else if (m_fInfoCreator=="XPR3") {
    checkFInfoType("XDOC","QuarkXPress 3-4") ||
    checkFInfoType("XTMP","QuarkXPress 3-4[template]") ||
    checkFInfoType("XBOK","QuarkXPress 4[book]") ||
    checkFInfoType("XLIB","QuarkXPress 4[library]") ||
    checkFInfoType("QuarkXPress 3-4");
  }
  else if (m_fInfoCreator=="XPRS") {
    checkFInfoType("XDOC","QuarkXPress 1") || checkFInfoType("QuarkXPress 1");
  }
  else if (m_fInfoCreator=="ZEBR") {
    checkFInfoType("ZWRT","GreatWorks") || checkFInfoType("ZTRM","GreatWorks[comm]") ||
    checkFInfoType("ZDBS","GreatWorks[database]") || checkFInfoType("ZCAL","GreatWorks[spreadsheet]") ||
    checkFInfoType("ZOLN","GreatWorks[outline]") || checkFInfoType("PNTG","GreatWorks v1[paint]") ||
    checkFInfoType("ZPNT","GreatWorks v2[paint]") || checkFInfoType("ZOBJ","GreatWorks[draw]") ||
    checkFInfoType("ZCHT","GreatWorks[chart]") || checkFInfoType("GreatWorks");
  }
  else if (m_fInfoCreator=="ZWRT") {
    checkFInfoType("Zart","Z-Write") || checkFInfoType("Z-Write");
  }
  else if (m_fInfoCreator=="aca3") {
    checkFInfoType("acf3","FreeHand v1") || checkFInfoType("act3","FreeHand v1[template]") ||
    checkFInfoType("FreeHand v1");
  }
  else if (m_fInfoCreator=="cAni") {
    checkFInfoType("curs","CursorAnimator") || checkFInfoType("CursorAnimator");
  }
  else if (m_fInfoCreator=="dPro") {
    checkFInfoType("dDoc","MacDraw Pro") || checkFInfoType("dLib","MacDraw Pro(slide)") || checkFInfoType("MacDraw Pro");
  }
  else if (m_fInfoCreator=="eDcR") {
    checkFInfoType("eDoc","eDOC") || checkFInfoType("eDOC");
  }
  else if (m_fInfoCreator=="eSRD") {
    checkFInfoType("APPL","eDOC(appli)");
  }
  else if (m_fInfoCreator=="nX^n") {
    checkFInfoType("nX^d","WriteNow 2") || checkFInfoType("nX^2","WriteNow 3-4") ||
    checkFInfoType("WriteNow");
  }
  else if (m_fInfoCreator=="ntxt") {
    checkFInfoType("TEXT","Anarcho");
  }
  else if (m_fInfoCreator=="ttxt") {
    if (m_fInfoType=="TEXT") {
      /* a little complex can be Classic MacOS SimpleText/TeachText or
      a << normal >> text file */
      XAttr rsrcAttr(m_fName.c_str());
      std::unique_ptr<InputStream> rsrcStream{rsrcAttr.getStream("com.apple.ResourceFork")};
      bool ok = false;
      if (rsrcStream && rsrcStream->length()) {
        libmwaw_tools::RSRC rsrcManager(*rsrcStream);
        ok = rsrcManager.hasEntry("styl", 128);
      }
      if (ok) checkFInfoType("TEXT","TeachText/SimpleText");
      else checkFInfoType("TEXT","Basic text");
    }
    else
      checkFInfoType("ttro","TeachText/SimpleText[readOnly]");
  }
  // now by type
  else if (m_fInfoType=="AAPL") {
    checkFInfoCreator("Application");
  }
  else if (m_fInfoType=="JFIF") {
    checkFInfoCreator("JPEG");
  }
  if (m_fInfoCreator.length()==0) {
    MWAW_DEBUG_MSG(("File::readFileInformation: Find unknown file info %s[%s]\n", m_fInfoCreator.c_str(), m_fInfoType.c_str()));
  }
  return true;
}

bool File::readDataInformation()
{
  if (!m_fName.length())
    return false;
  libmwaw_tools::FileStream input(m_fName.c_str());
  if (!input.ok()) {
    MWAW_DEBUG_MSG(("File::readDataInformation: can not open the data fork\n"));
    return false;
  }
  if (input.length() < 10)
    return true;
  input.seek(0, InputStream::SK_SET);
  int val[5];
  for (auto &v : val) v = int(input.readU16());
  // ----------- clearly discriminant ------------------
  if (val[2] == 0x424F && val[3] == 0x424F && (val[0]>>8) < 8) {
    m_dataResult.push_back("ClarisWorks/AppleWorks");
    return true;
  }
  if (val[0]==0x4257 && val[1]==0x6b73 && val[2]==0x4257 && val[4]==0x4257) {
    if (val[3]==0x6462)
      m_dataResult.push_back("BeagleWorks/WordPerfect Works[Database]");
    else if (val[3]==0x6472)
      m_dataResult.push_back("BeagleWorks/WordPerfect Works[Draw]");
    else if (val[3]==0x7074)
      m_dataResult.push_back("BeagleWorks/WordPerfect Works[Paint]");
    else if (val[3]==0x7373)
      m_dataResult.push_back("BeagleWorks/WordPerfect Works[Spreadsheet]");
    else if (val[3]==0x7770)
      m_dataResult.push_back("BeagleWorks/WordPerfect Works");
    else
      m_dataResult.push_back("BeagleWorks/WordPerfect Works[Unknown]");
    return true;
  }
  if (val[0]==0x4323 && val[1]==0x2b44 && val[2]==0xa443 && val[3]==0x4da5 && val[4]==0x4864) {
    m_dataResult.push_back("RagTime 5-6");
    return true;
  }
  if (val[0]==0x5772 && val[1]==0x6974 && val[2]==0x654e && val[3]==0x6f77 && val[4]==2) {
    m_dataResult.push_back("WriteNow 3-4");
    return true;
  }
  if (val[0]==0x4241 && val[1]==0x545F && val[2]==0x4254 && val[3]==0x5353) {
    m_dataResult.push_back("Claris Resolve");
    return true;
  }
  if (val[0]==0x574e && val[1]==0x475a && val[2]==0x575a) {
    if (val[3]==0x5353) {
      m_dataResult.push_back("Wingz");
      return true;
    }
    if (val[3]==0x5343) {
      m_dataResult.push_back("Wingz[script]");
      return true;
    }
  }
  if (val[0]==3 && val[1]==0x4d52 && val[2]==0x4949 && val[3]==0x80) {
    m_dataResult.push_back("More 2");
    return true;
  }
  if (val[0]==6 && val[1]==0x4d4f && val[2]==0x5233 && val[3]==0x80) {
    m_dataResult.push_back("More 3");
    return true;
  }
  if (val[0]==2 && val[1]==0 && val[2]==2 && val[3]==0x262 && val[4]==0x262) {
    m_dataResult.push_back("MacDraft 1");
    return true;
  }
  if (val[0]==0x4646 && val[1]==0x4646 && val[2]==0x3030 && val[3]==0x3030) {
    m_dataResult.push_back("Mariner Write");
    return true;
  }
  if (val[0]==0x4452 && val[1]==0x5747) { // DRWG
    if (val[2]==0x4d44) { // MD
      m_dataResult.push_back("MacDraw");
      return true;
    }
    if (val[2]==0 || val[2]==0x4432) { // nothing or D2
      m_dataResult.push_back("MacDraw II");
      return true;
    }
  }
  if (val[0]==0x5354 && val[1]==0x4154 && (val[2]==0||val[2]==0x4432)) { // STATD2
    m_dataResult.push_back("MacDraw II(template)");
    return true;
  }
  if (val[0]==0x6444 && val[1]==0x6f63 && val[2]==0x4432) { // dDocD2
    m_dataResult.push_back("MacDraw Pro");
    return true;
  }
  if (val[0]==0x644c && val[1]==0x6962 && val[2]==0x4432) { // dLibD2
    m_dataResult.push_back("MacDraw Pro(slide)");
    return true;
  }
  if (val[0]==0x4859 && val[1]==0x4c53 && val[2]==0x0210) {
    m_dataResult.push_back("HanMac Word-K");
    return true;
  }
  if (val[0]==0x594c && val[1]==0x5953 && val[2]==0x100) {
    m_dataResult.push_back("HanMac Word-J");
    return true;
  }
  if (val[0]==0x6163 && val[1]==0x6633 && val[2]<9) {
    m_dataResult.push_back("FreeHand v1");
    return true;
  }
  if (val[0]==0x4648 && val[1]==0x4432 && val[2]<20) {
    m_dataResult.push_back("FreeHand v2");
    return true;
  }
  if (val[0]==0x0447 && val[1]==0x4d30 && val[2]==0x3400) { // ^DGM04
    m_dataResult.push_back("MouseWrite");
    return true;
  }
  if (val[0]==0x2550 && val[1]==0x4446) {
    m_dataResult.push_back("PDF");
    return true;
  }
  if (val[0]==0x2854 && val[1]==0x6869 && val[2]==0x7320 && val[3]==0x6669) {
    m_dataResult.push_back("BinHex");
    return true;
  }
  if (val[0]==0x2521 && val[1]==0x5053 && val[2]==0x2d41 && val[3] == 0x646f && val[4]==0x6265) {
    m_dataResult.push_back("PostScript");
    return true;
  }
  if (val[0]==0xc5d0 && val[1]==0xd3c6) {
    m_dataResult.push_back("Adobe EPS");
    return true;
  }
  if (val[0]==0x7b5c && val[1]==0x7274 && (val[2]>>8)==0x66) {
    m_dataResult.push_back("RTF");
    return true;
  }
  if (val[2]==0x6d6f && val[3]==0x6f76) {
    m_dataResult.push_back("QuickTime movie");
    return true;
  }
  if (val[0]==0 && (val[1]>>8)==0 && val[2]==0x6674 && val[3]==0x7970 && val[4]==0x3367) {
    m_dataResult.push_back("MP4");
    return true;
  }
  if (val[0]==0x4749 && val[1]==0x4638 && (val[2]==0x3761 || val[2]==0x3961)) {
    m_dataResult.push_back("GIF");
    return true;
  }
  if (val[0]==0x8950 && val[1]==0x4e47 && val[2]==0x0d0a && val[3]==0x1a0a) {
    m_dataResult.push_back("PNG");
    return true;
  }
  if (val[3]==6 && val[4]==3 && input.length()>30) {
    input.seek(10, InputStream::SK_SET);
    if (val[0]==0x4d44 && val[1]==0x4443 && val[2]==0x3230) {
      m_dataResult.push_back("MacDraft 4-5");
      return true;
    }
    else if (input.readU16()==0 && input.readU16()==0x48 && input.readU16()==0x48) {
      m_dataResult.push_back("MacDraft 4-5[lib]");
      return true;
    }
  }
  if (val[0]==0xffd8 &&
      ((val[1]==0xffe0 && val[3]==0x4a46 && val[4] == 0x4946) ||
       (val[1]==0xffe1 && val[3]==0x4578 && val[4] == 0x6966) ||
       (val[1]==0xffe8 && val[3]==0x5350 && val[4] == 0x4946))) {
    m_dataResult.push_back("JPEG");
    return true;
  }
  if (val[0]==0x4949 && val[1]==0x2a00) {
    m_dataResult.push_back("TIF");
    return true;
  }
  if (val[0]==0x4d4d && val[1]==0x002a) {
    m_dataResult.push_back("TIFF");
    return true;
  }
  if (val[0]==0x4f67 && val[1]==0x6753) {
    m_dataResult.push_back("OGG data");
    return true;
  }
  // ----------- less discriminant ------------------
  if (val[0]==0xd0cf && val[1]==0x11e0 && val[2]==0xa1b1 && val[3]==0x1ae1) {
    libmwaw_tools::OLE ole(input);
    for (int step=0; step < 3; step++) {
      std::string res=step==0 ? ole.getClipName() : step==1 ? ole.getCLSIDType() : ole.getCompObjType();
      if (res.empty())
        continue;
      m_dataResult.push_back(res);
      return true;
    }
    m_dataResult.push_back("OLE file: can be DOC, DOT, PPS, PPT, XLA, XLS, WIZ, WPS(4.0), ...");
    return true;
  }
  if (val[0]==0x100 || val[0]==0x200) {
    if (val[1]==0x5a57 && val[2]==0x5254) {
      m_dataResult.push_back("GreatWorks");
      return true;
    }
    if (val[1]==0x5a4f && val[2]==0x4c4e) {
      m_dataResult.push_back("GreatWorks[outline]");
      return true;
    }
    if (val[1]==0x5a44 && val[2]==0x4253) {
      m_dataResult.push_back("GreatWorks[database]");
      return true;
    }
    if (val[1]==0x5a43 && val[2]==0x414c) {
      m_dataResult.push_back("GreatWorks[spreadsheet]");
      return true;
    }
    if (val[1]==0x5a4f && val[2]==0x424a) {
      m_dataResult.push_back("GreatWorks[draw]");
      return true;
    }
    if (val[1]==0x5a43 && val[2]==0x4854) {
      m_dataResult.push_back("GreatWorks[chart]");
      return true;
    }
  }
  if ((val[0]==0x100||val[0]==0x200) && val[2]==0x4558 && val[3]==0x5057) {
    if (val[0]==0x100)
      m_dataResult.push_back("ClarisDraw");
    else
      m_dataResult.push_back("ClarisDraw[library]");
    return true;
  }
  if (val[0]==0x4348 && val[1]==0x4e4b && val[2]==0x100 && val[3]==0) {
    m_dataResult.push_back("Style");
    return true;
  }
  if (val[0]==0x5041 && val[1]==0x5031 && (val[2]>=0x1fa0 && val[2]<=0x1fbc) && val[3]==0x0fa0) {
    m_dataResult.push_back("Papyrus");
    return true;
  }
  // less discriminant
  if ((val[0]==0xfe32 && val[1]==0) || (val[0]==0xfe34 && val[1]==0) ||
      (val[0] == 0xfe37 && (val[1] == 0x23 || val[1] == 0x1c))) {
    switch (val[1]) {
    case 0:
      if (val[0]==0xfe34)
        m_dataResult.push_back("Microsoft Word 3.0");
      else if (val[0]==0xfe32)
        m_dataResult.push_back("Microsoft Word 1.0");
      break;
    case 0x1c:
      m_dataResult.push_back("Microsoft Word 4.0");
      break;
    case 0x23:
      m_dataResult.push_back("Microsoft Word 5.0");
      break;
    default:
      break;
    }
  }
  if (val[0]==0 && input.length() > 30) {
    input.seek(16, InputStream::SK_SET);
    if (input.readU16()==0x688f && input.readU16()==0x688f) {
      m_dataResult.push_back("RagTime");
      return true;
    }
  }
  if (val[0]==0 && val[1]==0 && val[2]==0 && val[3]==0 &&
      ((val[4]>>8)==4 || (val[4]>>8)==0x44))
    m_dataResult.push_back("WriteNow 1-2");
  if (val[0] == 0x2e && val[1] == 0x2e)
    m_dataResult.push_back("MacWrite II");
  if (val[0] == 4 && val[1] == 4)
    m_dataResult.push_back("MacWrite Pro");
  if (val[0] == 0x7704)
    m_dataResult.push_back("MindWrite");
  if (val[0] == 0x110)
    m_dataResult.push_back("WriterPlus");
  if (val[0]==0xdba5 && val[1]==0x2d00) {
    m_dataResult.push_back("Microsoft Word 2.0[pc]");
    return true;
  }
  if (val[0]==0xabcd && val[1]==0x54) {
    m_dataResult.push_back("DiskDoubler[archive]");
    return true;
  }
  if (val[0]==0x4D44) { // MD
    m_dataResult.push_back("MacDraw v0[unsure]");
  }
  if (val[0]==0xbad && val[1]==0xdeed && val[2]==0) {
    m_dataResult.push_back("Microsoft PowerPoint Mac");
    return true;
  }
  if (val[0]==0xedde && val[1]==0xad0b && val[3]==0) {
    m_dataResult.push_back("Microsoft PowerPoint Windows");
    return true;
  }
  if (val[0]==0x11ab && val[1]==0x0 && val[2]==0x13e8 && val[3]==0) {
    m_dataResult.push_back("Microsoft Multiplan Mac");
    return true;
  }
  if (val[0] == 3 || val[0] == 6) {
    int numParaPos = val[0] == 3 ? 2 : 1;
    if (val[numParaPos] < 0x1000 && val[numParaPos+1] < 0x100 && val[numParaPos+2] < 0x100)
      m_dataResult.push_back("MacWrite[unsure]");
  }
  if (val[0]==0) {
    std::stringstream s;
    bool ok = true;
    switch (val[1]) {
    case 4:
      s << "Microsoft Works 1.0";
      break;
    case 8:
      s << "Microsoft Works 2.0";
      break;
    case 9:
      s << "Microsoft Works 3.0";
      break;
    case 11:
      s << "Microsoft Works 4.0";
      break; // all excepted a text file
    default:
      ok = false;
      break;
    }
    input.seek(16, InputStream::SK_SET);
    int type = ok ? int(input.readU16()) : -1;
    switch (type) {
    case 1:
      break;
    case 2:
      s << "[database]";
      break;
    case 3:
      s << "[spreadsheet]";
      break;
    case 12:
      s << "[draw]";
      break;
    default:
      ok = false;
      break;
    }
    if (ok)
      m_dataResult.push_back(s.str());
  }
  if (val[0]==0 && (val[1]==0x7FFF || val[1]==0x8000)) {
    m_dataResult.push_back("PixelPaint[unsure]");
  }
  if (val[0]>=1 && val[0]<=4) {
    int sSz=(val[1]>>8);
    if (sSz>=6 && sSz<=8) {
      // check if we have a date
      input.seek(3, InputStream::SK_SET);
      bool ok=true;
      int numSlash=0;
      for (int i=0; i<sSz; ++i) {
        auto c=char(input.readU8());
        if (c>='0' && c<='9')
          continue;
        if (c=='/')
          ++numSlash;
        else {
          ok=false;
          break;
        }
      }
      if (ok && numSlash==2)
        m_dataResult.push_back("Cricket Draw 1.0");
    }
  }
  input.seek(-4, InputStream::SK_END);
  int lVal[2];
  for (auto &v : lVal) v = int(input.readU16());
  if (lVal[0] == 0x4657 && lVal[1]==0x5254)
    m_dataResult.push_back("FullWrite 2.0");
  else if (lVal[0] == 0x4E4C && lVal[1]==0x544F)
    m_dataResult.push_back("Acta Classic");
  else if (lVal[1]==0 && val[0]==1 && (val[1]==1||val[1]==2))
    m_dataResult.push_back("Acta v2[unsure]");
  else if (lVal[0] == 0 && lVal[1]==1) { // Maybe a FullWrite 1.0 file, limited check
    input.seek(-38, InputStream::SK_END);
    long eof = input.length();
    bool ok = true;
    for (int i = 0; i < 2; i++) {
      auto pos = long(input.readU32());
      auto sz = long(input.read32());
      if (sz <= 0 || pos+sz > eof) {
        ok = false;
        break;
      }
    }
    if (ok)
      m_dataResult.push_back("FullWrite 1.0[unsure]");
  }
#ifdef DEBUG
  if (m_dataResult.size()==0) {
    std::stringstream s;
    s << "Unknown: " << std::hex << std::setfill('0');
    for (auto v : val)
      s << std::setw(4) << v << " ";
    m_dataResult.push_back(s.str());
  }
#endif
  return true;
}

bool File::readRSRCInformation()
{
  if (!m_fName.length())
    return false;

  XAttr xattr(m_fName.c_str());
  std::unique_ptr<libmwaw_tools::InputStream> rsrcStream{xattr.getStream("com.apple.ResourceFork")};
  if (!rsrcStream) return false;

  if (!rsrcStream->length()) {
    return true;
  }
  libmwaw_tools::RSRC rsrcManager(*rsrcStream);
#  if 0
  MWAW_DEBUG_MSG(("File::readRSRCInformation: find a resource fork\n"));
#  endif
  m_rsrcResult = rsrcManager.getString(-16396); // the application missing name
  m_rsrcMissingMessage = rsrcManager.getString(-16397);
  auto listVersion = rsrcManager.getVersionList();
  for (auto const &vers : listVersion) {
    switch (vers.m_id) {
    case 1:
      m_fileVersion = vers;
      break;
    case 2:
      if (!m_appliVersion.ok()) m_appliVersion = vers;
      break;
    case 2002:
      m_appliVersion = vers;
      break;
    default:
      break;
    }
  }
  return true;
}

bool File::printResult(std::ostream &o, int verbose) const
{
  if (!canPrintResult(verbose)) return false;
  if (m_printFileName)
    o << m_fName << ":";
  if (m_fInfoResult.length())
    o << m_fInfoResult;
  else if (m_rsrcResult.length())
    o << m_rsrcResult;
  else if (m_dataResult.size()) {
    size_t num = m_dataResult.size();
    if (num>1)
      o << "[";
    for (size_t i = 0; i < num; i++) {
      o << m_dataResult[i];
      if (i+1!=num)
        o << ",";
    }
    if (num>1)
      o << "]";
  }
  else
    o << "unknown";
  if (verbose > 0) {
    if (m_fInfoCreator.length() || m_fInfoType.length())
      o << ":type=" << m_fInfoCreator << "["  << m_fInfoType << "]";
  }
  if (verbose > 1) {
    if (m_fileVersion.ok())
      o << "\n\tFile" << m_fileVersion;
    if (m_appliVersion.ok())
      o << "\n\tAppli" << m_appliVersion;
  }
  o << "\n";
  return true;
}
}

static void usage(char const *fName)
{
  std::cerr << "Usage: " << fName << " [OPTION] FILENAME\n";
  std::cerr << "\n";
  std::cerr << "try to find the file type of FILENAME\n";
  std::cerr << "\n";
  std::cerr << "Options:\n";
  std::cerr << "\t -f: Does not print the filename,\n";
  std::cerr << "\t -F: Prints the filename[default],\n";
  std::cerr << "\t -h: Shows this help message,\n";
  std::cerr << "\t -v: Output mwawFile version\n";
  std::cerr << "\t -wNum: define the verbose level.\n";
}

static int printVersion()
{
  std::cerr << "mwawFile " << VERSION << "\n";
  return 0;
}

int main(int argc, char *const argv[])
{
  int ch, verbose=0;
  bool printFileName=true;

  while ((ch = getopt(argc, argv, "fFhvw:")) != -1) {
    switch (ch) {
    case 'w':
      verbose=atoi(optarg);
      break;
    case 'f':
      printFileName = false;
      break;
    case 'F':
      printFileName = true;
      break;
    case 'v':
      printVersion();
      return 0;
    case 'h':
    case '?':
    default:
      verbose=-1;
      break;
    }
  }
  if (argc != 1+optind || verbose < 0) {
    usage(argv[0]);
    return -1;
  }
  std::unique_ptr<libmwaw_tools::File> file;
  try {
    file.reset(new libmwaw_tools::File(argv[optind]));
    file->readFileInformation();
  }
  catch (...) {
    std::cerr << argv[0] << ": can not open file " << argv[optind] << "\n";
    return -1;
  }
  if (!file)
    return -1;
  try {
    file->readDataInformation();
  }
  catch (...) {
  }
  try {
    file->readRSRCInformation();
  }
  catch (...) {
  }

  file->m_printFileName = printFileName;
  if (verbose >= 4)
    std::cout << *file;
  else if (file->canPrintResult(verbose))
    file->printResult(std::cout, verbose);
  return 0;
}
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: