From: Jean-Philippe Orsini Date: Thu, 21 Apr 2011 08:00:29 +0000 (+0000) Subject: import psensor trunk from private svn X-Git-Tag: v0.8.0.5~842 X-Git-Url: https://git.wpitchoune.net/gitweb/?p=psensor.git;a=commitdiff_plain;h=609664bb77874990e10f8073e54bb7f1645c8d72 import psensor trunk from private svn --- 609664bb77874990e10f8073e54bb7f1645c8d72 diff --git a/ABOUT-NLS b/ABOUT-NLS new file mode 100644 index 0000000..ec20977 --- /dev/null +++ b/ABOUT-NLS @@ -0,0 +1,1101 @@ +1 Notes on the Free Translation Project +*************************************** + +Free software is going international! The Free Translation Project is +a way to get maintainers of free software, translators, and users all +together, so that free software will gradually become able to speak many +languages. A few packages already provide translations for their +messages. + + If you found this `ABOUT-NLS' file inside a distribution, you may +assume that the distributed package does use GNU `gettext' internally, +itself available at your nearest GNU archive site. But you do _not_ +need to install GNU `gettext' prior to configuring, installing or using +this package with messages translated. + + Installers will find here some useful hints. These notes also +explain how users should proceed for getting the programs to use the +available translations. They tell how people wanting to contribute and +work on translations can contact the appropriate team. + + When reporting bugs in the `intl/' directory or bugs which may be +related to internationalization, you should tell about the version of +`gettext' which is used. The information can be found in the +`intl/VERSION' file, in internationalized packages. + +1.1 Quick configuration advice +============================== + +If you want to exploit the full power of internationalization, you +should configure it using + + ./configure --with-included-gettext + +to force usage of internationalizing routines provided within this +package, despite the existence of internationalizing capabilities in the +operating system where this package is being installed. So far, only +the `gettext' implementation in the GNU C library version 2 provides as +many features (such as locale alias, message inheritance, automatic +charset conversion or plural form handling) as the implementation here. +It is also not possible to offer this additional functionality on top +of a `catgets' implementation. Future versions of GNU `gettext' will +very likely convey even more functionality. So it might be a good idea +to change to GNU `gettext' as soon as possible. + + So you need _not_ provide this option if you are using GNU libc 2 or +you have installed a recent copy of the GNU gettext package with the +included `libintl'. + +1.2 INSTALL Matters +=================== + +Some packages are "localizable" when properly installed; the programs +they contain can be made to speak your own native language. Most such +packages use GNU `gettext'. Other packages have their own ways to +internationalization, predating GNU `gettext'. + + By default, this package will be installed to allow translation of +messages. It will automatically detect whether the system already +provides the GNU `gettext' functions. If not, the included GNU +`gettext' library will be used. This library is wholly contained +within this package, usually in the `intl/' subdirectory, so prior +installation of the GNU `gettext' package is _not_ required. +Installers may use special options at configuration time for changing +the default behaviour. The commands: + + ./configure --with-included-gettext + ./configure --disable-nls + +will, respectively, bypass any pre-existing `gettext' to use the +internationalizing routines provided within this package, or else, +_totally_ disable translation of messages. + + When you already have GNU `gettext' installed on your system and run +configure without an option for your new package, `configure' will +probably detect the previously built and installed `libintl.a' file and +will decide to use this. This might not be desirable. You should use +the more recent version of the GNU `gettext' library. I.e. if the file +`intl/VERSION' shows that the library which comes with this package is +more recent, you should use + + ./configure --with-included-gettext + +to prevent auto-detection. + + The configuration process will not test for the `catgets' function +and therefore it will not be used. The reason is that even an +emulation of `gettext' on top of `catgets' could not provide all the +extensions of the GNU `gettext' library. + + Internationalized packages usually have many `po/LL.po' files, where +LL gives an ISO 639 two-letter code identifying the language. Unless +translations have been forbidden at `configure' time by using the +`--disable-nls' switch, all available translations are installed +together with the package. However, the environment variable `LINGUAS' +may be set, prior to configuration, to limit the installed set. +`LINGUAS' should then contain a space separated list of two-letter +codes, stating which languages are allowed. + +1.3 Using This Package +====================== + +As a user, if your language has been installed for this package, you +only have to set the `LANG' environment variable to the appropriate +`LL_CC' combination. Here `LL' is an ISO 639 two-letter language code, +and `CC' is an ISO 3166 two-letter country code. For example, let's +suppose that you speak German and live in Germany. At the shell +prompt, merely execute `setenv LANG de_DE' (in `csh'), +`export LANG; LANG=de_DE' (in `sh') or `export LANG=de_DE' (in `bash'). +This can be done from your `.login' or `.profile' file, once and for +all. + + You might think that the country code specification is redundant. +But in fact, some languages have dialects in different countries. For +example, `de_AT' is used for Austria, and `pt_BR' for Brazil. The +country code serves to distinguish the dialects. + + The locale naming convention of `LL_CC', with `LL' denoting the +language and `CC' denoting the country, is the one use on systems based +on GNU libc. On other systems, some variations of this scheme are +used, such as `LL' or `LL_CC.ENCODING'. You can get the list of +locales supported by your system for your language by running the +command `locale -a | grep '^LL''. + + Not all programs have translations for all languages. By default, an +English message is shown in place of a nonexistent translation. If you +understand other languages, you can set up a priority list of languages. +This is done through a different environment variable, called +`LANGUAGE'. GNU `gettext' gives preference to `LANGUAGE' over `LANG' +for the purpose of message handling, but you still need to have `LANG' +set to the primary language; this is required by other parts of the +system libraries. For example, some Swedish users who would rather +read translations in German than English for when Swedish is not +available, set `LANGUAGE' to `sv:de' while leaving `LANG' to `sv_SE'. + + Special advice for Norwegian users: The language code for Norwegian +bokma*l changed from `no' to `nb' recently (in 2003). During the +transition period, while some message catalogs for this language are +installed under `nb' and some older ones under `no', it's recommended +for Norwegian users to set `LANGUAGE' to `nb:no' so that both newer and +older translations are used. + + In the `LANGUAGE' environment variable, but not in the `LANG' +environment variable, `LL_CC' combinations can be abbreviated as `LL' +to denote the language's main dialect. For example, `de' is equivalent +to `de_DE' (German as spoken in Germany), and `pt' to `pt_PT' +(Portuguese as spoken in Portugal) in this context. + +1.4 Translating Teams +===================== + +For the Free Translation Project to be a success, we need interested +people who like their own language and write it well, and who are also +able to synergize with other translators speaking the same language. +Each translation team has its own mailing list. The up-to-date list of +teams can be found at the Free Translation Project's homepage, +`http://www.iro.umontreal.ca/contrib/po/HTML/', in the "National teams" +area. + + If you'd like to volunteer to _work_ at translating messages, you +should become a member of the translating team for your own language. +The subscribing address is _not_ the same as the list itself, it has +`-request' appended. For example, speakers of Swedish can send a +message to `sv-request@li.org', having this message body: + + subscribe + + Keep in mind that team members are expected to participate +_actively_ in translations, or at solving translational difficulties, +rather than merely lurking around. If your team does not exist yet and +you want to start one, or if you are unsure about what to do or how to +get started, please write to `translation@iro.umontreal.ca' to reach the +coordinator for all translator teams. + + The English team is special. It works at improving and uniformizing +the terminology in use. Proven linguistic skills are praised more than +programming skills, here. + +1.5 Available Packages +====================== + +Languages are not equally supported in all packages. The following +matrix shows the current state of internationalization, as of October +2006. The matrix shows, in regard of each package, for which languages +PO files have been submitted to translation coordination, with a +translation percentage of at least 50%. + + Ready PO files af am ar az be bg bs ca cs cy da de el en en_GB eo + +----------------------------------------------------+ + GNUnet | [] | + a2ps | [] [] [] [] [] | + aegis | () | + ant-phone | () | + anubis | [] | + ap-utils | | + aspell | [] [] [] [] [] | + bash | [] [] [] | + batchelor | [] | + bfd | | + bibshelf | [] | + binutils | [] | + bison | [] [] | + bison-runtime | | + bluez-pin | [] [] [] [] [] | + cflow | [] | + clisp | [] [] | + console-tools | [] [] | + coreutils | [] [] [] | + cpio | | + cpplib | [] [] [] | + cryptonit | [] | + darkstat | [] () [] | + dialog | [] [] [] [] [] [] | + diffutils | [] [] [] [] [] [] | + doodle | [] | + e2fsprogs | [] [] | + enscript | [] [] [] [] | + error | [] [] [] [] | + fetchmail | [] [] () [] | + fileutils | [] [] | + findutils | [] [] [] | + flex | [] [] [] | + fslint | [] | + gas | | + gawk | [] [] [] | + gbiff | [] | + gcal | [] | + gcc | [] | + gettext-examples | [] [] [] [] [] | + gettext-runtime | [] [] [] [] [] | + gettext-tools | [] [] | + gimp-print | [] [] [] [] | + gip | [] | + gliv | [] | + glunarclock | [] | + gmult | [] [] | + gnubiff | () | + gnucash | () () [] | + gnucash-glossary | [] () | + gnuedu | | + gnulib | [] [] [] [] [] [] | + gnunet-gtk | | + gnutls | | + gpe-aerial | [] [] | + gpe-beam | [] [] | + gpe-calendar | | + gpe-clock | [] [] | + gpe-conf | [] [] | + gpe-contacts | | + gpe-edit | [] | + gpe-filemanager | | + gpe-go | [] | + gpe-login | [] [] | + gpe-ownerinfo | [] [] | + gpe-package | | + gpe-sketchbook | [] [] | + gpe-su | [] [] | + gpe-taskmanager | [] [] | + gpe-timesheet | [] | + gpe-today | [] [] | + gpe-todo | | + gphoto2 | [] [] [] [] | + gprof | [] [] | + gpsdrive | () () | + gramadoir | [] [] | + grep | [] [] [] [] [] [] | + gretl | | + gsasl | | + gss | | + gst-plugins | [] [] [] [] | + gst-plugins-base | [] [] [] | + gst-plugins-good | [] [] [] [] [] [] [] | + gstreamer | [] [] [] [] [] [] [] | + gtick | () | + gtkam | [] [] [] | + gtkorphan | [] [] | + gtkspell | [] [] [] [] | + gutenprint | [] | + hello | [] [] [] [] [] | + id-utils | [] [] | + impost | | + indent | [] [] [] | + iso_3166 | [] [] | + iso_3166_2 | | + iso_4217 | [] | + iso_639 | [] [] | + jpilot | [] | + jtag | | + jwhois | | + kbd | [] [] [] [] | + keytouch | | + keytouch-editor | | + keytouch-keyboa... | | + latrine | () | + ld | [] | + leafpad | [] [] [] [] [] | + libc | [] [] [] [] [] | + libexif | [] | + libextractor | [] | + libgpewidget | [] [] [] | + libgpg-error | [] | + libgphoto2 | [] [] | + libgphoto2_port | [] [] | + libgsasl | | + libiconv | [] [] | + libidn | [] [] | + lifelines | [] () | + lilypond | [] | + lingoteach | | + lynx | [] [] [] [] | + m4 | [] [] [] [] | + mailutils | [] | + make | [] [] | + man-db | [] () [] [] | + minicom | [] [] [] | + mysecretdiary | [] [] | + nano | [] [] [] | + nano_1_0 | [] () [] [] | + opcodes | [] | + parted | | + pilot-qof | [] | + psmisc | [] | + pwdutils | | + python | | + qof | | + radius | [] | + recode | [] [] [] [] [] [] | + rpm | [] [] | + screem | | + scrollkeeper | [] [] [] [] [] [] [] [] | + sed | [] [] [] | + sh-utils | [] [] | + shared-mime-info | [] [] [] [] | + sharutils | [] [] [] [] [] [] | + shishi | | + silky | | + skencil | [] () | + sketch | [] () | + solfege | | + soundtracker | [] [] | + sp | [] | + stardict | [] | + system-tools-ba... | [] [] [] [] [] [] [] [] [] | + tar | [] | + texinfo | [] [] [] | + textutils | [] [] [] | + tin | () () | + tp-robot | [] | + tuxpaint | [] [] [] [] [] | + unicode-han-tra... | | + unicode-transla... | | + util-linux | [] [] [] [] | + vorbis-tools | [] [] [] [] | + wastesedge | () | + wdiff | [] [] [] [] | + wget | [] [] | + xchat | [] [] [] [] [] [] | + xkeyboard-config | | + xpad | [] [] | + +----------------------------------------------------+ + af am ar az be bg bs ca cs cy da de el en en_GB eo + 10 0 1 2 9 22 1 42 41 2 60 95 16 1 17 16 + + es et eu fa fi fr ga gl gu he hi hr hu id is it + +--------------------------------------------------+ + GNUnet | | + a2ps | [] [] [] () | + aegis | | + ant-phone | [] | + anubis | [] | + ap-utils | [] [] | + aspell | [] [] [] | + bash | [] [] [] | + batchelor | [] [] | + bfd | [] | + bibshelf | [] [] [] | + binutils | [] [] [] | + bison | [] [] [] [] [] [] | + bison-runtime | [] [] [] [] [] | + bluez-pin | [] [] [] [] [] | + cflow | [] | + clisp | [] [] | + console-tools | | + coreutils | [] [] [] [] [] [] | + cpio | [] [] [] | + cpplib | [] [] | + cryptonit | [] | + darkstat | [] () [] [] [] | + dialog | [] [] [] [] [] [] [] [] | + diffutils | [] [] [] [] [] [] [] [] [] | + doodle | [] [] | + e2fsprogs | [] [] [] | + enscript | [] [] [] | + error | [] [] [] [] [] | + fetchmail | [] | + fileutils | [] [] [] [] [] [] | + findutils | [] [] [] [] | + flex | [] [] [] | + fslint | [] | + gas | [] [] | + gawk | [] [] [] [] | + gbiff | [] | + gcal | [] [] | + gcc | [] | + gettext-examples | [] [] [] [] [] [] | + gettext-runtime | [] [] [] [] [] [] | + gettext-tools | [] [] [] | + gimp-print | [] [] | + gip | [] [] [] | + gliv | () | + glunarclock | [] [] [] | + gmult | [] [] [] | + gnubiff | () () | + gnucash | () () () | + gnucash-glossary | [] [] | + gnuedu | [] | + gnulib | [] [] [] [] [] [] [] [] | + gnunet-gtk | | + gnutls | | + gpe-aerial | [] [] | + gpe-beam | [] [] | + gpe-calendar | | + gpe-clock | [] [] [] [] | + gpe-conf | [] | + gpe-contacts | [] [] | + gpe-edit | [] [] [] [] | + gpe-filemanager | [] | + gpe-go | [] [] [] | + gpe-login | [] [] [] | + gpe-ownerinfo | [] [] [] [] [] | + gpe-package | [] | + gpe-sketchbook | [] [] | + gpe-su | [] [] [] [] | + gpe-taskmanager | [] [] [] | + gpe-timesheet | [] [] [] [] | + gpe-today | [] [] [] [] | + gpe-todo | [] | + gphoto2 | [] [] [] [] [] | + gprof | [] [] [] [] | + gpsdrive | () () [] () | + gramadoir | [] [] | + grep | [] [] [] [] [] [] [] [] [] [] [] [] | + gretl | [] [] [] | + gsasl | [] [] | + gss | [] | + gst-plugins | [] [] [] | + gst-plugins-base | [] [] | + gst-plugins-good | [] [] [] | + gstreamer | [] [] [] | + gtick | [] | + gtkam | [] [] [] [] | + gtkorphan | [] [] | + gtkspell | [] [] [] [] [] [] | + gutenprint | [] | + hello | [] [] [] [] [] [] [] [] [] [] [] [] [] | + id-utils | [] [] [] [] [] | + impost | [] [] | + indent | [] [] [] [] [] [] [] [] [] [] | + iso_3166 | [] [] [] | + iso_3166_2 | [] | + iso_4217 | [] [] [] [] | + iso_639 | [] [] [] [] [] | + jpilot | [] [] | + jtag | [] | + jwhois | [] [] [] [] [] | + kbd | [] [] | + keytouch | [] | + keytouch-editor | [] | + keytouch-keyboa... | [] | + latrine | [] [] [] | + ld | [] [] | + leafpad | [] [] [] [] [] [] | + libc | [] [] [] [] [] | + libexif | [] | + libextractor | [] | + libgpewidget | [] [] [] [] [] | + libgpg-error | | + libgphoto2 | [] [] [] | + libgphoto2_port | [] [] | + libgsasl | [] [] | + libiconv | [] [] | + libidn | [] [] | + lifelines | () | + lilypond | [] | + lingoteach | [] [] [] | + lynx | [] [] [] | + m4 | [] [] [] [] | + mailutils | [] [] | + make | [] [] [] [] [] [] [] [] | + man-db | () | + minicom | [] [] [] [] | + mysecretdiary | [] [] [] | + nano | [] [] [] [] [] [] | + nano_1_0 | [] [] [] [] [] | + opcodes | [] [] [] [] | + parted | [] [] [] [] | + pilot-qof | | + psmisc | [] [] [] | + pwdutils | | + python | | + qof | [] | + radius | [] [] | + recode | [] [] [] [] [] [] [] [] | + rpm | [] [] | + screem | | + scrollkeeper | [] [] [] | + sed | [] [] [] [] [] | + sh-utils | [] [] [] [] [] [] [] | + shared-mime-info | [] [] [] [] [] [] | + sharutils | [] [] [] [] [] [] [] [] | + shishi | | + silky | [] | + skencil | [] [] | + sketch | [] [] | + solfege | [] | + soundtracker | [] [] [] | + sp | [] | + stardict | [] | + system-tools-ba... | [] [] [] [] [] [] [] [] | + tar | [] [] [] [] [] [] [] | + texinfo | [] [] | + textutils | [] [] [] [] [] | + tin | [] () | + tp-robot | [] [] [] [] | + tuxpaint | [] [] | + unicode-han-tra... | | + unicode-transla... | [] [] | + util-linux | [] [] [] [] [] [] [] | + vorbis-tools | [] [] | + wastesedge | () | + wdiff | [] [] [] [] [] [] [] [] | + wget | [] [] [] [] [] [] [] [] | + xchat | [] [] [] [] [] [] [] [] | + xkeyboard-config | [] [] [] [] | + xpad | [] [] [] | + +--------------------------------------------------+ + es et eu fa fi fr ga gl gu he hi hr hu id is it + 88 22 14 2 40 115 61 14 1 8 1 6 59 31 0 52 + + ja ko ku ky lg lt lv mk mn ms mt nb ne nl nn no + +-------------------------------------------------+ + GNUnet | | + a2ps | () [] [] () | + aegis | () | + ant-phone | [] | + anubis | [] [] [] | + ap-utils | [] | + aspell | [] [] | + bash | [] | + batchelor | [] [] | + bfd | | + bibshelf | [] | + binutils | | + bison | [] [] [] | + bison-runtime | [] [] [] | + bluez-pin | [] [] [] | + cflow | | + clisp | [] | + console-tools | | + coreutils | [] | + cpio | | + cpplib | [] | + cryptonit | [] | + darkstat | [] [] | + dialog | [] [] | + diffutils | [] [] [] | + doodle | | + e2fsprogs | [] | + enscript | [] | + error | [] | + fetchmail | [] [] | + fileutils | [] [] | + findutils | [] | + flex | [] [] | + fslint | [] [] | + gas | | + gawk | [] [] | + gbiff | [] | + gcal | | + gcc | | + gettext-examples | [] [] | + gettext-runtime | [] [] [] | + gettext-tools | [] [] | + gimp-print | [] [] | + gip | [] [] | + gliv | [] | + glunarclock | [] [] | + gmult | [] [] | + gnubiff | | + gnucash | () () | + gnucash-glossary | [] | + gnuedu | | + gnulib | [] [] [] [] | + gnunet-gtk | | + gnutls | | + gpe-aerial | [] | + gpe-beam | [] | + gpe-calendar | [] | + gpe-clock | [] [] [] | + gpe-conf | [] [] | + gpe-contacts | [] | + gpe-edit | [] [] [] | + gpe-filemanager | [] [] | + gpe-go | [] [] [] | + gpe-login | [] [] [] | + gpe-ownerinfo | [] [] | + gpe-package | [] [] | + gpe-sketchbook | [] [] | + gpe-su | [] [] [] | + gpe-taskmanager | [] [] [] [] | + gpe-timesheet | [] | + gpe-today | [] [] | + gpe-todo | [] | + gphoto2 | [] [] | + gprof | | + gpsdrive | () () () | + gramadoir | () | + grep | [] [] [] [] | + gretl | | + gsasl | [] | + gss | | + gst-plugins | [] | + gst-plugins-base | | + gst-plugins-good | [] | + gstreamer | [] | + gtick | | + gtkam | [] | + gtkorphan | [] | + gtkspell | [] [] | + gutenprint | | + hello | [] [] [] [] [] [] | + id-utils | [] | + impost | | + indent | [] [] | + iso_3166 | [] | + iso_3166_2 | [] | + iso_4217 | [] [] [] | + iso_639 | [] [] | + jpilot | () () () | + jtag | | + jwhois | [] | + kbd | [] | + keytouch | [] | + keytouch-editor | | + keytouch-keyboa... | | + latrine | [] | + ld | | + leafpad | [] [] | + libc | [] [] [] [] [] | + libexif | | + libextractor | | + libgpewidget | [] | + libgpg-error | | + libgphoto2 | [] | + libgphoto2_port | [] | + libgsasl | [] | + libiconv | | + libidn | [] [] | + lifelines | [] | + lilypond | | + lingoteach | [] | + lynx | [] [] | + m4 | [] [] | + mailutils | | + make | [] [] [] | + man-db | () | + minicom | [] | + mysecretdiary | [] | + nano | [] [] [] | + nano_1_0 | [] [] [] | + opcodes | [] | + parted | [] [] | + pilot-qof | | + psmisc | [] [] [] | + pwdutils | | + python | | + qof | | + radius | | + recode | [] | + rpm | [] [] | + screem | [] | + scrollkeeper | [] [] [] [] | + sed | [] [] | + sh-utils | [] [] | + shared-mime-info | [] [] [] [] [] | + sharutils | [] [] | + shishi | | + silky | [] | + skencil | | + sketch | | + solfege | | + soundtracker | | + sp | () | + stardict | [] [] | + system-tools-ba... | [] [] [] [] | + tar | [] [] [] | + texinfo | [] [] [] | + textutils | [] [] [] | + tin | | + tp-robot | [] | + tuxpaint | [] | + unicode-han-tra... | | + unicode-transla... | | + util-linux | [] [] | + vorbis-tools | [] | + wastesedge | [] | + wdiff | [] [] | + wget | [] [] | + xchat | [] [] [] [] | + xkeyboard-config | [] | + xpad | [] [] [] | + +-------------------------------------------------+ + ja ko ku ky lg lt lv mk mn ms mt nb ne nl nn no + 52 24 2 2 1 3 0 2 3 21 0 15 1 97 5 1 + + nso or pa pl pt pt_BR rm ro ru rw sk sl sq sr sv ta + +------------------------------------------------------+ + GNUnet | | + a2ps | () [] [] [] [] [] [] | + aegis | () () | + ant-phone | [] [] | + anubis | [] [] [] | + ap-utils | () | + aspell | [] [] | + bash | [] [] [] | + batchelor | [] [] | + bfd | | + bibshelf | [] | + binutils | [] [] | + bison | [] [] [] [] [] | + bison-runtime | [] [] [] [] | + bluez-pin | [] [] [] [] [] [] [] [] [] | + cflow | [] | + clisp | [] | + console-tools | [] | + coreutils | [] [] [] [] | + cpio | [] [] [] | + cpplib | [] | + cryptonit | [] [] | + darkstat | [] [] [] [] [] [] | + dialog | [] [] [] [] [] [] [] [] [] | + diffutils | [] [] [] [] [] [] | + doodle | [] [] | + e2fsprogs | [] [] | + enscript | [] [] [] [] [] | + error | [] [] [] [] | + fetchmail | [] [] [] | + fileutils | [] [] [] [] [] | + findutils | [] [] [] [] [] [] | + flex | [] [] [] [] [] | + fslint | [] [] [] [] | + gas | | + gawk | [] [] [] [] | + gbiff | [] | + gcal | [] | + gcc | [] | + gettext-examples | [] [] [] [] [] [] [] [] | + gettext-runtime | [] [] [] [] [] [] [] [] | + gettext-tools | [] [] [] [] [] [] [] | + gimp-print | [] [] | + gip | [] [] [] [] | + gliv | [] [] [] [] | + glunarclock | [] [] [] [] [] [] | + gmult | [] [] [] [] | + gnubiff | () | + gnucash | () [] | + gnucash-glossary | [] [] [] | + gnuedu | | + gnulib | [] [] [] [] [] | + gnunet-gtk | [] | + gnutls | [] [] | + gpe-aerial | [] [] [] [] [] [] [] | + gpe-beam | [] [] [] [] [] [] [] | + gpe-calendar | [] | + gpe-clock | [] [] [] [] [] [] [] [] | + gpe-conf | [] [] [] [] [] [] [] | + gpe-contacts | [] [] [] [] [] | + gpe-edit | [] [] [] [] [] [] [] [] | + gpe-filemanager | [] [] | + gpe-go | [] [] [] [] [] [] | + gpe-login | [] [] [] [] [] [] [] [] | + gpe-ownerinfo | [] [] [] [] [] [] [] [] | + gpe-package | [] [] | + gpe-sketchbook | [] [] [] [] [] [] [] [] | + gpe-su | [] [] [] [] [] [] [] [] | + gpe-taskmanager | [] [] [] [] [] [] [] [] | + gpe-timesheet | [] [] [] [] [] [] [] [] | + gpe-today | [] [] [] [] [] [] [] [] | + gpe-todo | [] [] [] [] | + gphoto2 | [] [] [] [] [] | + gprof | [] [] [] | + gpsdrive | [] [] [] | + gramadoir | [] [] | + grep | [] [] [] [] [] [] [] [] | + gretl | [] | + gsasl | [] [] [] | + gss | [] [] [] | + gst-plugins | [] [] [] [] | + gst-plugins-base | [] | + gst-plugins-good | [] [] [] [] | + gstreamer | [] [] [] | + gtick | [] | + gtkam | [] [] [] [] | + gtkorphan | [] | + gtkspell | [] [] [] [] [] [] [] [] | + gutenprint | [] | + hello | [] [] [] [] [] [] [] [] | + id-utils | [] [] [] [] | + impost | [] | + indent | [] [] [] [] [] [] | + iso_3166 | [] [] [] [] [] [] | + iso_3166_2 | | + iso_4217 | [] [] [] [] | + iso_639 | [] [] [] [] | + jpilot | | + jtag | [] | + jwhois | [] [] [] [] | + kbd | [] [] [] | + keytouch | [] | + keytouch-editor | [] | + keytouch-keyboa... | [] | + latrine | [] [] | + ld | [] | + leafpad | [] [] [] [] [] [] | + libc | [] [] [] [] [] | + libexif | [] | + libextractor | [] [] | + libgpewidget | [] [] [] [] [] [] [] | + libgpg-error | [] [] | + libgphoto2 | [] | + libgphoto2_port | [] [] [] | + libgsasl | [] [] [] [] | + libiconv | [] [] | + libidn | [] [] () | + lifelines | [] [] | + lilypond | | + lingoteach | [] | + lynx | [] [] [] | + m4 | [] [] [] [] [] | + mailutils | [] [] [] [] | + make | [] [] [] [] | + man-db | [] [] | + minicom | [] [] [] [] [] | + mysecretdiary | [] [] [] [] | + nano | [] [] [] | + nano_1_0 | [] [] [] [] | + opcodes | [] [] | + parted | [] | + pilot-qof | [] | + psmisc | [] [] | + pwdutils | [] [] | + python | | + qof | [] [] | + radius | [] [] | + recode | [] [] [] [] [] [] [] | + rpm | [] [] [] [] | + screem | | + scrollkeeper | [] [] [] [] [] [] [] | + sed | [] [] [] [] [] [] [] [] [] | + sh-utils | [] [] [] | + shared-mime-info | [] [] [] [] [] | + sharutils | [] [] [] [] | + shishi | [] | + silky | [] | + skencil | [] [] [] | + sketch | [] [] [] | + solfege | [] | + soundtracker | [] [] | + sp | | + stardict | [] [] [] | + system-tools-ba... | [] [] [] [] [] [] [] [] [] | + tar | [] [] [] [] [] | + texinfo | [] [] [] [] | + textutils | [] [] [] | + tin | () | + tp-robot | [] | + tuxpaint | [] [] [] [] [] | + unicode-han-tra... | | + unicode-transla... | | + util-linux | [] [] [] [] | + vorbis-tools | [] [] | + wastesedge | | + wdiff | [] [] [] [] [] [] | + wget | [] [] [] [] | + xchat | [] [] [] [] [] [] [] | + xkeyboard-config | [] [] | + xpad | [] [] [] | + +------------------------------------------------------+ + nso or pa pl pt pt_BR rm ro ru rw sk sl sq sr sv ta + 0 2 3 58 30 54 5 73 72 4 40 46 11 50 128 2 + + tg th tk tr uk ven vi wa xh zh_CN zh_HK zh_TW zu + +---------------------------------------------------+ + GNUnet | [] | 2 + a2ps | [] [] [] | 19 + aegis | | 0 + ant-phone | [] [] | 6 + anubis | [] [] [] | 11 + ap-utils | () [] | 4 + aspell | [] [] [] | 15 + bash | [] | 11 + batchelor | [] [] | 9 + bfd | | 1 + bibshelf | [] | 7 + binutils | [] [] [] | 9 + bison | [] [] [] | 19 + bison-runtime | [] [] [] | 15 + bluez-pin | [] [] [] [] [] [] | 28 + cflow | [] [] | 5 + clisp | | 6 + console-tools | [] [] | 5 + coreutils | [] [] | 16 + cpio | [] [] [] | 9 + cpplib | [] [] [] [] | 11 + cryptonit | | 5 + darkstat | [] () () | 15 + dialog | [] [] [] [] [] | 30 + diffutils | [] [] [] [] | 28 + doodle | [] | 6 + e2fsprogs | [] [] | 10 + enscript | [] [] [] | 16 + error | [] [] [] [] | 18 + fetchmail | [] [] | 12 + fileutils | [] [] [] | 18 + findutils | [] [] [] | 17 + flex | [] [] | 15 + fslint | [] | 9 + gas | [] | 3 + gawk | [] [] | 15 + gbiff | [] | 5 + gcal | [] | 5 + gcc | [] [] [] | 6 + gettext-examples | [] [] [] [] [] [] | 27 + gettext-runtime | [] [] [] [] [] [] | 28 + gettext-tools | [] [] [] [] [] | 19 + gimp-print | [] [] | 12 + gip | [] [] | 12 + gliv | [] [] | 8 + glunarclock | [] [] [] | 15 + gmult | [] [] [] [] | 15 + gnubiff | [] | 1 + gnucash | () | 2 + gnucash-glossary | [] [] | 9 + gnuedu | [] | 2 + gnulib | [] [] [] [] [] | 28 + gnunet-gtk | | 1 + gnutls | | 2 + gpe-aerial | [] [] | 14 + gpe-beam | [] [] | 14 + gpe-calendar | [] | 3 + gpe-clock | [] [] [] [] | 21 + gpe-conf | [] [] | 14 + gpe-contacts | [] [] | 10 + gpe-edit | [] [] [] [] | 20 + gpe-filemanager | [] | 6 + gpe-go | [] [] | 15 + gpe-login | [] [] [] [] [] | 21 + gpe-ownerinfo | [] [] [] [] | 21 + gpe-package | [] | 6 + gpe-sketchbook | [] [] | 16 + gpe-su | [] [] [] | 20 + gpe-taskmanager | [] [] [] | 20 + gpe-timesheet | [] [] [] [] | 18 + gpe-today | [] [] [] [] [] | 21 + gpe-todo | [] | 7 + gphoto2 | [] [] [] [] | 20 + gprof | [] [] | 11 + gpsdrive | | 4 + gramadoir | [] | 7 + grep | [] [] [] [] | 34 + gretl | | 4 + gsasl | [] [] | 8 + gss | [] | 5 + gst-plugins | [] [] [] | 15 + gst-plugins-base | [] [] [] | 9 + gst-plugins-good | [] [] [] [] [] | 20 + gstreamer | [] [] [] | 17 + gtick | [] | 3 + gtkam | [] | 13 + gtkorphan | [] | 7 + gtkspell | [] [] [] [] [] [] | 26 + gutenprint | | 3 + hello | [] [] [] [] [] | 37 + id-utils | [] [] | 14 + impost | [] | 4 + indent | [] [] [] [] | 25 + iso_3166 | [] [] [] [] | 16 + iso_3166_2 | | 2 + iso_4217 | [] [] | 14 + iso_639 | [] | 14 + jpilot | [] [] [] [] | 7 + jtag | [] | 3 + jwhois | [] [] [] | 13 + kbd | [] [] | 12 + keytouch | [] | 4 + keytouch-editor | | 2 + keytouch-keyboa... | [] | 3 + latrine | [] [] | 8 + ld | [] [] [] [] | 8 + leafpad | [] [] [] [] | 23 + libc | [] [] [] | 23 + libexif | [] | 4 + libextractor | [] | 5 + libgpewidget | [] [] [] | 19 + libgpg-error | [] | 4 + libgphoto2 | [] | 8 + libgphoto2_port | [] [] [] | 11 + libgsasl | [] | 8 + libiconv | [] | 7 + libidn | [] [] | 10 + lifelines | | 4 + lilypond | | 2 + lingoteach | [] | 6 + lynx | [] [] [] | 15 + m4 | [] [] [] | 18 + mailutils | [] | 8 + make | [] [] [] | 20 + man-db | [] | 6 + minicom | [] | 14 + mysecretdiary | [] [] | 12 + nano | [] [] | 17 + nano_1_0 | [] [] [] | 18 + opcodes | [] [] | 10 + parted | [] [] [] | 10 + pilot-qof | [] | 3 + psmisc | [] | 10 + pwdutils | [] | 3 + python | | 0 + qof | [] | 4 + radius | [] | 6 + recode | [] [] [] | 25 + rpm | [] [] [] [] | 14 + screem | [] | 2 + scrollkeeper | [] [] [] [] | 26 + sed | [] [] [] | 22 + sh-utils | [] | 15 + shared-mime-info | [] [] [] [] | 24 + sharutils | [] [] [] | 23 + shishi | | 1 + silky | [] | 4 + skencil | [] | 7 + sketch | | 6 + solfege | | 2 + soundtracker | [] [] | 9 + sp | [] | 3 + stardict | [] [] [] [] | 11 + system-tools-ba... | [] [] [] [] [] [] [] | 37 + tar | [] [] [] [] | 20 + texinfo | [] [] [] | 15 + textutils | [] [] [] | 17 + tin | | 1 + tp-robot | [] [] [] | 10 + tuxpaint | [] [] [] | 16 + unicode-han-tra... | | 0 + unicode-transla... | | 2 + util-linux | [] [] [] | 20 + vorbis-tools | [] [] | 11 + wastesedge | | 1 + wdiff | [] [] | 22 + wget | [] [] [] | 19 + xchat | [] [] [] [] | 29 + xkeyboard-config | [] [] [] [] | 11 + xpad | [] [] [] | 14 + +---------------------------------------------------+ + 77 teams tg th tk tr uk ven vi wa xh zh_CN zh_HK zh_TW zu + 170 domains 0 1 1 77 39 0 136 10 1 48 5 54 0 2028 + + Some counters in the preceding matrix are higher than the number of +visible blocks let us expect. This is because a few extra PO files are +used for implementing regional variants of languages, or language +dialects. + + For a PO file in the matrix above to be effective, the package to +which it applies should also have been internationalized and +distributed as such by its maintainer. There might be an observable +lag between the mere existence a PO file and its wide availability in a +distribution. + + If October 2006 seems to be old, you may fetch a more recent copy of +this `ABOUT-NLS' file on most GNU archive sites. The most up-to-date +matrix with full percentage details can be found at +`http://www.iro.umontreal.ca/contrib/po/HTML/matrix.html'. + +1.6 Using `gettext' in new packages +=================================== + +If you are writing a freely available program and want to +internationalize it you are welcome to use GNU `gettext' in your +package. Of course you have to respect the GNU Library General Public +License which covers the use of the GNU `gettext' library. This means +in particular that even non-free programs can use `libintl' as a shared +library, whereas only free software can use `libintl' as a static +library or use modified versions of `libintl'. + + Once the sources are changed appropriately and the setup can handle +the use of `gettext' the only thing missing are the translations. The +Free Translation Project is also available for packages which are not +developed inside the GNU project. Therefore the information given above +applies also for every other Free Software Project. Contact +`translation@iro.umontreal.ca' to make the `.pot' files available to +the translation teams. + diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..5850e46 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,4 @@ +Authors of psensor. + +wpitchoune@gmail.com all files. +linux.dabao@gmail.com po/zh_CN.po \ No newline at end of file diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..c5611e4 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e69de29 diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..7d1c323 --- /dev/null +++ b/INSTALL @@ -0,0 +1,365 @@ +Installation Instructions +************************* + +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, +2006, 2007, 2008, 2009 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, +are permitted in any medium without royalty provided the copyright +notice and this notice are preserved. This file is offered as-is, +without warranty of any kind. + +Basic Installation +================== + + Briefly, the shell commands `./configure; make; make install' should +configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. Some packages provide this +`INSTALL' file but do not implement all of the features documented +below. The lack of an optional feature in a given package is not +necessarily a bug. More recommendations for GNU packages can be found +in *note Makefile Conventions: (standards)Makefile Conventions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. + + The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. + + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package, generally using the just-built uninstalled binaries. + + 4. Type `make install' to install the programs and any data files and + documentation. When installing into a prefix owned by root, it is + recommended that the package be configured and built as a regular + user, and only the `make install' phase executed with root + privileges. + + 5. Optionally, type `make installcheck' to repeat any self-tests, but + this time using the binaries in their final installed location. + This target does not install anything. Running this target as a + regular user, particularly if the prior `make install' required + root privileges, verifies that the installation completed + correctly. + + 6. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + 7. Often, you can also type `make uninstall' to remove the installed + files again. In practice, not all packages have tested that + uninstallation works correctly, even though it is required by the + GNU Coding Standards. + + 8. Some packages, particularly those that use Automake, provide `make + distcheck', which can by used by developers to test that all other + targets like `make install' and `make uninstall' work correctly. + This target is generally not run by end users. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. This +is known as a "VPATH" build. + + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + + On MacOS X 10.5 and later systems, you can create libraries and +executables that work on multiple system types--known as "fat" or +"universal" binaries--by specifying multiple `-arch' options to the +compiler but only a single `-arch' option to the preprocessor. Like +this: + + ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CPP="gcc -E" CXXCPP="g++ -E" + + This is not guaranteed to produce working output in all cases, you +may have to build one architecture at a time and combine the results +using the `lipo' tool if you have problems. + +Installation Names +================== + + By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX', where PREFIX must be an +absolute file name. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. In general, the +default for these options is expressed in terms of `${prefix}', so that +specifying just `--prefix' will affect all of the other directory +specifications that were not explicitly provided. + + The most portable way to affect installation locations is to pass the +correct locations to `configure'; however, many packages provide one or +both of the following shortcuts of passing variable assignments to the +`make install' command line to change installation locations without +having to reconfigure or recompile. + + The first method involves providing an override variable for each +affected directory. For example, `make install +prefix=/alternate/directory' will choose an alternate location for all +directory configuration variables that were expressed in terms of +`${prefix}'. Any directories that were specified during `configure', +but not in terms of `${prefix}', must each be overridden at install +time for the entire installation to be relocated. The approach of +makefile variable overrides for each directory variable is required by +the GNU Coding Standards, and ideally causes no recompilation. +However, some platforms have known limitations with the semantics of +shared libraries that end up requiring recompilation when using this +method, particularly noticeable in packages that use GNU Libtool. + + The second method involves providing the `DESTDIR' variable. For +example, `make install DESTDIR=/alternate/directory' will prepend +`/alternate/directory' before all installation names. The approach of +`DESTDIR' overrides is not required by the GNU Coding Standards, and +does not work on platforms that have drive letters. On the other hand, +it does better at avoiding recompilation issues, and works well even +when some directory options were not specified in terms of `${prefix}' +at `configure' time. + +Optional Features +================= + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + + Some packages offer the ability to configure how verbose the +execution of `make' will be. For these packages, running `./configure +--enable-silent-rules' sets the default to minimal output, which can be +overridden with `make V=1'; while running `./configure +--disable-silent-rules' sets the default to verbose, which can be +overridden with `make V=0'. + +Particular systems +================== + + On HP-UX, the default C compiler is not ANSI C compatible. If GNU +CC is not installed, it is recommended to use the following options in +order to use an ANSI C compiler: + + ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" + +and if that doesn't work, install pre-built binaries of GCC for HP-UX. + + On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot +parse its `' header file. The option `-nodtk' can be used as +a workaround. If GNU CC is not installed, it is therefore recommended +to try + + ./configure CC="cc" + +and if that doesn't work, try + + ./configure CC="cc -nodtk" + + On Solaris, don't put `/usr/ucb' early in your `PATH'. This +directory contains several dysfunctional programs; working variants of +these programs are available in `/usr/bin'. So, if you need `/usr/ucb' +in your `PATH', put it _after_ `/usr/bin'. + + On Haiku, software installed for all users goes in `/boot/common', +not `/usr/local'. It is recommended to use the following options: + + ./configure --prefix=/boot/common + +Specifying the System Type +========================== + + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS + KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf bug. Until the bug is fixed you can use this workaround: + + CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + + `configure' recognizes the following options to control how it +operates. + +`--help' +`-h' + Print a summary of all of the options to `configure', and exit. + +`--help=short' +`--help=recursive' + Print a summary of the options unique to this package's + `configure', and exit. The `short' variant lists options used + only in the top level, while the `recursive' variant lists options + also present in any nested packages. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--prefix=DIR' + Use DIR as the installation prefix. *note Installation Names:: + for more details, including other options available for fine-tuning + the installation locations. + +`--no-create' +`-n' + Run the configure checks, but stop before creating any output + files. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..b60ef2a --- /dev/null +++ b/Makefile.am @@ -0,0 +1,9 @@ +SUBDIRS = po src pixmaps/scalable pixmaps/48x48 www tests +dist_doc_DATA = README COPYING NEWS AUTHORS INSTALL + +desktopdir = $(datadir)/applications +desktop_DATA = psensor.desktop + +EXTRA_DIST = $(desktop_DATA) + + diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..4cdefcd --- /dev/null +++ b/NEWS @@ -0,0 +1,248 @@ +* v0.6.2.8 + +** psensor: escaped - in manpage +** psensor-server: escaped - in manpage +** psensor: add support of libnotify 0.7 + +* v0.6.2.7 + +** psensor: src/glade/psensor-pref.glade, added button separator. +** psensor: src/glade/sensor-edit.glade, fixed horiz/vert padding. +** psensor: src/lib/measure.c, initialize time. +** psensor: fixed freeze when click on preferences appindicator with + natty. + +* v0.6.2.6 + +** psensor: fixed min temp/fan of the graph +** psensor: very minor optimization (graph.c) + +* v0.6.2.5 + +** psensor: dialog box for editing psensor preferences is using Glade +** psensor: dialog box for editing sensor preferences is using Glade +** psensor: added support of negative temperatures (lm-sensors) + +* v0.6.2.4 + +** psensor, graph: fixed padding bugs +** psensor, graph: drawing code refactored +** psensor: the value of the first detected sensor is displayed in the +unity launcher entry of psensor (requires unity >=3.4.2). + +* v0.6.2.3 + +** psensor-server: new global variable 'psensor_version' for lua template +** psensor-server: /index.html replaced by /index.lua +** avoid useless lib linkage by using --as-needed + +* v0.6.2.2 + +** psensor-server: web server support binary files +** psensor-server: added favicon.ico +** psensor-server: moved index.lua to monitor.lua +** psensor-server: added index.html (welcome page) + +* v0.6.2.1 + +** added uptime/memory information (psensor-server web) +** added debug mode for psensor-server (--debug) +** when used in debug mode, psensor-server can be stopped remotely + using http://hostname:port/api/1.0/server/stop +** fixed memory leak (label of lmsensor) +** some code cleanup/refactoring + +* v0.6.1.7 + +** psensor: add support of libnotify 0.7 + +* v0.6.1.6 + +** psensor: escaped - in manpage +** psensor-server: escaped - in manpage + +* v0.6.1.5 + +** added multi-language for hdd.c/nvidia.c/lmsensor.c +** some code cleanup/refactoring + +* v0.6.1.4 + +** fixed manpage formatting in 'REPORTING BUGS' section +** improved manpages +** removed few useless translations (fr/zh_CN) +** improved error messages for remote sensors + multilanguages support + +* v0.6.1.3 + +** fixed compilation error of psensor when libcurl is present but not + libjson0 +** suppress pointer to Textinfo manual (psensor/psensor-server). +** added --name to help2man call (psensor/psensor-server manpages). +** added more information in the psensor/psensor-server manpages. +** added warning about psensor-server and security in README. + +* v0.6.1.1 + +** application icon is loaded using GtkIconTheme +** appindicator icon is now the psensor one +** added appindicator icon for status 'attention' + +* v0.6.1 + +** added style.css for psensor-server +** use some styling in index.lua +** fixed typo in README +** fixed psensor-server short option -p +** multi-language support for psensor-server +** added some translation for French language0 +** fixed psensor-server bug: first sensor was not displayed + +* v0.6.0.14 + +** psensor (gtk client): + +*** added global preference: graph update interval +*** added global preference: graph monitoring duration +*** added global preference: sensor update interval +*** standard gnu command line interface for psensor +*** new psensor options: --help, --version, --url +*** added generation and installation of the psensor man +*** added remote monitoring it requires a new optionnal dependencies + (libjson0 and libcurl) +*** added internationnalization support for the UI +*** added Simplified Chinese Language (from DaBao ) + +** psensor-server: + +*** psensor-server is now a minimal webserver based on microhttpd. + Lua5.1 is used to generate HTML pages to display temperatures and + gtop2 information +*** temperature information can be retrieved using a webservice based + on json +*** added cpu information (requires optional libgtop-2.0) +*** psensor-server +*** psensor-server options: --port, --help, --version +*** added generation and installation of the psensor-server man + +** common news for psensor and psensor-server: + +*** moved to autoconf/automake +*** gnu standard: use NEWS instead of CHANGES file +*** moved LICENSE to COPYING + +* v0.5.1 + +** Makefile: avoid debug compilation by default +** Makefile: fixed duplicate server.o in OBJS_SERVER +** moved LICENSE to copyright +** moved CHANGES to NEWS +** Fixed creation of directories for the target 'install' + +* v0.5.0 + +** Added double buffering of the graph to avoid flickering +** Fixed README error gconf package name +** Refactoring makefile +** Added desktop notification support +** Added application indicator support +** Fixed MT issue (UI freeze, refresh_thread) +** Added global preferences editor (right click on the graph) +** Added sensor preferences editor (right click on the sensor table) +** Displayed name of each sensor can be changed +** Position of the sensor table can be changed +** Window decoration can be hidden +** Added 'keep window below' setting + +* v0.4.5 + +** Makefile: fixed duplicate server.o in OBJS_SERVER + +v0.4.4 +Added README.debian + +v0.4.3 +Fixed foreground graph color first time psensor is started +Added support of gtk 2.12 (used by Debian 5.0.6 Lenny) by redefining +gtk_dialog_get_content_area which exist since 2.14 + +v0.4.2 +Fixed potential MT issue (refresh_thread) +Added double buffering of the graph to avoid flickering +Fixed minor graph code bugs + +v0.4.1 +Fixed README error gconf package name +Refactoring makefile + +v0.4.0 +Added FAN support +Added HDD support (using hddtemp daemon) +Added popup menu for changing graph bg/fg colors and opacity +Graph background can be transparent, opacity can be changed +Fixed BR3: sensors with the same name but different chips share the same color configuration +Fixed BR4: wrong OBJS variable and gconf when calling pkg-config in Makefile.distrib for compiling with NVidia support +Changed website URL +Fixed BR5: Psensor crashes during startup with "You forgot to call g_type_init()" + +v0.3.3 +Fixed potential MT issue (refresh_thread) +Improved Makefile + +v0.3.2 +Fixed BR4: wrong OBJS variable and gconf when calling pkg-config in Makefile.distrib for compiling with NVidia support +Changed website URL +Fixed BR5: Psensor crashes during startup with "You forgot to call g_type_init()" + +v0.3.1 +Fixed BR3: sensors with the same name but different chips share the same color configuration + +v0.3.0 +Sensor graph colors can be changed: click on the colored sensor cell in the information table) +Background color of the graph can be changed: click on the graph +Configuration is stored using GConf +Fixed not initialized memory bug (min/max) in nvidia support + +v0.2.7 +Updated the website URL +Added contact information in the README +Backported Makefile improvement from trunk +Removed pwiki styling of the README +Backport: Fixed not initialized memory bug (min/max) in nvidia support + + +v0.2.6 +Changed default graph colors and background +Added vertical padding for the graph canvas +Disabled row selection of the sensor table +Fixed BR2: nvidia gpu is always using black color + +v0.2.5 +Added a basic server to retrieve remotely temperatures + +v0.2.4 +Added application icon +Added .desktop file + +v0.2.3 +Added max and min temperature information for each sensor +Fixed a missing #include in nvidia.c +Small UI improvements (scrollbar for the right panel + slider) + +v0.2.2 +Fixed compilation warning when NVidia support is disabled +Fixed BR1: crash when no temperature sensor is available + +v0.2.1 +Compilation dependance to Nvidia lib is optional + +v0.2.0 +Added support of NVidia GPUs + +v0.1.1 +Improved right panel (list of sensors) + ability to disable each sensor graph + +v0.1.0 +Initial release + + diff --git a/README b/README new file mode 100644 index 0000000..9f2cf4c --- /dev/null +++ b/README @@ -0,0 +1,113 @@ +Psensor - Temperature Monitoring For Linux +------------------------------------------ + +Psensor is a graphical temperature monitor for Linux. + +It can monitor: + + * the temperature of the motherboard and CPU sensors (using + lm-sensors). + * the temperature of the NVidia GPUs (using XNVCtrl). + * the temperature of the Hard Disk Drives (using hddtemp). + * the rotation speed of the fans (using lm-sensors). + * the temperature of a remote computer. + +Alarms using Desktop Notification can be set to each sensor to notify +high temperatures. + +For Ubuntu users, Psensor is providing an Application Indicator to +alert users when a temperature is too high. + +To monitor the temperature of a remote computer: + + * start psensor-server on the remote computer. The default port is + 3131 and can be changed by using the '--port=PORT' command line + option. + + * start psensor with the '--url' option: + psensor --url=http://hostname:3131 + +WARNING: +psensor-server does not provide any way to restrict the connection to +the HTTP server, worst, no effort has been made against malicious HTTP +attacks. You should make the psensor-server port available only to a +network or computer you trust by using the usual network security +tools of the system (for example, iptables). + + +Installation +------------ + +Ubuntu +------ + +For Ubuntu, the easy way to install Psensor and Psensor-server is to +use the dedicated PPA. + +For the last stable version: +sudo apt-get apt-add-repository ppa:/jfi/psensor +sudo apt-get update +sudo apt-get install psensor + +For the last development version: +sudo apt-get apt-add-repository ppa:/jfi/psensor-unstable +sudo apt-get update +sudo apt-get install psensor +sudo apt-get install psensor-server + +Alternatively, you can download Ubuntu binary packages from: +http://wpitchoune.net/psensor/files/ubuntu/ +The manual installation of the psensor and psensor-server packages +requires also the installation of psensor-common which contains +the multiple languages support. + +Installation from source archive +-------------------------------- + +Psensor compilation requires: + + * make/gcc + * lm-sensors + * library sensors4 + * library gtk2 + * library gconf2 + * help2man + * library libnotify (optional) + * library libappindicator (optional) + * library libXNVCtrl (optional) + * library json0 and curl (optional, required for remote monitoring) + * library unity (>=v3.4.2, optional) + +Psensor-server compilation requires: + * make/gcc + * lm-sensors + * library sensors4 + * help2man + * library libmicrohttpd + * library json0 + * library LUA5.1 (optional, required to enable HTML page generation + of the psensor-server webserver) + * library gtop2 (optional, required for CPU usage) + +Compilation and Installation Steps +---------------------------------- + + * Extract files from the source archive + * Compilation: + ./configure + make clean all + + * Installation: + make install + + * Start the sensor detection script: 'sensors-detect' and follows the +instructions + + * Verify the sensor detection by running: 'sensors' + + * Run 'psensor' + +Contact +------- +Bugs and comments can be sent to wpitchoune@gmail.com +Home page: http://wpitchoune.net/psensor \ No newline at end of file diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..17410da --- /dev/null +++ b/configure.ac @@ -0,0 +1,221 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ([2.64]) +AC_INIT([psensor], [0.6.2.8],[wpitchoune@gmail.com],[psensor],[http://wpitchoune.net/psensor]) + +AM_INIT_AUTOMAKE([-Wall -Werror gnu]) + +AC_CONFIG_SRCDIR([src/compat.h]) +AC_CONFIG_HEADERS([config.h]) + +# Checks for programs. +AC_PROG_CC +AM_PROG_CC_C_O + +# Checks lib build +AC_PROG_RANLIB + +# Checks for header files. +AC_PATH_X +AC_CHECK_HEADERS([arpa/inet.h netinet/in.h stdint.h stdlib.h string.h sys/socket.h sys/time.h unistd.h getopt.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_TYPE_SIZE_T +AC_TYPE_SSIZE_T + +# Checks for library functions. +AC_FUNC_MALLOC +AC_FUNC_REALLOC +AC_CHECK_FUNCS([gettimeofday memmove socket strdup strtol]) + +AM_GNU_GETTEXT_VERSION([0.16]) +AM_GNU_GETTEXT([external]) + +############### common + +# Checks sensors, required by psensor and psensor-server +AC_CHECK_LIB(sensors, sensors_init) +AC_CHECK_HEADERS([sensors/sensors.h sensors/errors.h]) +SENSORS_LIBS=-lsensors +AC_SUBST(SENSORS_LIBS) + +############### psensor + +### Required + +PKG_CHECK_MODULES(X11, x11) +AC_SUBST(X11_CFLAGS) +AC_SUBST(X11_LIBS) + +PKG_CHECK_MODULES(XEXT, xext) +AC_SUBST(XEXT_CFLAGS) +AC_SUBST(XEXT_LIBS) + +# Checks GTK +PKG_CHECK_MODULES(GTK, gtk+-2.0 ) +AC_SUBST(GTK_CFLAGS) +AC_SUBST(GTK_LIBS) + +# Check GCONF +PKG_CHECK_MODULES(GCONF, gconf-2.0) +AC_SUBST(GCONF_CFLAGS) +AC_SUBST(GCONF_LIBS) + +### Optional + +# Check libnotify +LIBNOTIFY_LIBS= +PKG_CHECK_MODULES(LIBNOTIFY, + libnotify, + [AC_DEFINE([HAVE_LIBNOTIFY],[1],[Use desktop notification])], + [AC_MSG_WARN("Desktop notification disabled")]) +AM_CONDITIONAL(LIBNOTIFY, test -n "$LIBNOTIFY_LIBS") +AC_SUBST(LIBNOTIFY_CFLAGS) +AC_SUBST(LIBNOTIFY_LIBS) + +# Checks AppIndicator +APPINDICATOR_LIBS= +PKG_CHECK_MODULES(APPINDICATOR, + appindicator-0.1 = 0.2.9 , + [AC_DEFINE([HAVE_APPINDICATOR],[1],[Use AppIndicator 0.2.9]) + AC_DEFINE([HAVE_APPINDICATOR_029],[1],[Use AppIndicator 0.2.9]) + ], + [AC_MSG_WARN(AppIndicator 0.2.9 not present")]) + +if test "$APPINDICATOR_LIBS" == ""; then + PKG_CHECK_MODULES(APPINDICATOR, + appindicator-0.1 > 0.2.9, + [AC_DEFINE([HAVE_APPINDICATOR],[1],[Use AppIndicator > 0.2.9])], + [AC_MSG_WARN("AppIndicator > 0.2.9 not present")]) +fi +AM_CONDITIONAL(APPINDICATOR, test -n "$APPINDICATOR_LIBS") +AC_SUBST(APPINDICATOR_CFLAGS) +AC_SUBST(APPINDICATOR_LIBS) + +# Check CURL, needed for remote monitoring +CURL_LIBS= +PKG_CHECK_MODULES(CURL, + libcurl, + [AC_DEFINE([HAVE_CURL],[1],[Use CURL])], + [AC_MSG_WARN("Remote monitoring disabled, curl missing")]) +AM_CONDITIONAL(CURL, test -n "$CURL_LIBS") +AC_SUBST(CURL_CFLAGS) +AC_SUBST(CURL_LIBS) + +# Check JSON, needed for remote monitoring +JSON_LIBS= +PKG_CHECK_MODULES(JSON, + json, + [AC_DEFINE([HAVE_JSON],[1],[Use JSON])], + [AC_MSG_WARN("Remote monitoring disabled, json missing")]) +AM_CONDITIONAL(JSON, test -n "$JSON_LIBS") +AC_SUBST(JSON_CFLAGS) +AC_SUBST(JSON_LIBS) + +# Enables remote monitoring if JSON and CURL is present +if test -n "$JSON_LIBS"; then + if test -n "$CURL_LIBS"; then + AC_DEFINE([HAVE_REMOTE_SUPPORT],[1],[Remote monitoring enabled]) + fi +fi + +# Checks NVIDIA +# following code from sensors-applet +# sensors-applet.sourceforge.net/ +AC_CHECK_HEADERS(NVCtrl/NVCtrl.h NVCtrl/NVCtrlLib.h, + [ HAVE_NVIDIA=true ], [], + [ + #include + ]) +if test -n "${X11_LIBS}"; then +LIBS="${LIBS} -lX11 -lXext" + +if test "${HAVE_NVIDIA}" = "true"; then + AC_CHECK_LIB(XNVCtrl,XNVCTRLQueryExtension, + [NVIDIA_LIBS="-lXNVCtrl -lX11 -lXext" + AC_DEFINE(HAVE_NVIDIA,1,[NVidia support enabled]) + ]) +fi +fi +AM_CONDITIONAL(NVIDIA, test -n "$NVIDIA_LIBS") +AC_SUBST(NVIDIA_CFLAGS) +AC_SUBST(NVIDIA_LIBS) + +# Checks Unity +PKG_CHECK_MODULES(UNITY, + unity >= 3.4.2, + [AC_DEFINE([HAVE_UNITY],[1],[Use Unity])], + [AC_MSG_WARN(Unity not present)]) +AC_SUBST(UNITY_CFLAGS) +AC_SUBST(UNITY_LIBS) +AM_CONDITIONAL(UNITY, test -n "$UNITY_LIBS") + +############### psensor-server + +# Checks Lua 5.1 +LUA_LIBS= +PKG_CHECK_MODULES(LUA, + lua, + [AC_DEFINE([HAVE_LUA],[1],[Use Lua])], + [AC_MSG_WARN(Lua not present, checks Lua5.1)]) + +# Ubuntu pkg module is lua5.1 not lua +if test "$LUA_LIBS" == ""; then + PKG_CHECK_MODULES(LUA, + lua5.1, + [AC_DEFINE([HAVE_LUA],[1],[Use Lua5.1])], + [AC_MSG_WARN(Lua5.1 not present, psensor-server will NOT be built")]) +fi +AM_CONDITIONAL(LUA, test -n "$LUA_LIBS") +AC_SUBST(LUA_CFLAGS) +AC_SUBST(LUA_LIBS) + +# libmicrohttpd, mandatory for psensor-server +LIBMICROHTTPD_LIBS= +PKG_CHECK_MODULES(LIBMICROHTTPD, + libmicrohttpd, + [AC_DEFINE([HAVE_LIBMICROHTTPD],[1],[Use libmicrohttpd])], + [AC_MSG_WARN("libmicrohttpd not present, psensor-server will NOT be built")]) +AM_CONDITIONAL(LIBMICROHTTPD, test -n "$LIBMICROHTTPD_LIBS") +AC_SUBST(LIBMICROHTTPD_CFLAGS) +AC_SUBST(LIBMICROHTTPD_LIBS) + +# GTop, optional + +AC_ARG_WITH(gtop, +[ --with-gtop[=yes|no] use gtop],[ + with_gtop=$withval],[ + with_gtop="yes" +]) + +GTOP_LIBS= +if test "$with_gtop" = "yes"; then +PKG_CHECK_MODULES(GTOP, + libgtop-2.0, + [AC_DEFINE([HAVE_GTOP],[1],[Use GTOP])], + [AC_MSG_WARN("gtop not present, CPU/Memory information will NOT be available")]) +fi +AM_CONDITIONAL(GTOP, test -n "$GTOP_LIBS") +AC_SUBST(GTOP_CFLAGS) +AC_SUBST(GTOP_LIBS) + +AC_CONFIG_FILES([ + Makefile + src/Makefile + src/glade/Makefile + src/plib/Makefile + src/lib/Makefile + src/unity/Makefile + src/libpsensor_json/Makefile + src/server/Makefile + pixmaps/scalable/Makefile + pixmaps/48x48/Makefile + www/Makefile + po/Makefile.in + tests/Makefile +]) + +AC_CHECK_PROGS([HELP2MAN], [help2man]) + +AC_OUTPUT diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..4bca628 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,94 @@ +psensor (0.6.1.3-3) lucid; urgency=low + + * bump lucid + + -- Jean-Philippe Orsini Wed, 16 Feb 2011 10:13:55 +0100 + +psensor (0.6.1.3-2) natty; urgency=low + + * bump natty + + -- Jean-Philippe Orsini Wed, 16 Feb 2011 10:13:08 +0100 + +psensor (0.6.1.3-1) maverick; urgency=low + + * upgrade upstream + * removed debian/tmp in .install files + * improved long description of the packages + * fixed debian/copyright(add real name and license preamble) + * use dh_installman + * added debian/watch file + + -- Jean-Philippe Orsini Wed, 16 Feb 2011 09:56:05 +0100 + +psensor (0.6.1.1-3) lucid; urgency=low + + * bump lucid + + -- jeanfi Tue, 15 Feb 2011 12:10:08 +0100 + +psensor (0.6.1.1-2) natty; urgency=low + + * bump natty + + -- jeanfi Tue, 15 Feb 2011 12:09:19 +0100 + +psensor (0.6.1.1-1) maverick; urgency=low + + * upgrade upstream + + -- jeanfi Tue, 15 Feb 2011 11:41:38 +0100 + +psensor (0.6.1-4) natty; urgency=low + + * bump natty + + -- jeanfi Mon, 14 Feb 2011 12:18:28 +0100 + +psensor (0.6.1-3) lucid; urgency=low + + * bump lucid + + -- jeanfi Mon, 14 Feb 2011 12:17:37 +0100 + +psensor (0.6.1-2) maverick; urgency=low + + * fixed file conflicts concerning doc dir + + -- jeanfi Mon, 14 Feb 2011 11:47:16 +0100 + +psensor (0.6.1-1) maverick; urgency=low + + * upgrade upstream + + -- jeanfi Mon, 14 Feb 2011 10:52:24 +0100 + +psensor (0.6.0.14-5) karmic; urgency=low + + * bump karmic + + -- jeanfi Sun, 13 Feb 2011 14:05:07 +0100 + +psensor (0.6.0.14-4) maverick; urgency=low + + * www/index.lua in the psensor-server package + + -- jeanfi Sun, 13 Feb 2011 13:30:30 +0100 + +psensor (0.6.0.14-3) lucid; urgency=low + + * bump lucid + + -- jeanfi Sat, 12 Feb 2011 21:18:22 +0100 + +psensor (0.6.0.14-2) natty; urgency=low + + * bump natty + + -- jeanfi Sat, 12 Feb 2011 21:16:38 +0100 + +psensor (0.6.0.14-1) maverick; urgency=low + + * upgrade upstream + + -- jeanfi Sat, 12 Feb 2011 14:34:42 +0100 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..a670a4f --- /dev/null +++ b/debian/control @@ -0,0 +1,54 @@ +Source: psensor +Section: utils +Priority: optional +Maintainer: Jean-Philippe Orsini +Build-Depends: debhelper (>= 7.0.50~),libgtk2.0-dev,libgconf2-dev,libnotify-dev,libsensors4-dev,nvidia-settings,help2man,libcurl4-openssl-dev,libjson0-dev,liblua5.1-0-dev,libmicrohttpd-dev,libgtop2-dev,perl,libappindicator-dev +Standards-Version: 3.9.1 +Homepage: http://wpitchoune.net/psensor + +Package: psensor +Architecture: any +Depends: psensor-common (= ${source:Version}), ${shlibs:Depends}, ${misc:Depends} +Recommends: hddtemp +Description: Display graphs for monitoring hardware temperature. + Psensor is a GTK application for monitoring hardware sensors, + including temperatures and fan speeds. + . + It displays a curve for each sensor, alerts user using Desktop Notification + and Application Indicator when a temperature is too high. + . + It can monitor: + * the temperature of the motherboard and CPU sensors (using lm-sensors). + * the temperature of the NVidia GPUs (using XNVCtrl). + * the temperature of the Hard Disk Drives (using hddtemp). + * the rotation speed of the fans (using lm-sensors). + * the sensors of a remote computer (using psensor-server). + . + +Package: psensor-server +Architecture: any +Depends: psensor-common (= ${source:Version}), ${shlibs:Depends}, ${misc:Depends} +Recommends: hddtemp +Description: Psensor server for monitoring hardware sensors remotely. + . + Psensor server is an HTTP server providing a JSON Web service which can be + used by Psensor GTK Application to monitor remotely the hardware sensors + of a computer. + . + It can provide information about: + * the temperature of the motherboard and CPU sensors (using lm-sensors). + * the temperature of the NVidia GPUs (using XNVCtrl). + * the temperature of the Hard Disk Drives (using hddtemp). + * the rotation speed of the fans (using lm-sensors). + . + It is also possible to connect to Psensor server with a browser, a simple + Web page is displaying the information. + . + +Package: psensor-common +Architecture: all +Depends: ${misc:Depends} +Description: Common files for Psensor and Psensor server. + . + It contains languages packs for both Psensor and Psensor server. + . \ No newline at end of file diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..7be00c5 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,32 @@ +This package was debianized by Jean-Philippe Orsini +Tue, 15 Feb 18:33:41 CET 2011. + +It was downloaded from: http://wpitchoune.net/psensor + +Upstream Author(s): Jean-Philippe Orsini + +Copyright: + Copyright (C) 2010-2011 by Jean-Philippe Orsini + +License: + + This package is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 dated June, 1991. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this package; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301, USA. + +On Debian/Ubuntu systems, the complete text of the GNU General Public +License can be found in `/usr/share/common-licenses/GPL-2'. + +Packaging: + Copyright (C) 2010-2011 by Jean-Philippe Orsini + released under GNU General Public License version 2. \ No newline at end of file diff --git a/debian/docs b/debian/docs new file mode 100644 index 0000000..139597f --- /dev/null +++ b/debian/docs @@ -0,0 +1,2 @@ + + diff --git a/debian/psensor-common.install b/debian/psensor-common.install new file mode 100644 index 0000000..94125c7 --- /dev/null +++ b/debian/psensor-common.install @@ -0,0 +1 @@ +/usr/share/locale diff --git a/debian/psensor-server.install b/debian/psensor-server.install new file mode 100644 index 0000000..7bcc095 --- /dev/null +++ b/debian/psensor-server.install @@ -0,0 +1,3 @@ +/usr/bin/psensor-server +/usr/share/psensor/www/index.lua + diff --git a/debian/psensor-server.manpages b/debian/psensor-server.manpages new file mode 100644 index 0000000..4d121e6 --- /dev/null +++ b/debian/psensor-server.manpages @@ -0,0 +1 @@ +debian/tmp/usr/share/man/man1/psensor-server.1 \ No newline at end of file diff --git a/debian/psensor.install b/debian/psensor.install new file mode 100644 index 0000000..8f69a68 --- /dev/null +++ b/debian/psensor.install @@ -0,0 +1,8 @@ +/usr/bin/psensor +/usr/share/icons/hicolor/scalable/apps/psensor.svg +/usr/share/icons/hicolor/scalable/apps/psensor_hot.svg +/usr/share/icons/hicolor/48x48/apps/psensor.png +/usr/share/icons/hicolor/48x48/apps/psensor_hot.png +/usr/share/applications/psensor.desktop + + diff --git a/debian/psensor.manpages b/debian/psensor.manpages new file mode 100644 index 0000000..16e0814 --- /dev/null +++ b/debian/psensor.manpages @@ -0,0 +1,2 @@ +debian/tmp/usr/share/man/man1/psensor.1 + diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..b760bee --- /dev/null +++ b/debian/rules @@ -0,0 +1,13 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# This file was originally written by Joey Hess and Craig Small. +# As a special exception, when this file is copied by dh-make into a +# dh-make output file, you may use that output file without restriction. +# This special exception was added by Craig Small in version 0.37 of dh-make. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +%: + dh $@ diff --git a/debian/watch b/debian/watch new file mode 100644 index 0000000..8833e44 --- /dev/null +++ b/debian/watch @@ -0,0 +1,3 @@ +version=3 + +http://wpitchoune.net/psensor/files/psensor-(\d.*)\.tar\.gz \ No newline at end of file diff --git a/pixmaps/16x16/psensor.png b/pixmaps/16x16/psensor.png new file mode 100644 index 0000000..d06f187 Binary files /dev/null and b/pixmaps/16x16/psensor.png differ diff --git a/pixmaps/22x22/psensor.png b/pixmaps/22x22/psensor.png new file mode 100644 index 0000000..be45344 Binary files /dev/null and b/pixmaps/22x22/psensor.png differ diff --git a/pixmaps/24x24/psensor.png b/pixmaps/24x24/psensor.png new file mode 100644 index 0000000..93f2d79 Binary files /dev/null and b/pixmaps/24x24/psensor.png differ diff --git a/pixmaps/32x32/psensor.png b/pixmaps/32x32/psensor.png new file mode 100644 index 0000000..02028ce Binary files /dev/null and b/pixmaps/32x32/psensor.png differ diff --git a/pixmaps/48x48/Makefile.am b/pixmaps/48x48/Makefile.am new file mode 100644 index 0000000..7d09bac --- /dev/null +++ b/pixmaps/48x48/Makefile.am @@ -0,0 +1,6 @@ +themedir = $(datadir)/icons/hicolor +size = 48x48 +context = apps +EXTRA_DIST = psensor.png psensor_hot.png +psensordistpixdir = $(themedir)/$(size)/$(context) +psensordistpix_DATA = $(EXTRA_DIST) diff --git a/pixmaps/48x48/psensor.png b/pixmaps/48x48/psensor.png new file mode 100644 index 0000000..3acb429 Binary files /dev/null and b/pixmaps/48x48/psensor.png differ diff --git a/pixmaps/48x48/psensor_hot.png b/pixmaps/48x48/psensor_hot.png new file mode 100644 index 0000000..6f05f59 Binary files /dev/null and b/pixmaps/48x48/psensor_hot.png differ diff --git a/pixmaps/scalable/Makefile.am b/pixmaps/scalable/Makefile.am new file mode 100644 index 0000000..47adc6c --- /dev/null +++ b/pixmaps/scalable/Makefile.am @@ -0,0 +1,6 @@ +themedir = $(datadir)/icons/hicolor +size = scalable +context = apps +EXTRA_DIST = psensor.svg psensor_hot.svg +psensordistpixdir = $(themedir)/$(size)/$(context) +psensordistpix_DATA = $(EXTRA_DIST) diff --git a/pixmaps/scalable/psensor.svg b/pixmaps/scalable/psensor.svg new file mode 100644 index 0000000..2bd5d03 --- /dev/null +++ b/pixmaps/scalable/psensor.svg @@ -0,0 +1,276 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pixmaps/scalable/psensor_hot.svg b/pixmaps/scalable/psensor_hot.svg new file mode 100644 index 0000000..c4e9e60 --- /dev/null +++ b/pixmaps/scalable/psensor_hot.svg @@ -0,0 +1,276 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/po/LINGUAS b/po/LINGUAS new file mode 100644 index 0000000..12a5907 --- /dev/null +++ b/po/LINGUAS @@ -0,0 +1,10 @@ +# Set of available languages. +# +# Copyright (C) 1992, 1993, 1995, 1996, 1997, 1998, 1999, 2000, 2001, +# 2002, 2003, 2005, 2006, 2007 Free Software Foundation, Inc. +# +# Copying and distribution of this file, with or without modification, +# are permitted in any medium without royalty provided the copyright +# notice and this notice are preserved. +fr +zh_CN diff --git a/po/Makefile.in.in b/po/Makefile.in.in new file mode 100644 index 0000000..5022b8b --- /dev/null +++ b/po/Makefile.in.in @@ -0,0 +1,403 @@ +# Makefile for PO directory in any package using GNU gettext. +# Copyright (C) 1995-1997, 2000-2006 by Ulrich Drepper +# +# This file can be copied and used freely without restrictions. It can +# be used in projects which are not available under the GNU General Public +# License but which still want to provide support for the GNU gettext +# functionality. +# Please note that the actual code of GNU gettext is covered by the GNU +# General Public License and is *not* in the public domain. +# +# Origin: gettext-0.16 + +PACKAGE = @PACKAGE@ +VERSION = @VERSION@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ + +SHELL = /bin/sh +@SET_MAKE@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +datarootdir = @datarootdir@ +datadir = @datadir@ +localedir = @localedir@ +gettextsrcdir = $(datadir)/gettext/po + +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ + +# We use $(mkdir_p). +# In automake <= 1.9.x, $(mkdir_p) is defined either as "mkdir -p --" or as +# "$(mkinstalldirs)" or as "$(install_sh) -d". For these automake versions, +# @install_sh@ does not start with $(SHELL), so we add it. +# In automake >= 1.10, @mkdir_p@ is derived from ${MKDIR_P}, which is defined +# either as "/path/to/mkdir -p" or ".../install-sh -c -d". For these automake +# versions, $(mkinstalldirs) and $(install_sh) are unused. +mkinstalldirs = $(SHELL) @install_sh@ -d +install_sh = $(SHELL) @install_sh@ +MKDIR_P = @MKDIR_P@ +mkdir_p = @mkdir_p@ + +GMSGFMT_ = @GMSGFMT@ +GMSGFMT_no = @GMSGFMT@ +GMSGFMT_yes = @GMSGFMT_015@ +GMSGFMT = $(GMSGFMT_$(USE_MSGCTXT)) +MSGFMT_ = @MSGFMT@ +MSGFMT_no = @MSGFMT@ +MSGFMT_yes = @MSGFMT_015@ +MSGFMT = $(MSGFMT_$(USE_MSGCTXT)) +XGETTEXT_ = @XGETTEXT@ +XGETTEXT_no = @XGETTEXT@ +XGETTEXT_yes = @XGETTEXT_015@ +XGETTEXT = $(XGETTEXT_$(USE_MSGCTXT)) +MSGMERGE = msgmerge +MSGMERGE_UPDATE = @MSGMERGE@ --update +MSGINIT = msginit +MSGCONV = msgconv +MSGFILTER = msgfilter + +POFILES = @POFILES@ +GMOFILES = @GMOFILES@ +UPDATEPOFILES = @UPDATEPOFILES@ +DUMMYPOFILES = @DUMMYPOFILES@ +DISTFILES.common = Makefile.in.in remove-potcdate.sin \ +$(DISTFILES.common.extra1) $(DISTFILES.common.extra2) $(DISTFILES.common.extra3) +DISTFILES = $(DISTFILES.common) Makevars POTFILES.in \ +$(POFILES) $(GMOFILES) \ +$(DISTFILES.extra1) $(DISTFILES.extra2) $(DISTFILES.extra3) + +POTFILES = \ + +CATALOGS = @CATALOGS@ + +# Makevars gets inserted here. (Don't remove this line!) + +.SUFFIXES: +.SUFFIXES: .po .gmo .mo .sed .sin .nop .po-create .po-update + +.po.mo: + @echo "$(MSGFMT) -c -o $@ $<"; \ + $(MSGFMT) -c -o t-$@ $< && mv t-$@ $@ + +.po.gmo: + @lang=`echo $* | sed -e 's,.*/,,'`; \ + test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \ + echo "$${cdcmd}rm -f $${lang}.gmo && $(GMSGFMT) -c --statistics -o $${lang}.gmo $${lang}.po"; \ + cd $(srcdir) && rm -f $${lang}.gmo && $(GMSGFMT) -c --statistics -o t-$${lang}.gmo $${lang}.po && mv t-$${lang}.gmo $${lang}.gmo + +.sin.sed: + sed -e '/^#/d' $< > t-$@ + mv t-$@ $@ + + +all: all-@USE_NLS@ + +all-yes: stamp-po +all-no: + +# $(srcdir)/$(DOMAIN).pot is only created when needed. When xgettext finds no +# internationalized messages, no $(srcdir)/$(DOMAIN).pot is created (because +# we don't want to bother translators with empty POT files). We assume that +# LINGUAS is empty in this case, i.e. $(POFILES) and $(GMOFILES) are empty. +# In this case, stamp-po is a nop (i.e. a phony target). + +# stamp-po is a timestamp denoting the last time at which the CATALOGS have +# been loosely updated. Its purpose is that when a developer or translator +# checks out the package via CVS, and the $(DOMAIN).pot file is not in CVS, +# "make" will update the $(DOMAIN).pot and the $(CATALOGS), but subsequent +# invocations of "make" will do nothing. This timestamp would not be necessary +# if updating the $(CATALOGS) would always touch them; however, the rule for +# $(POFILES) has been designed to not touch files that don't need to be +# changed. +stamp-po: $(srcdir)/$(DOMAIN).pot + test ! -f $(srcdir)/$(DOMAIN).pot || \ + test -z "$(GMOFILES)" || $(MAKE) $(GMOFILES) + @test ! -f $(srcdir)/$(DOMAIN).pot || { \ + echo "touch stamp-po" && \ + echo timestamp > stamp-poT && \ + mv stamp-poT stamp-po; \ + } + +# Note: Target 'all' must not depend on target '$(DOMAIN).pot-update', +# otherwise packages like GCC can not be built if only parts of the source +# have been downloaded. + +# This target rebuilds $(DOMAIN).pot; it is an expensive operation. +# Note that $(DOMAIN).pot is not touched if it doesn't need to be changed. +$(DOMAIN).pot-update: $(POTFILES) $(srcdir)/POTFILES.in remove-potcdate.sed + if test -n '$(MSGID_BUGS_ADDRESS)' || test '$(PACKAGE_BUGREPORT)' = '@'PACKAGE_BUGREPORT'@'; then \ + msgid_bugs_address='$(MSGID_BUGS_ADDRESS)'; \ + else \ + msgid_bugs_address='$(PACKAGE_BUGREPORT)'; \ + fi; \ + $(XGETTEXT) --default-domain=$(DOMAIN) --directory=$(top_srcdir) \ + --add-comments=TRANSLATORS: $(XGETTEXT_OPTIONS) \ + --files-from=$(srcdir)/POTFILES.in \ + --copyright-holder='$(COPYRIGHT_HOLDER)' \ + --msgid-bugs-address="$$msgid_bugs_address" + test ! -f $(DOMAIN).po || { \ + if test -f $(srcdir)/$(DOMAIN).pot; then \ + sed -f remove-potcdate.sed < $(srcdir)/$(DOMAIN).pot > $(DOMAIN).1po && \ + sed -f remove-potcdate.sed < $(DOMAIN).po > $(DOMAIN).2po && \ + if cmp $(DOMAIN).1po $(DOMAIN).2po >/dev/null 2>&1; then \ + rm -f $(DOMAIN).1po $(DOMAIN).2po $(DOMAIN).po; \ + else \ + rm -f $(DOMAIN).1po $(DOMAIN).2po $(srcdir)/$(DOMAIN).pot && \ + mv $(DOMAIN).po $(srcdir)/$(DOMAIN).pot; \ + fi; \ + else \ + mv $(DOMAIN).po $(srcdir)/$(DOMAIN).pot; \ + fi; \ + } + +# This rule has no dependencies: we don't need to update $(DOMAIN).pot at +# every "make" invocation, only create it when it is missing. +# Only "make $(DOMAIN).pot-update" or "make dist" will force an update. +$(srcdir)/$(DOMAIN).pot: + $(MAKE) $(DOMAIN).pot-update + +# This target rebuilds a PO file if $(DOMAIN).pot has changed. +# Note that a PO file is not touched if it doesn't need to be changed. +$(POFILES): $(srcdir)/$(DOMAIN).pot + @lang=`echo $@ | sed -e 's,.*/,,' -e 's/\.po$$//'`; \ + if test -f "$(srcdir)/$${lang}.po"; then \ + test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \ + echo "$${cdcmd}$(MSGMERGE_UPDATE) $${lang}.po $(DOMAIN).pot"; \ + cd $(srcdir) && $(MSGMERGE_UPDATE) $${lang}.po $(DOMAIN).pot; \ + else \ + $(MAKE) $${lang}.po-create; \ + fi + + +install: install-exec install-data +install-exec: +install-data: install-data-@USE_NLS@ + if test "$(PACKAGE)" = "gettext-tools"; then \ + $(mkdir_p) $(DESTDIR)$(gettextsrcdir); \ + for file in $(DISTFILES.common) Makevars.template; do \ + $(INSTALL_DATA) $(srcdir)/$$file \ + $(DESTDIR)$(gettextsrcdir)/$$file; \ + done; \ + for file in Makevars; do \ + rm -f $(DESTDIR)$(gettextsrcdir)/$$file; \ + done; \ + else \ + : ; \ + fi +install-data-no: all +install-data-yes: all + $(mkdir_p) $(DESTDIR)$(datadir) + @catalogs='$(CATALOGS)'; \ + for cat in $$catalogs; do \ + cat=`basename $$cat`; \ + lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \ + dir=$(localedir)/$$lang/LC_MESSAGES; \ + $(mkdir_p) $(DESTDIR)$$dir; \ + if test -r $$cat; then realcat=$$cat; else realcat=$(srcdir)/$$cat; fi; \ + $(INSTALL_DATA) $$realcat $(DESTDIR)$$dir/$(DOMAIN).mo; \ + echo "installing $$realcat as $(DESTDIR)$$dir/$(DOMAIN).mo"; \ + for lc in '' $(EXTRA_LOCALE_CATEGORIES); do \ + if test -n "$$lc"; then \ + if (cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc 2>/dev/null) | grep ' -> ' >/dev/null; then \ + link=`cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc | sed -e 's/^.* -> //'`; \ + mv $(DESTDIR)$(localedir)/$$lang/$$lc $(DESTDIR)$(localedir)/$$lang/$$lc.old; \ + mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \ + (cd $(DESTDIR)$(localedir)/$$lang/$$lc.old && \ + for file in *; do \ + if test -f $$file; then \ + ln -s ../$$link/$$file $(DESTDIR)$(localedir)/$$lang/$$lc/$$file; \ + fi; \ + done); \ + rm -f $(DESTDIR)$(localedir)/$$lang/$$lc.old; \ + else \ + if test -d $(DESTDIR)$(localedir)/$$lang/$$lc; then \ + :; \ + else \ + rm -f $(DESTDIR)$(localedir)/$$lang/$$lc; \ + mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \ + fi; \ + fi; \ + rm -f $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \ + ln -s ../LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo 2>/dev/null || \ + ln $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo 2>/dev/null || \ + cp -p $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \ + echo "installing $$realcat link as $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo"; \ + fi; \ + done; \ + done + +install-strip: install + +installdirs: installdirs-exec installdirs-data +installdirs-exec: +installdirs-data: installdirs-data-@USE_NLS@ + if test "$(PACKAGE)" = "gettext-tools"; then \ + $(mkdir_p) $(DESTDIR)$(gettextsrcdir); \ + else \ + : ; \ + fi +installdirs-data-no: +installdirs-data-yes: + $(mkdir_p) $(DESTDIR)$(datadir) + @catalogs='$(CATALOGS)'; \ + for cat in $$catalogs; do \ + cat=`basename $$cat`; \ + lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \ + dir=$(localedir)/$$lang/LC_MESSAGES; \ + $(mkdir_p) $(DESTDIR)$$dir; \ + for lc in '' $(EXTRA_LOCALE_CATEGORIES); do \ + if test -n "$$lc"; then \ + if (cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc 2>/dev/null) | grep ' -> ' >/dev/null; then \ + link=`cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc | sed -e 's/^.* -> //'`; \ + mv $(DESTDIR)$(localedir)/$$lang/$$lc $(DESTDIR)$(localedir)/$$lang/$$lc.old; \ + mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \ + (cd $(DESTDIR)$(localedir)/$$lang/$$lc.old && \ + for file in *; do \ + if test -f $$file; then \ + ln -s ../$$link/$$file $(DESTDIR)$(localedir)/$$lang/$$lc/$$file; \ + fi; \ + done); \ + rm -f $(DESTDIR)$(localedir)/$$lang/$$lc.old; \ + else \ + if test -d $(DESTDIR)$(localedir)/$$lang/$$lc; then \ + :; \ + else \ + rm -f $(DESTDIR)$(localedir)/$$lang/$$lc; \ + mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \ + fi; \ + fi; \ + fi; \ + done; \ + done + +# Define this as empty until I found a useful application. +installcheck: + +uninstall: uninstall-exec uninstall-data +uninstall-exec: +uninstall-data: uninstall-data-@USE_NLS@ + if test "$(PACKAGE)" = "gettext-tools"; then \ + for file in $(DISTFILES.common) Makevars.template; do \ + rm -f $(DESTDIR)$(gettextsrcdir)/$$file; \ + done; \ + else \ + : ; \ + fi +uninstall-data-no: +uninstall-data-yes: + catalogs='$(CATALOGS)'; \ + for cat in $$catalogs; do \ + cat=`basename $$cat`; \ + lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \ + for lc in LC_MESSAGES $(EXTRA_LOCALE_CATEGORIES); do \ + rm -f $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \ + done; \ + done + +check: all + +info dvi ps pdf html tags TAGS ctags CTAGS ID: + +mostlyclean: + rm -f remove-potcdate.sed + rm -f stamp-poT + rm -f core core.* $(DOMAIN).po $(DOMAIN).1po $(DOMAIN).2po *.new.po + rm -fr *.o + +clean: mostlyclean + +distclean: clean + rm -f Makefile Makefile.in POTFILES *.mo + +maintainer-clean: distclean + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + rm -f stamp-po $(GMOFILES) + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) +dist distdir: + $(MAKE) update-po + @$(MAKE) dist2 +# This is a separate target because 'update-po' must be executed before. +dist2: stamp-po $(DISTFILES) + dists="$(DISTFILES)"; \ + if test "$(PACKAGE)" = "gettext-tools"; then \ + dists="$$dists Makevars.template"; \ + fi; \ + if test -f $(srcdir)/$(DOMAIN).pot; then \ + dists="$$dists $(DOMAIN).pot stamp-po"; \ + fi; \ + if test -f $(srcdir)/ChangeLog; then \ + dists="$$dists ChangeLog"; \ + fi; \ + for i in 0 1 2 3 4 5 6 7 8 9; do \ + if test -f $(srcdir)/ChangeLog.$$i; then \ + dists="$$dists ChangeLog.$$i"; \ + fi; \ + done; \ + if test -f $(srcdir)/LINGUAS; then dists="$$dists LINGUAS"; fi; \ + for file in $$dists; do \ + if test -f $$file; then \ + cp -p $$file $(distdir) || exit 1; \ + else \ + cp -p $(srcdir)/$$file $(distdir) || exit 1; \ + fi; \ + done + +update-po: Makefile + $(MAKE) $(DOMAIN).pot-update + test -z "$(UPDATEPOFILES)" || $(MAKE) $(UPDATEPOFILES) + $(MAKE) update-gmo + +# General rule for creating PO files. + +.nop.po-create: + @lang=`echo $@ | sed -e 's/\.po-create$$//'`; \ + echo "File $$lang.po does not exist. If you are a translator, you can create it through 'msginit'." 1>&2; \ + exit 1 + +# General rule for updating PO files. + +.nop.po-update: + @lang=`echo $@ | sed -e 's/\.po-update$$//'`; \ + if test "$(PACKAGE)" = "gettext-tools"; then PATH=`pwd`/../src:$$PATH; fi; \ + tmpdir=`pwd`; \ + echo "$$lang:"; \ + test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \ + echo "$${cdcmd}$(MSGMERGE) $$lang.po $(DOMAIN).pot -o $$lang.new.po"; \ + cd $(srcdir); \ + if $(MSGMERGE) $$lang.po $(DOMAIN).pot -o $$tmpdir/$$lang.new.po; then \ + if cmp $$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \ + rm -f $$tmpdir/$$lang.new.po; \ + else \ + if mv -f $$tmpdir/$$lang.new.po $$lang.po; then \ + :; \ + else \ + echo "msgmerge for $$lang.po failed: cannot move $$tmpdir/$$lang.new.po to $$lang.po" 1>&2; \ + exit 1; \ + fi; \ + fi; \ + else \ + echo "msgmerge for $$lang.po failed!" 1>&2; \ + rm -f $$tmpdir/$$lang.new.po; \ + fi + +$(DUMMYPOFILES): + +update-gmo: Makefile $(GMOFILES) + @: + +Makefile: Makefile.in.in Makevars $(top_builddir)/config.status @POMAKEFILEDEPS@ + cd $(top_builddir) \ + && $(SHELL) ./config.status $(subdir)/$@.in po-directories + +force: + +# Tell versions [3.59,3.63) of GNU make not to export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/po/Makevars b/po/Makevars new file mode 100644 index 0000000..32692ab --- /dev/null +++ b/po/Makevars @@ -0,0 +1,41 @@ +# Makefile variables for PO directory in any package using GNU gettext. + +# Usually the message domain is the same as the package name. +DOMAIN = $(PACKAGE) + +# These two variables depend on the location of this directory. +subdir = po +top_builddir = .. + +# These options get passed to xgettext. +XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ + +# This is the copyright holder that gets inserted into the header of the +# $(DOMAIN).pot file. Set this to the copyright holder of the surrounding +# package. (Note that the msgstr strings, extracted from the package's +# sources, belong to the copyright holder of the package.) Translators are +# expected to transfer the copyright for their translations to this person +# or entity, or to disclaim their copyright. The empty string stands for +# the public domain; in this case the translators are expected to disclaim +# their copyright. +COPYRIGHT_HOLDER = Free Software Foundation, Inc. + +# This is the email address or URL to which the translators shall report +# bugs in the untranslated strings: +# - Strings which are not entire sentences, see the maintainer guidelines +# in the GNU gettext documentation, section 'Preparing Strings'. +# - Strings which use unclear terms or require additional context to be +# understood. +# - Strings which make invalid assumptions about notation of date, time or +# money. +# - Pluralisation problems. +# - Incorrect English spelling. +# - Incorrect formatting. +# It can be your email address, or a mailing list address where translators +# can write to without being subscribed, or the URL of a web page through +# which the translators can contact you. +MSGID_BUGS_ADDRESS = + +# This is the list of locale categories, beyond LC_MESSAGES, for which the +# message catalogs shall be used. It is usually empty. +EXTRA_LOCALE_CATEGORIES = diff --git a/po/POTFILES.in b/po/POTFILES.in new file mode 100644 index 0000000..e04b4b2 --- /dev/null +++ b/po/POTFILES.in @@ -0,0 +1,12 @@ +src/main.c +src/ui_sensorlist.c +src/ui_appindicator.c +src/ui_color.c +src/ui_notify.c +src/ui_pref.c +src/rsensor.c +src/plib/plib_luatpl.c +src/server/server.c +src/lib/hdd.c +src/lib/nvidia.c +src/lib/lmsensor.c diff --git a/po/Rules-quot b/po/Rules-quot new file mode 100644 index 0000000..9c2a995 --- /dev/null +++ b/po/Rules-quot @@ -0,0 +1,47 @@ +# Special Makefile rules for English message catalogs with quotation marks. + +DISTFILES.common.extra1 = quot.sed boldquot.sed en@quot.header en@boldquot.header insert-header.sin Rules-quot + +.SUFFIXES: .insert-header .po-update-en + +en@quot.po-create: + $(MAKE) en@quot.po-update +en@boldquot.po-create: + $(MAKE) en@boldquot.po-update + +en@quot.po-update: en@quot.po-update-en +en@boldquot.po-update: en@boldquot.po-update-en + +.insert-header.po-update-en: + @lang=`echo $@ | sed -e 's/\.po-update-en$$//'`; \ + if test "$(PACKAGE)" = "gettext"; then PATH=`pwd`/../src:$$PATH; GETTEXTLIBDIR=`cd $(top_srcdir)/src && pwd`; export GETTEXTLIBDIR; fi; \ + tmpdir=`pwd`; \ + echo "$$lang:"; \ + ll=`echo $$lang | sed -e 's/@.*//'`; \ + LC_ALL=C; export LC_ALL; \ + cd $(srcdir); \ + if $(MSGINIT) -i $(DOMAIN).pot --no-translator -l $$ll -o - 2>/dev/null | sed -f $$tmpdir/$$lang.insert-header | $(MSGCONV) -t UTF-8 | $(MSGFILTER) sed -f `echo $$lang | sed -e 's/.*@//'`.sed 2>/dev/null > $$tmpdir/$$lang.new.po; then \ + if cmp $$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \ + rm -f $$tmpdir/$$lang.new.po; \ + else \ + if mv -f $$tmpdir/$$lang.new.po $$lang.po; then \ + :; \ + else \ + echo "creation of $$lang.po failed: cannot move $$tmpdir/$$lang.new.po to $$lang.po" 1>&2; \ + exit 1; \ + fi; \ + fi; \ + else \ + echo "creation of $$lang.po failed!" 1>&2; \ + rm -f $$tmpdir/$$lang.new.po; \ + fi + +en@quot.insert-header: insert-header.sin + sed -e '/^#/d' -e 's/HEADER/en@quot.header/g' $(srcdir)/insert-header.sin > en@quot.insert-header + +en@boldquot.insert-header: insert-header.sin + sed -e '/^#/d' -e 's/HEADER/en@boldquot.header/g' $(srcdir)/insert-header.sin > en@boldquot.insert-header + +mostlyclean: mostlyclean-quot +mostlyclean-quot: + rm -f *.insert-header diff --git a/po/boldquot.sed b/po/boldquot.sed new file mode 100644 index 0000000..4b937aa --- /dev/null +++ b/po/boldquot.sed @@ -0,0 +1,10 @@ +s/"\([^"]*\)"/“\1”/g +s/`\([^`']*\)'/‘\1’/g +s/ '\([^`']*\)' / ‘\1’ /g +s/ '\([^`']*\)'$/ ‘\1’/g +s/^'\([^`']*\)' /‘\1’ /g +s/“”/""/g +s/“/“/g +s/”/”/g +s/‘/‘/g +s/’/’/g diff --git a/po/en@boldquot.header b/po/en@boldquot.header new file mode 100644 index 0000000..fedb6a0 --- /dev/null +++ b/po/en@boldquot.header @@ -0,0 +1,25 @@ +# All this catalog "translates" are quotation characters. +# The msgids must be ASCII and therefore cannot contain real quotation +# characters, only substitutes like grave accent (0x60), apostrophe (0x27) +# and double quote (0x22). These substitutes look strange; see +# http://www.cl.cam.ac.uk/~mgk25/ucs/quotes.html +# +# This catalog translates grave accent (0x60) and apostrophe (0x27) to +# left single quotation mark (U+2018) and right single quotation mark (U+2019). +# It also translates pairs of apostrophe (0x27) to +# left single quotation mark (U+2018) and right single quotation mark (U+2019) +# and pairs of quotation mark (0x22) to +# left double quotation mark (U+201C) and right double quotation mark (U+201D). +# +# When output to an UTF-8 terminal, the quotation characters appear perfectly. +# When output to an ISO-8859-1 terminal, the single quotation marks are +# transliterated to apostrophes (by iconv in glibc 2.2 or newer) or to +# grave/acute accent (by libiconv), and the double quotation marks are +# transliterated to 0x22. +# When output to an ASCII terminal, the single quotation marks are +# transliterated to apostrophes, and the double quotation marks are +# transliterated to 0x22. +# +# This catalog furthermore displays the text between the quotation marks in +# bold face, assuming the VT100/XTerm escape sequences. +# diff --git a/po/en@quot.header b/po/en@quot.header new file mode 100644 index 0000000..a9647fc --- /dev/null +++ b/po/en@quot.header @@ -0,0 +1,22 @@ +# All this catalog "translates" are quotation characters. +# The msgids must be ASCII and therefore cannot contain real quotation +# characters, only substitutes like grave accent (0x60), apostrophe (0x27) +# and double quote (0x22). These substitutes look strange; see +# http://www.cl.cam.ac.uk/~mgk25/ucs/quotes.html +# +# This catalog translates grave accent (0x60) and apostrophe (0x27) to +# left single quotation mark (U+2018) and right single quotation mark (U+2019). +# It also translates pairs of apostrophe (0x27) to +# left single quotation mark (U+2018) and right single quotation mark (U+2019) +# and pairs of quotation mark (0x22) to +# left double quotation mark (U+201C) and right double quotation mark (U+201D). +# +# When output to an UTF-8 terminal, the quotation characters appear perfectly. +# When output to an ISO-8859-1 terminal, the single quotation marks are +# transliterated to apostrophes (by iconv in glibc 2.2 or newer) or to +# grave/acute accent (by libiconv), and the double quotation marks are +# transliterated to 0x22. +# When output to an ASCII terminal, the single quotation marks are +# transliterated to apostrophes, and the double quotation marks are +# transliterated to 0x22. +# diff --git a/po/fr.po b/po/fr.po new file mode 100644 index 0000000..feeede7 --- /dev/null +++ b/po/fr.po @@ -0,0 +1,329 @@ +# Oriya translations for psensor package. +# Copyright (C) 2011 Free Software Foundation, Inc. +# This file is distributed under the same license as the psensor package. +# wpitchoune , 2011. +# +msgid "" +msgstr "" +"Project-Id-Version: psensor 0.6.0.9\n" +"Report-Msgid-Bugs-To: wpitchoune@gmail.com\n" +"POT-Creation-Date: 2011-04-03 18:46+0200\n" +"PO-Revision-Date: 2011-02-07 21:18+0100\n" +"Last-Translator: wpitchoune \n" +"Language-Team: Oriya\n" +"Language: or\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8-bit\n" + +#: src/main.c:73 src/server/server.c:80 +#, c-format +msgid "" +"Copyright (C) %s wpitchoune@gmail.com\n" +"License GPLv2: GNU GPL version 2 or later \n" +"This is free software: you are free to change and redistribute it.\n" +"There is NO WARRANTY, to the extent permitted by law.\n" +msgstr "" + +#: src/main.c:83 src/server/server.c:90 +#, c-format +msgid "Usage: %s [OPTION]...\n" +msgstr "" + +#: src/main.c:85 +msgid "" +"psensor is a GTK application for monitoring hardware sensors, including " +"temperatures and fan speeds." +msgstr "" + +#: src/main.c:89 +msgid "Options:" +msgstr "" + +#: src/main.c:90 src/server/server.c:97 +msgid "" +" -h, --help display this help and exit\n" +" -v, --version display version information and exit" +msgstr "" + +#: src/main.c:96 +msgid "" +" -u, --url=URL the URL of the psensor-server, example: http://" +"hostname:3131" +msgstr "" + +#: src/main.c:102 src/server/server.c:110 +#, c-format +msgid "Report bugs to: %s\n" +msgstr "" + +#: src/main.c:104 src/server/server.c:112 +#, c-format +msgid "%s home page: <%s>\n" +msgstr "" + +#: src/main.c:327 src/server/server.c:376 +#, c-format +msgid "Try `%s --help' for more information.\n" +msgstr "" + +#: src/main.c:350 +#, fuzzy, c-format +msgid "ERROR: lmsensor init failure: %s\n" +msgstr "ERREUR: Echec de l'initialisation de 'lm-sensors'\n" + +#: src/main.c:361 +#, c-format +msgid "ERROR: Not compiled with remote sensor support.\n" +msgstr "" + +#: src/ui_sensorlist.c:285 +msgid "Preferences" +msgstr "" + +#: src/ui_sensorlist.c:318 +msgid "Select foreground color" +msgstr "" + +#: src/ui_sensorlist.c:401 +msgid "Sensor" +msgstr "Sonde" + +#: src/ui_sensorlist.c:407 +msgid "Current" +msgstr "Actuelle" + +#: src/ui_sensorlist.c:413 +msgid "Min" +msgstr "" + +#: src/ui_sensorlist.c:419 +msgid "Max" +msgstr "" + +#: src/ui_sensorlist.c:426 +msgid "Color" +msgstr "Couleur" + +#: src/ui_sensorlist.c:439 +msgid "Enabled" +msgstr "Activée" + +#: src/ui_sensorlist.c:468 src/ui_sensorlist.c:469 src/ui_sensorlist.c:470 +msgid "N/A" +msgstr "" + +#: src/ui_notify.c:36 +#, c-format +msgid "ERROR: failed gettimeofday\n" +msgstr "" + +#: src/ui_notify.c:64 +msgid "Temperature alert" +msgstr "" + +#: src/rsensor.c:131 +#, c-format +msgid "ERROR: Fail to connect to: %s\n" +msgstr "" + +#: src/rsensor.c:166 +#, c-format +msgid "ERROR: Invalid content: %s\n" +msgstr "" + +#: src/rsensor.c:204 +#, c-format +msgid "ERROR: Invalid JSON: %s\n" +msgstr "" + +#: src/plib/plib_luatpl.c:121 +#, c-format +msgid "LUATPL Error: failed to load Lua script: %s.\n" +msgstr "" + +#: src/plib/plib_luatpl.c:127 +#, c-format +msgid "LUATPL Error: failed to call init function: %s.\n" +msgstr "" + +#: src/plib/plib_luatpl.c:133 +#, c-format +msgid "LUATPL Error:failed to execute Lua script (%s): %s.\n" +msgstr "" + +#: src/plib/plib_luatpl.c:140 +#, c-format +msgid "LUATPL Error:lua script (%s) returned a wrong type.\n" +msgstr "" + +#: src/plib/plib_luatpl.c:147 +#, c-format +msgid "LUATPL Error:failed to open lua state.\n" +msgstr "" + +#: src/plib/plib_luatpl.c:154 +#, c-format +msgid "LUATPL Error: code: %d.\n" +msgstr "" + +#: src/server/server.c:56 +msgid "" +"

Page not found - Go to Main page

" +msgstr "" + +#: src/server/server.c:92 +msgid "" +"psensor-server is an HTTP server for monitoring hardware sensors remotely." +msgstr "" + +#: src/server/server.c:103 +msgid "" +" -d,--debug run in debug mode\n" +" -p,--port=PORT webserver port\n" +" -w,--wdir=DIR directory containing webserver pages" +msgstr "" + +#: src/server/server.c:184 +msgid "

Server stop requested

" +msgstr "" + +#: src/server/server.c:315 +#, c-format +msgid "HTTP Request: %s\n" +msgstr "" + +#: src/server/server.c:382 +#, c-format +msgid "ERROR: failed to init lm-sensors\n" +msgstr "ERREUR: Echec de l'initialisation de 'lm-sensors'\n" + +#: src/server/server.c:389 +#, c-format +msgid "ERROR: no sensors detected\n" +msgstr "ERREUR: Aucune sonde detectee\n" + +#: src/server/server.c:396 +#, c-format +msgid "ERROR: Fail to create web server\n" +msgstr "ERREUR: Echec de la creation du serveur Web\n" + +#: src/server/server.c:400 +#, c-format +msgid "Web server started on port: %d\n" +msgstr "Server Web demarre sur le port: %d\n" + +#: src/server/server.c:401 +#, c-format +msgid "WWW directory: %s\n" +msgstr "WWW repertoire: %s\n" + +#: src/server/server.c:402 +#, c-format +msgid "URL: http://localhost:%d\n" +msgstr "" + +#: src/lib/hdd.c:59 +#, c-format +msgid "ERROR: hdd_fetch, failed to open socket\n" +msgstr "" + +#: src/lib/hdd.c:73 +#, c-format +msgid "ERROR: hdd_fetch, failed to open connection\n" +msgstr "" + +#: src/lib/hdd.c:182 +#, c-format +msgid "ERROR: wrong hdd string: %s" +msgstr "" + +#: src/lib/hdd.c:252 +#, c-format +msgid "ERROR: wrong hdd string: %s\n" +msgstr "" + +#: src/lib/nvidia.c:53 +#, c-format +msgid "ERROR: failed to retrieve nvidia temperature\n" +msgstr "" + +#: src/lib/nvidia.c:85 +#, c-format +msgid "ERROR: nvidia initialization failure\n" +msgstr "" + +#: src/lib/nvidia.c:99 +#, c-format +msgid "ERROR: nvidia initialization failure: %d\n" +msgstr "" + +#: src/lib/nvidia.c:132 +#, c-format +msgid "ERROR: no nvidia chips or initialization failure\n" +msgstr "" + +#: src/lib/lmsensor.c:43 +#, c-format +msgid "ERROR: Can't get value of subfeature %s: %s\n" +msgstr "" + +#: src/lib/lmsensor.c:122 +#, c-format +msgid "ERROR: create_sensor, wrong feature type\n" +msgstr "" + +#: src/lib/lmsensor.c:166 +#, c-format +msgid "ERROR: lm-sensors initialization failure: %s\n" +msgstr "" + +#~ msgid "Graph Colors" +#~ msgstr "Couleurs du graphe" + +#~ msgid "Background:" +#~ msgstr "Fond:" + +#~ msgid "Background opacity:" +#~ msgstr "Opacite du fond:" + +#~ msgid "Position of sensors table:" +#~ msgstr "Position de la table des sondes:" + +#~ msgid "Right" +#~ msgstr "Droite" + +#~ msgid "Left" +#~ msgstr "Gauche" + +#~ msgid "Top" +#~ msgstr "Haut" + +#~ msgid "Bottom" +#~ msgstr "Bas" + +#~ msgid "Name:" +#~ msgstr "Nom:" + +#~ msgid "Draw sensor curve" +#~ msgstr "Dessiner la courbe de la sonde" + +#, fuzzy +#~ msgid "Color:" +#~ msgstr "Couleur" + +#~ msgid "Alarm" +#~ msgstr "Alarme" + +#~ msgid "Activate desktop notifications" +#~ msgstr "Activer les notifications du bureau" + +#~ msgid "Show" +#~ msgstr "Montrer" + +#~ msgid "ERROR: Lua support not enabled\n" +#~ msgstr "ERREUR: Le support de Lua n'est pas active\n" + +#~ msgid "Psensor - Temperature Monitor" +#~ msgstr "Psensor - Surveillance Des Temperatures" diff --git a/po/insert-header.sin b/po/insert-header.sin new file mode 100644 index 0000000..b26de01 --- /dev/null +++ b/po/insert-header.sin @@ -0,0 +1,23 @@ +# Sed script that inserts the file called HEADER before the header entry. +# +# At each occurrence of a line starting with "msgid ", we execute the following +# commands. At the first occurrence, insert the file. At the following +# occurrences, do nothing. The distinction between the first and the following +# occurrences is achieved by looking at the hold space. +/^msgid /{ +x +# Test if the hold space is empty. +s/m/m/ +ta +# Yes it was empty. First occurrence. Read the file. +r HEADER +# Output the file's contents by reading the next line. But don't lose the +# current line while doing this. +g +N +bb +:a +# The hold space was nonempty. Following occurrences. Do nothing. +x +:b +} diff --git a/po/psensor.pot b/po/psensor.pot new file mode 100644 index 0000000..6c7f32a --- /dev/null +++ b/po/psensor.pot @@ -0,0 +1,281 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Free Software Foundation, Inc. +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: wpitchoune@gmail.com\n" +"POT-Creation-Date: 2011-04-03 18:46+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: src/main.c:73 src/server/server.c:80 +#, c-format +msgid "" +"Copyright (C) %s wpitchoune@gmail.com\n" +"License GPLv2: GNU GPL version 2 or later \n" +"This is free software: you are free to change and redistribute it.\n" +"There is NO WARRANTY, to the extent permitted by law.\n" +msgstr "" + +#: src/main.c:83 src/server/server.c:90 +#, c-format +msgid "Usage: %s [OPTION]...\n" +msgstr "" + +#: src/main.c:85 +msgid "" +"psensor is a GTK application for monitoring hardware sensors, including " +"temperatures and fan speeds." +msgstr "" + +#: src/main.c:89 +msgid "Options:" +msgstr "" + +#: src/main.c:90 src/server/server.c:97 +msgid "" +" -h, --help display this help and exit\n" +" -v, --version display version information and exit" +msgstr "" + +#: src/main.c:96 +msgid "" +" -u, --url=URL the URL of the psensor-server, example: http://" +"hostname:3131" +msgstr "" + +#: src/main.c:102 src/server/server.c:110 +#, c-format +msgid "Report bugs to: %s\n" +msgstr "" + +#: src/main.c:104 src/server/server.c:112 +#, c-format +msgid "%s home page: <%s>\n" +msgstr "" + +#: src/main.c:327 src/server/server.c:376 +#, c-format +msgid "Try `%s --help' for more information.\n" +msgstr "" + +#: src/main.c:350 +#, c-format +msgid "ERROR: lmsensor init failure: %s\n" +msgstr "" + +#: src/main.c:361 +#, c-format +msgid "ERROR: Not compiled with remote sensor support.\n" +msgstr "" + +#: src/ui_sensorlist.c:285 +msgid "Preferences" +msgstr "" + +#: src/ui_sensorlist.c:318 +msgid "Select foreground color" +msgstr "" + +#: src/ui_sensorlist.c:401 +msgid "Sensor" +msgstr "" + +#: src/ui_sensorlist.c:407 +msgid "Current" +msgstr "" + +#: src/ui_sensorlist.c:413 +msgid "Min" +msgstr "" + +#: src/ui_sensorlist.c:419 +msgid "Max" +msgstr "" + +#: src/ui_sensorlist.c:426 +msgid "Color" +msgstr "" + +#: src/ui_sensorlist.c:439 +msgid "Enabled" +msgstr "" + +#: src/ui_sensorlist.c:468 src/ui_sensorlist.c:469 src/ui_sensorlist.c:470 +msgid "N/A" +msgstr "" + +#: src/ui_notify.c:36 +#, c-format +msgid "ERROR: failed gettimeofday\n" +msgstr "" + +#: src/ui_notify.c:64 +msgid "Temperature alert" +msgstr "" + +#: src/rsensor.c:131 +#, c-format +msgid "ERROR: Fail to connect to: %s\n" +msgstr "" + +#: src/rsensor.c:166 +#, c-format +msgid "ERROR: Invalid content: %s\n" +msgstr "" + +#: src/rsensor.c:204 +#, c-format +msgid "ERROR: Invalid JSON: %s\n" +msgstr "" + +#: src/plib/plib_luatpl.c:121 +#, c-format +msgid "LUATPL Error: failed to load Lua script: %s.\n" +msgstr "" + +#: src/plib/plib_luatpl.c:127 +#, c-format +msgid "LUATPL Error: failed to call init function: %s.\n" +msgstr "" + +#: src/plib/plib_luatpl.c:133 +#, c-format +msgid "LUATPL Error:failed to execute Lua script (%s): %s.\n" +msgstr "" + +#: src/plib/plib_luatpl.c:140 +#, c-format +msgid "LUATPL Error:lua script (%s) returned a wrong type.\n" +msgstr "" + +#: src/plib/plib_luatpl.c:147 +#, c-format +msgid "LUATPL Error:failed to open lua state.\n" +msgstr "" + +#: src/plib/plib_luatpl.c:154 +#, c-format +msgid "LUATPL Error: code: %d.\n" +msgstr "" + +#: src/server/server.c:56 +msgid "" +"

Page not found - Go to Main page

" +msgstr "" + +#: src/server/server.c:92 +msgid "" +"psensor-server is an HTTP server for monitoring hardware sensors remotely." +msgstr "" + +#: src/server/server.c:103 +msgid "" +" -d,--debug run in debug mode\n" +" -p,--port=PORT webserver port\n" +" -w,--wdir=DIR directory containing webserver pages" +msgstr "" + +#: src/server/server.c:184 +msgid "

Server stop requested

" +msgstr "" + +#: src/server/server.c:315 +#, c-format +msgid "HTTP Request: %s\n" +msgstr "" + +#: src/server/server.c:382 +#, c-format +msgid "ERROR: failed to init lm-sensors\n" +msgstr "" + +#: src/server/server.c:389 +#, c-format +msgid "ERROR: no sensors detected\n" +msgstr "" + +#: src/server/server.c:396 +#, c-format +msgid "ERROR: Fail to create web server\n" +msgstr "" + +#: src/server/server.c:400 +#, c-format +msgid "Web server started on port: %d\n" +msgstr "" + +#: src/server/server.c:401 +#, c-format +msgid "WWW directory: %s\n" +msgstr "" + +#: src/server/server.c:402 +#, c-format +msgid "URL: http://localhost:%d\n" +msgstr "" + +#: src/lib/hdd.c:59 +#, c-format +msgid "ERROR: hdd_fetch, failed to open socket\n" +msgstr "" + +#: src/lib/hdd.c:73 +#, c-format +msgid "ERROR: hdd_fetch, failed to open connection\n" +msgstr "" + +#: src/lib/hdd.c:182 +#, c-format +msgid "ERROR: wrong hdd string: %s" +msgstr "" + +#: src/lib/hdd.c:252 +#, c-format +msgid "ERROR: wrong hdd string: %s\n" +msgstr "" + +#: src/lib/nvidia.c:53 +#, c-format +msgid "ERROR: failed to retrieve nvidia temperature\n" +msgstr "" + +#: src/lib/nvidia.c:85 +#, c-format +msgid "ERROR: nvidia initialization failure\n" +msgstr "" + +#: src/lib/nvidia.c:99 +#, c-format +msgid "ERROR: nvidia initialization failure: %d\n" +msgstr "" + +#: src/lib/nvidia.c:132 +#, c-format +msgid "ERROR: no nvidia chips or initialization failure\n" +msgstr "" + +#: src/lib/lmsensor.c:43 +#, c-format +msgid "ERROR: Can't get value of subfeature %s: %s\n" +msgstr "" + +#: src/lib/lmsensor.c:122 +#, c-format +msgid "ERROR: create_sensor, wrong feature type\n" +msgstr "" + +#: src/lib/lmsensor.c:166 +#, c-format +msgid "ERROR: lm-sensors initialization failure: %s\n" +msgstr "" diff --git a/po/quot.sed b/po/quot.sed new file mode 100644 index 0000000..0122c46 --- /dev/null +++ b/po/quot.sed @@ -0,0 +1,6 @@ +s/"\([^"]*\)"/“\1”/g +s/`\([^`']*\)'/‘\1’/g +s/ '\([^`']*\)' / ‘\1’ /g +s/ '\([^`']*\)'$/ ‘\1’/g +s/^'\([^`']*\)' /‘\1’ /g +s/“”/""/g diff --git a/po/remove-potcdate.sin b/po/remove-potcdate.sin new file mode 100644 index 0000000..2436c49 --- /dev/null +++ b/po/remove-potcdate.sin @@ -0,0 +1,19 @@ +# Sed script that remove the POT-Creation-Date line in the header entry +# from a POT file. +# +# The distinction between the first and the following occurrences of the +# pattern is achieved by looking at the hold space. +/^"POT-Creation-Date: .*"$/{ +x +# Test if the hold space is empty. +s/P/P/ +ta +# Yes it was empty. First occurrence. Remove the line. +g +d +bb +:a +# The hold space was nonempty. Following occurrences. Do nothing. +x +:b +} diff --git a/po/zh_CN.po b/po/zh_CN.po new file mode 100644 index 0000000..a85f583 --- /dev/null +++ b/po/zh_CN.po @@ -0,0 +1,366 @@ +# Chinese translations for psensor package. +# Copyright (C) 2011 Free Software Foundation, Inc. +# This file is distributed under the same license as the psensor package. +# wpitchoune , 2011. +# Chinese translation from 大宝 +msgid "" +msgstr "" +"Project-Id-Version: psensor 0.6.0.10\n" +"Report-Msgid-Bugs-To: wpitchoune@gmail.com\n" +"POT-Creation-Date: 2011-04-03 18:46+0200\n" +"PO-Revision-Date: 2011-02-07 22:21+0100\n" +"Last-Translator: 大宝 \n" +"Language-Team: Chinese (simplified)\n" +"Language: zh_CN\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: src/main.c:73 src/server/server.c:80 +#, c-format +msgid "" +"Copyright (C) %s wpitchoune@gmail.com\n" +"License GPLv2: GNU GPL version 2 or later \n" +"This is free software: you are free to change and redistribute it.\n" +"There is NO WARRANTY, to the extent permitted by law.\n" +msgstr "" + +#: src/main.c:83 src/server/server.c:90 +#, c-format +msgid "Usage: %s [OPTION]...\n" +msgstr "" + +#: src/main.c:85 +msgid "" +"psensor is a GTK application for monitoring hardware sensors, including " +"temperatures and fan speeds." +msgstr "" + +#: src/main.c:89 +msgid "Options:" +msgstr "" + +#: src/main.c:90 src/server/server.c:97 +msgid "" +" -h, --help display this help and exit\n" +" -v, --version display version information and exit" +msgstr "" + +#: src/main.c:96 +msgid "" +" -u, --url=URL the URL of the psensor-server, example: http://" +"hostname:3131" +msgstr "" + +#: src/main.c:102 src/server/server.c:110 +#, c-format +msgid "Report bugs to: %s\n" +msgstr "" + +#: src/main.c:104 src/server/server.c:112 +#, c-format +msgid "%s home page: <%s>\n" +msgstr "" + +#: src/main.c:327 src/server/server.c:376 +#, c-format +msgid "Try `%s --help' for more information.\n" +msgstr "" + +#: src/main.c:350 +#, c-format +msgid "ERROR: lmsensor init failure: %s\n" +msgstr "" + +#: src/main.c:361 +#, c-format +msgid "ERROR: Not compiled with remote sensor support.\n" +msgstr "" + +#: src/ui_sensorlist.c:285 +msgid "Preferences" +msgstr "选项" + +#: src/ui_sensorlist.c:318 +msgid "Select foreground color" +msgstr "" + +#: src/ui_sensorlist.c:401 +msgid "Sensor" +msgstr "监视设备" + +#: src/ui_sensorlist.c:407 +msgid "Current" +msgstr "当前" + +#: src/ui_sensorlist.c:413 +msgid "Min" +msgstr "最小值" + +#: src/ui_sensorlist.c:419 +msgid "Max" +msgstr "最大值" + +#: src/ui_sensorlist.c:426 +msgid "Color" +msgstr "颜色" + +#: src/ui_sensorlist.c:439 +msgid "Enabled" +msgstr "激活" + +#: src/ui_sensorlist.c:468 src/ui_sensorlist.c:469 src/ui_sensorlist.c:470 +msgid "N/A" +msgstr "" + +#: src/ui_notify.c:36 +#, c-format +msgid "ERROR: failed gettimeofday\n" +msgstr "" + +#: src/ui_notify.c:64 +msgid "Temperature alert" +msgstr "" + +#: src/rsensor.c:131 +#, c-format +msgid "ERROR: Fail to connect to: %s\n" +msgstr "" + +#: src/rsensor.c:166 +#, c-format +msgid "ERROR: Invalid content: %s\n" +msgstr "" + +#: src/rsensor.c:204 +#, c-format +msgid "ERROR: Invalid JSON: %s\n" +msgstr "" + +#: src/plib/plib_luatpl.c:121 +#, c-format +msgid "LUATPL Error: failed to load Lua script: %s.\n" +msgstr "" + +#: src/plib/plib_luatpl.c:127 +#, c-format +msgid "LUATPL Error: failed to call init function: %s.\n" +msgstr "" + +#: src/plib/plib_luatpl.c:133 +#, c-format +msgid "LUATPL Error:failed to execute Lua script (%s): %s.\n" +msgstr "" + +#: src/plib/plib_luatpl.c:140 +#, c-format +msgid "LUATPL Error:lua script (%s) returned a wrong type.\n" +msgstr "" + +#: src/plib/plib_luatpl.c:147 +#, c-format +msgid "LUATPL Error:failed to open lua state.\n" +msgstr "" + +#: src/plib/plib_luatpl.c:154 +#, c-format +msgid "LUATPL Error: code: %d.\n" +msgstr "" + +#: src/server/server.c:56 +msgid "" +"

Page not found - Go to Main page

" +msgstr "" + +#: src/server/server.c:92 +msgid "" +"psensor-server is an HTTP server for monitoring hardware sensors remotely." +msgstr "" + +#: src/server/server.c:103 +msgid "" +" -d,--debug run in debug mode\n" +" -p,--port=PORT webserver port\n" +" -w,--wdir=DIR directory containing webserver pages" +msgstr "" + +#: src/server/server.c:184 +msgid "

Server stop requested

" +msgstr "" + +#: src/server/server.c:315 +#, c-format +msgid "HTTP Request: %s\n" +msgstr "" + +#: src/server/server.c:382 +#, c-format +msgid "ERROR: failed to init lm-sensors\n" +msgstr "" + +#: src/server/server.c:389 +#, c-format +msgid "ERROR: no sensors detected\n" +msgstr "" + +#: src/server/server.c:396 +#, c-format +msgid "ERROR: Fail to create web server\n" +msgstr "" + +#: src/server/server.c:400 +#, c-format +msgid "Web server started on port: %d\n" +msgstr "" + +#: src/server/server.c:401 +#, c-format +msgid "WWW directory: %s\n" +msgstr "" + +#: src/server/server.c:402 +#, c-format +msgid "URL: http://localhost:%d\n" +msgstr "" + +#: src/lib/hdd.c:59 +#, c-format +msgid "ERROR: hdd_fetch, failed to open socket\n" +msgstr "" + +#: src/lib/hdd.c:73 +#, c-format +msgid "ERROR: hdd_fetch, failed to open connection\n" +msgstr "" + +#: src/lib/hdd.c:182 +#, c-format +msgid "ERROR: wrong hdd string: %s" +msgstr "" + +#: src/lib/hdd.c:252 +#, c-format +msgid "ERROR: wrong hdd string: %s\n" +msgstr "" + +#: src/lib/nvidia.c:53 +#, c-format +msgid "ERROR: failed to retrieve nvidia temperature\n" +msgstr "" + +#: src/lib/nvidia.c:85 +#, c-format +msgid "ERROR: nvidia initialization failure\n" +msgstr "" + +#: src/lib/nvidia.c:99 +#, c-format +msgid "ERROR: nvidia initialization failure: %d\n" +msgstr "" + +#: src/lib/nvidia.c:132 +#, c-format +msgid "ERROR: no nvidia chips or initialization failure\n" +msgstr "" + +#: src/lib/lmsensor.c:43 +#, c-format +msgid "ERROR: Can't get value of subfeature %s: %s\n" +msgstr "" + +#: src/lib/lmsensor.c:122 +#, c-format +msgid "ERROR: create_sensor, wrong feature type\n" +msgstr "" + +#: src/lib/lmsensor.c:166 +#, c-format +msgid "ERROR: lm-sensors initialization failure: %s\n" +msgstr "" + +#~ msgid "Graph Colors" +#~ msgstr "图表颜色" + +#~ msgid "Foreground:" +#~ msgstr "坐标栏:" + +#~ msgid "Background:" +#~ msgstr "背景:" + +#~ msgid "Background opacity:" +#~ msgstr "背景亮度:" + +#~ msgid "Min" +#~ msgstr "最小值" + +#~ msgid "Max" +#~ msgstr "最大值" + +#~ msgid "Graph" +#~ msgstr "图表" + +#~ msgid "Interface" +#~ msgstr "界面" + +#~ msgid "Position of sensors table:" +#~ msgstr "监视设备列表位置:" + +#~ msgid "Right" +#~ msgstr "右侧" + +#~ msgid "Left" +#~ msgstr "左侧" + +#~ msgid "Top" +#~ msgstr "顶部" + +#~ msgid "Bottom" +#~ msgstr "底部" + +#~ msgid "Hide window decoration" +#~ msgstr "隐藏窗口标题栏" + +#~ msgid "Keep window below" +#~ msgstr "保持在最底层" + +#, fuzzy +#~ msgid "Edit Preferences" +#~ msgstr "监视设备选项" + +#~ msgid "Edit Sensor Preferences" +#~ msgstr "监视设备选项" + +#~ msgid "Sensor Information" +#~ msgstr "设备信息" + +#~ msgid "Id:" +#~ msgstr "标识:" + +#~ msgid "Type:" +#~ msgstr "类型:" + +#~ msgid "Name:" +#~ msgstr "名称:" + +#~ msgid "Draw sensor curve" +#~ msgstr "绘制监视曲线" + +#, fuzzy +#~ msgid "Color:" +#~ msgstr "颜色:" + +#~ msgid "Alarm" +#~ msgstr "警告信息" + +#~ msgid "Activate desktop notifications" +#~ msgstr "激活桌面气泡提示" + +#~ msgid "Temperature limit:" +#~ msgstr "温度警戒线:" + +#~ msgid "Show" +#~ msgstr "显示主窗口" + +#~ msgid "Quit" +#~ msgstr "退出" diff --git a/psensor.desktop b/psensor.desktop new file mode 100644 index 0000000..ecc02db --- /dev/null +++ b/psensor.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Type=Application +Version=1.0 +Name=Psensor +GenericName=Psensor +Comment=Psensor is a graphical temperature monitor for Linux +Icon=psensor +TryExec=psensor +Exec=psensor +Categories=Application;System; diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..e567c83 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,81 @@ +SUBDIRS = plib lib unity glade + +AM_LDFLAGS = -Wl,--as-needed + +if JSON +SUBDIRS += libpsensor_json +endif + +if LIBMICROHTTPD +if JSON +if LUA +SUBDIRS += server +endif +endif +endif + +AM_CPPFLAGS = -Wall -pedantic -Werror -DDEFAULT_WWW_DIR=\""$(pkgdatadir)/www"\"\ + -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/unity \ + $(GTK_CFLAGS)\ + $(GCONF_CFLAGS)\ + $(SENSORS_CFLAGS) + +DEFS = -DPACKAGE_DATA_DIR=\"$(pkgdatadir)\" -DLOCALEDIR=\"$(localedir)\" @DEFS@ + +LIBS = \ + plib/libplib.a \ + lib/libpsensor.a \ + $(GTK_LIBS)\ + $(GCONF_LIBS)\ + $(SENSORS_LIBS) + +bin_PROGRAMS = psensor +psensor_SOURCES = \ + compat.h \ + cfg.h cfg.c \ + graph.h graph.c \ + main.c \ + ui.h ui.c \ + ui_color.h ui_color.c \ + ui_graph.h ui_graph.c \ + ui_pref.h ui_pref.c \ + ui_sensorlist.h ui_sensorlist.c + +if LIBNOTIFY +psensor_SOURCES += ui_notify.h ui_notify.c +LIBS += $(LIBNOTIFY_LIBS) +AM_CPPFLAGS += $(LIBNOTIFY_CFLAGS) +endif + +if APPINDICATOR +psensor_SOURCES += ui_appindicator.h ui_appindicator.c +LIBS += $(APPINDICATOR_LIBS) +AM_CPPFLAGS += $(APPINDICATOR_CFLAGS) +endif + +if NVIDIA +AM_CPPFLAGS += $(NVIDIA_CFLAGS) +LIBS += $(NVIDIA_LIBS) +endif + +if UNITY +AM_CPPFLAGS += $(UNITY_CFLAGS) +LIBS += unity/libpsensor_unity.a $(UNITY_LIBS) +endif + +if CURL +if JSON +psensor_SOURCES += rsensor.h rsensor.c plib/url.c plib/url.h +LIBS += $(CURL_LIBS) $(JSON_LIBS) +AM_CPPFLAGS += $(CURL_CFLAGS) $(JSON_CFLAGS) +endif +endif + +dist_man_MANS = psensor.1 + + +psensor.1: main.c $(top_srcdir)/configure.ac + $(MAKE) $(AM_MAKEFLAGS) psensor$(EXEEXT) + help2man --include=description.txt -N --name="Temperature monitoring application" --output=psensor.1 ./psensor$(EXEEXT) + diff --git a/src/cfg.c b/src/cfg.c new file mode 100644 index 0000000..24dedf0 --- /dev/null +++ b/src/cfg.c @@ -0,0 +1,456 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include +#include +#include +#include + +#include + +#include "cfg.h" + +#define KEY_SENSOR_UPDATE_INTERVAL "/apps/psensor/sensor/update_interval" + +#define KEY_GRAPH_UPDATE_INTERVAL "/apps/psensor/graph/update_interval" +#define KEY_GRAPH_MONITORING_DURATION "/apps/psensor/graph/monitoring_duration" + +#define KEY_GRAPH_BACKGROUND_COLOR "/apps/psensor/graph/background_color" +#define DEFAULT_GRAPH_BACKGROUND_COLOR "#e8f4e8f4a8f5" + +#define KEY_GRAPH_BACKGROUND_ALPHA "/apps/psensor/graph/background_alpha" +#define DEFAULT_GRAPH_BACKGROUND_ALPHA "1.0" + +#define KEY_GRAPH_FOREGROUND_COLOR "/apps/psensor/graph/foreground_color" +#define DEFAULT_GRAPH_FOREGROUND_COLOR "#000000000000" + +#define KEY_ALPHA_CHANNEL_ENABLED "/apps/psensor/graph/alpha_channel_enabled" +#define DEFAULT_ALPHA_CHANNEL_ENABLED 0 + +#define KEY_INTERFACE_SENSORLIST_POSITION \ +"/apps/psensor/interface/sensorlist_position" + +#define KEY_INTERFACE_WINDOW_DECORATION_DISABLED \ +"/apps/psensor/interface/window_decoration_disabled" + +#define KEY_INTERFACE_WINDOW_KEEP_BELOW_ENABLED \ +"/apps/psensor/interface/window_keep_below_enabled" + +GConfClient *client; + +char *config_get_string(char *key, char *default_value) +{ + char *value = gconf_client_get_string(client, + key, + NULL); + + if (!value) { + value = strdup(default_value); + + gconf_client_set_string(client, key, default_value, NULL); + } + + return value; +} + +struct color *config_get_background_color() +{ + + char *scolor = config_get_string(KEY_GRAPH_BACKGROUND_COLOR, + DEFAULT_GRAPH_BACKGROUND_COLOR); + + struct color *c = string_to_color(scolor); + + free(scolor); + + if (c == NULL) + return color_new(0xffff, 0xffff, 0xffff); + + return c; +} + +struct color *config_get_foreground_color() +{ + char *scolor = config_get_string(KEY_GRAPH_FOREGROUND_COLOR, + DEFAULT_GRAPH_FOREGROUND_COLOR); + + struct color *c = string_to_color(scolor); + + free(scolor); + + if (c == NULL) + return color_new(0x0000, 0x0000, 0x0000); + + return c; +} + +int config_is_alpha_channel_enabled() +{ + gboolean b = gconf_client_get_bool(client, + KEY_ALPHA_CHANNEL_ENABLED, + NULL); + + return b == TRUE; +} + +void config_set_alpha_channel_enabled(int enabled) +{ + if (enabled) + gconf_client_set_bool(client, + KEY_ALPHA_CHANNEL_ENABLED, TRUE, NULL); + else + gconf_client_set_bool(client, + KEY_ALPHA_CHANNEL_ENABLED, FALSE, NULL); +} + +int config_get_sensorlist_position() +{ + return gconf_client_get_int(client, + KEY_INTERFACE_SENSORLIST_POSITION, NULL); +} + +void config_set_sensorlist_position(int pos) +{ + gconf_client_set_int(client, + KEY_INTERFACE_SENSORLIST_POSITION, pos, NULL); +} + +double config_get_graph_background_alpha() +{ + double a = gconf_client_get_float(client, + KEY_GRAPH_BACKGROUND_ALPHA, + NULL); + + if (a == 0) + gconf_client_set_float(client, + KEY_GRAPH_BACKGROUND_ALPHA, 1.0, NULL); + return a; +} + +void config_set_graph_background_alpha(double alpha) +{ + gconf_client_set_float(client, KEY_GRAPH_BACKGROUND_ALPHA, alpha, NULL); +} + +void config_set_background_color(struct color *color) +{ + char *scolor = color_to_string(color); + + if (!scolor) + scolor = strdup(DEFAULT_GRAPH_BACKGROUND_COLOR); + + gconf_client_set_string(client, + KEY_GRAPH_BACKGROUND_COLOR, scolor, NULL); + + free(scolor); +} + +void config_set_foreground_color(struct color *color) +{ + char *scolor = color_to_string(color); + + if (!scolor) + scolor = strdup(DEFAULT_GRAPH_FOREGROUND_COLOR); + + gconf_client_set_string(client, + KEY_GRAPH_FOREGROUND_COLOR, scolor, NULL); + + free(scolor); +} + +char *config_get_sensor_key(char *sensor_name) +{ + char *escaped_name = gconf_escape_key(sensor_name, -1); + /* /apps/psensor/sensors/[sensor_name]/color */ + char *key = malloc(22 + 2 * strlen(escaped_name) + 6 + 1); + + sprintf(key, "/apps/psensor/sensors/%s/color", escaped_name); + + free(escaped_name); + + return key; +} + +struct color *config_get_sensor_color(char *sensor_name, + struct color *default_color) +{ + char *key = config_get_sensor_key(sensor_name); + + char *scolor = gconf_client_get_string(client, + key, + NULL); + + struct color *color = NULL; + + if (scolor) + color = string_to_color(scolor); + + if (!scolor || !color) { + color = color_new(default_color->red, + default_color->green, default_color->blue); + + scolor = color_to_string(color); + + gconf_client_set_string(client, key, scolor, NULL); + } + + free(scolor); + free(key); + + return color; +} + +void config_set_sensor_color(char *sensor_name, struct color *color) +{ + char *key = config_get_sensor_key(sensor_name); + + char *scolor = color_to_string(color); + + gconf_client_set_string(client, key, scolor, NULL); + + free(scolor); +} + +int config_get_sensor_alarm_limit(char *sensor_name, int def) +{ + int res; + char *escaped_name = gconf_escape_key(sensor_name, -1); + /* /apps/psensor/sensors/[sensor_name]/alarmlimit */ + char *key = malloc(22 + 2 * strlen(escaped_name) + 1 + 10 + 1); + + sprintf(key, "/apps/psensor/sensors/%s/alarmlimit", escaped_name); + + res = gconf_client_get_int(client, key, NULL); + + free(escaped_name); + + return res ? res : def; +} + +void config_set_sensor_alarm_limit(char *sensor_name, int alarm_limit) +{ + char *escaped_name = gconf_escape_key(sensor_name, -1); + /* /apps/psensor/sensors/[sensor_name]/alarmlimit */ + char *key = malloc(22 + 2 * strlen(escaped_name) + 1 + 10 + 1); + + sprintf(key, "/apps/psensor/sensors/%s/alarmlimit", escaped_name); + + gconf_client_set_int(client, key, alarm_limit, NULL); + + free(escaped_name); +} + +int config_get_sensor_alarm_enabled(char *sid) +{ + gboolean res; + char *escaped_name = gconf_escape_key(sid, -1); + /* /apps/psensor/sensors/[sensor_name]/alarmenabled */ + char *key = malloc(22 + 2 * strlen(escaped_name) + 1 + 12 + 1); + + sprintf(key, "/apps/psensor/sensors/%s/alarmenabled", escaped_name); + + res = gconf_client_get_bool(client, key, NULL); + + free(escaped_name); + + return res == TRUE; +} + +void config_set_sensor_alarm_enabled(char *sid, int enabled) +{ + char *escaped_name = gconf_escape_key(sid, -1); + /* /apps/psensor/sensors/[sensor_name]/alarmenabled */ + char *key = malloc(22 + 2 * strlen(escaped_name) + 1 + 12 + 1); + + sprintf(key, "/apps/psensor/sensors/%s/alarmenabled", escaped_name); + + gconf_client_set_bool(client, key, enabled, NULL); + + free(escaped_name); +} + +int config_is_sensor_enabled(char *sid) +{ + gboolean res; + char *escaped_name = gconf_escape_key(sid, -1); + /* /apps/psensor/sensors/[sensor_name]/enabled */ + char *key = malloc(22 + 2 * strlen(escaped_name) + 1 + 7 + 1); + + sprintf(key, "/apps/psensor/sensors/%s/enabled", escaped_name); + + res = gconf_client_get_bool(client, key, NULL); + + free(escaped_name); + + return res == TRUE; + +} + +void config_set_sensor_enabled(char *sid, int enabled) +{ + char *escaped_name = gconf_escape_key(sid, -1); + /* /apps/psensor/sensors/[sensor_name]/enabled */ + char *key = malloc(22 + 2 * strlen(escaped_name) + 1 + 7 + 1); + + sprintf(key, "/apps/psensor/sensors/%s/enabled", escaped_name); + + gconf_client_set_bool(client, key, enabled, NULL); + + free(escaped_name); +} + +char *config_get_sensor_name(char *sid) +{ + char *res; + char *escaped_name = gconf_escape_key(sid, -1); + /* /apps/psensor/sensors/[sensor_name]/name */ + char *key = malloc(22 + 2 * strlen(escaped_name) + 1 + 4 + 1); + + sprintf(key, "/apps/psensor/sensors/%s/name", escaped_name); + + res = gconf_client_get_string(client, key, NULL); + + free(escaped_name); + + return res; +} + +void config_set_sensor_name(char *sid, const char *name) +{ + char *escaped_name = gconf_escape_key(sid, -1); + /* /apps/psensor/sensors/[sensor_name]/name */ + char *key = malloc(22 + 2 * strlen(escaped_name) + 1 + 4 + 1); + + sprintf(key, "/apps/psensor/sensors/%s/name", escaped_name); + + gconf_client_set_string(client, key, name, NULL); + + free(escaped_name); +} + +int config_is_window_decoration_enabled() +{ + gboolean b; + + b = gconf_client_get_bool(client, + KEY_INTERFACE_WINDOW_DECORATION_DISABLED, + NULL); + + return b == FALSE; +} + +int config_is_window_keep_below_enabled() +{ + gboolean b; + + b = gconf_client_get_bool(client, + KEY_INTERFACE_WINDOW_KEEP_BELOW_ENABLED, + NULL); + + return b == TRUE; +} + +void config_set_window_decoration_enabled(int enabled) +{ + if (enabled) + gconf_client_set_bool + (client, + KEY_INTERFACE_WINDOW_DECORATION_DISABLED, FALSE, NULL); + else + gconf_client_set_bool + (client, + KEY_INTERFACE_WINDOW_DECORATION_DISABLED, TRUE, NULL); +} + +void config_set_window_keep_below_enabled(int enabled) +{ + if (enabled) + gconf_client_set_bool(client, + KEY_INTERFACE_WINDOW_KEEP_BELOW_ENABLED, + TRUE, NULL); + else + gconf_client_set_bool(client, + KEY_INTERFACE_WINDOW_KEEP_BELOW_ENABLED, + FALSE, NULL); +} + +void config_init() +{ + client = gconf_client_get_default(); +} + +struct config *config_load() +{ + struct config *cfg = malloc(sizeof(struct config)); + + cfg->graph_bgcolor = config_get_background_color(); + cfg->graph_fgcolor = config_get_foreground_color(); + cfg->graph_bg_alpha = config_get_graph_background_alpha(); + cfg->alpha_channel_enabled = config_is_alpha_channel_enabled(); + cfg->sensorlist_position = config_get_sensorlist_position(); + cfg->window_decoration_enabled = config_is_window_decoration_enabled(); + cfg->window_keep_below_enabled = config_is_window_keep_below_enabled(); + + cfg->sensor_update_interval + = gconf_client_get_int(client, KEY_SENSOR_UPDATE_INTERVAL, NULL); + if (cfg->sensor_update_interval < 1) + cfg->sensor_update_interval = 1; + + cfg->graph_update_interval + = gconf_client_get_int(client, KEY_GRAPH_UPDATE_INTERVAL, NULL); + if (cfg->graph_update_interval < 1) + cfg->graph_update_interval = 1; + + cfg->graph_monitoring_duration + = gconf_client_get_int(client, KEY_GRAPH_MONITORING_DURATION, NULL); + + if (cfg->graph_monitoring_duration < 1) + cfg->graph_monitoring_duration = 10; + + cfg->sensor_values_max_length + = + (cfg->graph_monitoring_duration * 60) / cfg->sensor_update_interval; + if (cfg->sensor_values_max_length < 3) + cfg->sensor_values_max_length = 3; + + return cfg; +} + +void config_save(struct config *cfg) +{ + config_set_background_color(cfg->graph_bgcolor); + config_set_foreground_color(cfg->graph_fgcolor); + config_set_graph_background_alpha(cfg->graph_bg_alpha); + config_set_sensorlist_position(cfg->sensorlist_position); + config_set_window_decoration_enabled(cfg->window_decoration_enabled); + config_set_window_keep_below_enabled(cfg->window_keep_below_enabled); + + gconf_client_set_int(client, + KEY_GRAPH_UPDATE_INTERVAL, + cfg->graph_update_interval, NULL); + + gconf_client_set_int(client, + KEY_GRAPH_MONITORING_DURATION, + cfg->graph_monitoring_duration, NULL); + + gconf_client_set_int(client, + KEY_SENSOR_UPDATE_INTERVAL, + cfg->sensor_update_interval, NULL); + +} diff --git a/src/cfg.h b/src/cfg.h new file mode 100644 index 0000000..f6c46e3 --- /dev/null +++ b/src/cfg.h @@ -0,0 +1,80 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#ifndef _PSENSOR_CONFIG_H_ +#define _PSENSOR_CONFIG_H_ + +#include "color.h" + +#define SENSORLIST_POSITION_RIGHT 0 +#define SENSORLIST_POSITION_LEFT 1 +#define SENSORLIST_POSITION_TOP 2 +#define SENSORLIST_POSITION_BOTTOM 3 + +struct config { + struct color *graph_bgcolor; + struct color *graph_fgcolor; + + double graph_bg_alpha; + + int alpha_channel_enabled; + + /* + Position of the sensors list table + */ + int sensorlist_position; + + int window_decoration_enabled; + + int window_keep_below_enabled; + + int graph_update_interval; + int graph_monitoring_duration; + + int sensor_values_max_length; + int sensor_update_interval; +}; + +/* + Loads config from GConf +*/ +struct config *config_load(); + +void config_save(struct config *); + +void config_init(); + +struct color *config_get_sensor_color(char *sensor_name, + struct color *default_color); + +void config_set_sensor_color(char *sensor_name, struct color *color); + +int config_get_sensor_alarm_limit(char *, int); +void config_set_sensor_alarm_limit(char *sensor_name, int alarm_limit); + +int config_get_sensor_alarm_enabled(char *); +void config_set_sensor_alarm_enabled(char *, int); + +int config_is_sensor_enabled(char *); +void config_set_sensor_enabled(char *, int); + +char *config_get_sensor_name(char *); +void config_set_sensor_name(char *, const char *); + +#endif diff --git a/src/compat.h b/src/compat.h new file mode 100644 index 0000000..243c46f --- /dev/null +++ b/src/compat.h @@ -0,0 +1,26 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +/* + gtk_dialog_get_content_area exists since gtk 2.14 As debian 5.0.6 + (lenny) is using gtk 2.12 defines the function by using a macro. +*/ +#if !defined(gtk_dialog_get_content_area) +#define gtk_dialog_get_content_area(dialog) (GTK_DIALOG(dialog)->vbox) +#endif diff --git a/src/description.txt b/src/description.txt new file mode 100644 index 0000000..27d3db9 --- /dev/null +++ b/src/description.txt @@ -0,0 +1,32 @@ +[description] + +/psensor is a/ + +It displays a curve for each sensor, and uses Desktop Notification to +raise an alarm when a temperature is too high. On Ubuntu an +Application Indicator is also available, its icon changes when a +temperature alert is raised. + +It can monitor: + * the temperature of the motherboard and CPU sensors (using lm\-sensors). + * the temperature of the NVidia GPUs (using XNVCtrl). + * the temperature of the Hard Disk Drives (using hddtemp). + * the rotation speed of the fans (using lm\-sensors). + * the sensors of a remote computer (using psensor\-server). + +Psensor requires lm\-sensors to be correctly installed and configured, +it can be checked by running the command 'sensors'. If it has never be +done, you may need to run the command 'sensors\-detect' and follow the +instruction. See the manpages of sensors(1) and sensors\-detect(8) for +more information. + +To retrieve the temperature of the Hard Disk Drives, the hddtemp +daemon must be running. + +For remote monitoring: + * start psensor\-server(1) on the remote computer + * run psensor with '\-\-url' option: 'psensor \-\-url=http://localhost:3131' + +[SEE ALSO] + +psensor\-server(1), sensors(1), sensors\-detect(8), hddtemp(8) diff --git a/src/glade/Makefile.am b/src/glade/Makefile.am new file mode 100644 index 0000000..58d1551 --- /dev/null +++ b/src/glade/Makefile.am @@ -0,0 +1,6 @@ +gladedir = $(pkgdatadir) +glade_DATA = \ + sensor-edit.glade \ + psensor-pref.glade + +EXTRA_DIST = $(glade_DATA) diff --git a/src/glade/psensor-pref.glade b/src/glade/psensor-pref.glade new file mode 100644 index 0000000..1501644 --- /dev/null +++ b/src/glade/psensor-pref.glade @@ -0,0 +1,534 @@ + + + + + + 5 + Edit Preferences + True + True + normal + True + + + True + 2 + + + True + 16 + 3 + + + True + 0 + Graph Colors + + + + + + 3 + 4 + 4 + + + + + True + 0 + Foreground: + + + 1 + 2 + 14 + 4 + + + + + True + 0 + Background: + + + 2 + 3 + 14 + 4 + + + + + True + 0 + Background opacity: + + + 3 + 3 + 4 + 14 + 4 + + + + + True + True + True + #000000000000 + + + 1 + 3 + 1 + 2 + + 4 + 4 + + + + + True + True + True + #000000000000 + + + 1 + 3 + 2 + 3 + + 4 + 4 + + + + + True + 0 + Graph + + + + + + 3 + 5 + 6 + 4 + 4 + + + + + True + 0 + Update interval: + + + 6 + 7 + 14 + 4 + + + + + True + 0 + Monitoring duration: + + + 7 + 8 + 14 + 4 + + + + + True + second(s) + + + 2 + 3 + 6 + 7 + 4 + 4 + + + + + True + minute(s) + + + 2 + 3 + 7 + 8 + 4 + 4 + + + + + True + 0 + Sensor + + + + + + 3 + 8 + 9 + 4 + 4 + + + + + True + 0 + Measure update interval: + + + 9 + 10 + 14 + 4 + + + + + True + second(s) + + + 2 + 3 + 9 + 10 + 4 + 4 + + + + + True + 0 + Interface + + + + + + 3 + 10 + 11 + 4 + 4 + + + + + True + 0 + Position of sensors table: + + + 11 + 12 + 14 + 4 + + + + + Hide window decoration + True + True + False + 0 + True + + + 3 + 12 + 13 + 14 + 4 + + + + + Keep window below + True + True + False + 0 + True + + + 3 + 13 + 14 + 14 + 4 + + + + + True + True + • + secs + + + 1 + 2 + 6 + 7 + 4 + 4 + + + + + True + True + • + mins + + + 1 + 2 + 7 + 8 + 4 + 4 + + + + + True + True + • + secs2 + + + 1 + 2 + 9 + 10 + 4 + 4 + + + + + True + liststore1 + + + + 0 + + + + + 1 + 3 + 11 + 12 + 4 + 4 + + + + + True + 8 + + + True + 0 + <i>Min</i> + True + + + False + 0 + + + + + True + True + opacity + False + + + 1 + + + + + True + 0 + <i>Max</i> + True + + + False + 2 + + + + + 3 + 4 + 5 + 14 + 4 + + + + + Enable menu + True + True + False + True + + + 3 + 14 + 15 + 14 + 4 + + + + + Enable Unity Launcher counter + True + True + False + 0 + True + + + 3 + 15 + 16 + 14 + 4 + + + + + 1 + + + + + True + end + + + gtk-ok + True + True + True + True + + + False + False + 0 + + + + + gtk-cancel + True + True + True + True + + + False + False + 1 + + + + + False + end + 0 + + + + + + button1 + button2 + + + + 1 + 0.01 + 0.10000000000000001 + + + + + + + + + Right + + + Left + + + Top + + + Bottom + + + + + 1 + 1 + 256 + 1 + 10 + + + 10 + 1 + 65535 + 1 + 10 + + + 1 + 256 + 1 + 10 + + diff --git a/src/glade/sensor-edit.glade b/src/glade/sensor-edit.glade new file mode 100644 index 0000000..fd5acea --- /dev/null +++ b/src/glade/sensor-edit.glade @@ -0,0 +1,329 @@ + + + + + + 5 + Edit Sensor Preferences + True + True + dialog + True + + + True + 2 + + + True + 10 + 2 + + + True + 0 + N/A + + + 1 + 2 + 1 + 2 + 4 + 4 + + + + + True + 0 + 10 + Name: + + + 3 + 4 + GTK_FILL + 4 + 4 + + + + + True + True + • + + + 1 + 2 + 3 + 4 + 4 + 4 + + + + + True + 0 + Type: + + + 2 + 3 + 14 + 4 + + + + + True + 0 + N/A + + + 1 + 2 + 2 + 3 + 4 + 4 + + + + + True + 0 + Id: + + + 1 + 2 + GTK_FILL + 14 + 4 + + + + + True + 0 + Graph + + + + + + 2 + 4 + 5 + 4 + 4 + + + + + True + 0 + Color: + + + 6 + 7 + 14 + 4 + + + + + True + 0 + Alarm + + + + + + 2 + 7 + 8 + 4 + 4 + + + + + Activate desktop notifications + True + True + False + True + + + 2 + 8 + 9 + 14 + 4 + + + + + True + 0 + Temperature limit: + + + 9 + 10 + GTK_FILL + 14 + 4 + + + + + True + 0 + Sensor Information + + + + + + 2 + 4 + 4 + + + + + Draw sensor curve + True + True + False + 0.41999998688697815 + True + + + 2 + 5 + 6 + 14 + 4 + + + + + True + + + True + True + 3 + • + temp_limit + 1 + True + True + if-valid + + + False + 0 + + + + + True + 0 + °C + + + 1 + + + + + 1 + 2 + 9 + 10 + + + + + True + True + True + 0 + #000000000000 + + + 1 + 2 + 6 + 7 + + 4 + 4 + + + + + 1 + + + + + True + end + + + gtk-ok + True + True + True + True + + + False + False + 0 + + + + + gtk-cancel + True + True + True + True + + + False + False + 1 + + + + + False + end + 0 + + + + + + btn_ok + btn_cancel + + + + 256 + 1 + 10 + + diff --git a/src/graph.c b/src/graph.c new file mode 100644 index 0000000..3b70d3e --- /dev/null +++ b/src/graph.c @@ -0,0 +1,331 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include +#include +#include + +#include "cfg.h" +#include "psensor.h" + +/* horizontal padding */ +#define GRAPH_H_PADDING 4 +/* vertical padding */ +#define GRAPH_V_PADDING 4 + +static time_t get_graph_end_time_s() +{ + struct timeval tv; + + if (gettimeofday(&tv, NULL) == 0) + return tv.tv_sec; + else + return 0; +} + +static time_t get_graph_begin_time_s(struct config *cfg) +{ + int ct; + + ct = get_graph_end_time_s(); + + if (!ct) + return 0; + + return ct - cfg->graph_monitoring_duration * 60; +} + +static int compute_y(double value, double min, double max, int height, int off) +{ + double t = value - min; + return height - ((double)height * (t / (max - min))) + off; +} + +static char *time_to_str(time_t s) +{ + char *str; + /* note: localtime returns a static field, no free required */ + struct tm *tm = localtime(&s); + + if (!tm) + return NULL; + + str = malloc(6); + strftime(str, 6, "%H:%M", tm); + + return str; +} + +static void +draw_graph_background(cairo_t *cr, + int width, int height, struct config *config) +{ + struct color *bgcolor = config->graph_bgcolor; + + /* draw background */ + if (config->alpha_channel_enabled) + cairo_set_source_rgba(cr, + bgcolor->f_red, + bgcolor->f_green, + bgcolor->f_blue, config->graph_bg_alpha); + else + cairo_set_source_rgb(cr, + bgcolor->f_red, + bgcolor->f_green, bgcolor->f_blue); + + cairo_rectangle(cr, 0, 0, width, height); + cairo_fill(cr); +} + +/* setup dash style */ +static double dashes[] = { + 1.0, /* ink */ + 1.0, /* skip */ + 1.0, /* ink */ + 1.0 /* skip */ +}; +static int ndash = sizeof(dashes) / sizeof(dashes[0]); + +static void draw_background_lines(cairo_t *cr, + struct color *color, + int g_width, int g_height, + int g_xoff, int g_yoff, + int min, int max) +{ + int i; + + /* draw background lines */ + cairo_set_dash(cr, dashes, ndash, 0); + cairo_set_line_width(cr, 1); + cairo_set_source_rgb(cr, + color->f_red, color->f_green, color->f_blue); + + /* vertical lines representing time steps */ + for (i = 0; i <= 5; i++) { + int x = i * (g_width / 5) + g_xoff; + cairo_move_to(cr, x, g_yoff); + cairo_line_to(cr, x, g_yoff + g_height); + cairo_stroke(cr); + } + + /* horizontal lines draws a line for each 10C step */ + for (i = min; i < max; i++) { + if (i % 10 == 0) { + int y = compute_y(i, min, max, g_height, g_yoff); + + cairo_move_to(cr, g_xoff, y); + cairo_line_to(cr, g_xoff + g_width, y); + cairo_stroke(cr); + } + } + + /* back to normal line style */ + cairo_set_dash(cr, 0, 0, 0); + +} + +static void draw_sensor_curve(struct psensor *s, + cairo_t *cr, + double min, + double max, + int bt, + int et, + int g_width, + int g_height, + int g_xoff, + int g_yoff) +{ + int first = 1; + int i; + + cairo_set_source_rgb(cr, + s->color->f_red, + s->color->f_green, + s->color->f_blue); + cairo_set_line_width(cr, 1); + + for (i = 0; i < s->values_max_length; i++) { + int x, y, t; + double v; + + t = s->measures[i].time.tv_sec; + v = s->measures[i].value; + + if (v == UNKNOWN_VALUE || !t || (t - bt) < 0) + continue; + + x = (t - bt) * g_width / (et - bt) + g_xoff; + + y = compute_y(v, min, max, g_height, g_yoff); + + if (first) { + cairo_move_to(cr, x, y); + first = 0; + } else { + cairo_line_to(cr, x, y); + } + + } + cairo_stroke(cr); +} + + +void +graph_update(struct psensor **sensors, + GtkWidget *w_graph, struct config *config) +{ + struct color *fgcolor = config->graph_fgcolor; + int et, bt; + double min_rpm = get_min_rpm(sensors); + double max_rpm = get_max_rpm(sensors); + + double mint = get_min_temp(sensors); + char *strmin = psensor_value_to_string(SENSOR_TYPE_TEMP, mint); + + double maxt = get_max_temp(sensors); + char *strmax = psensor_value_to_string(SENSOR_TYPE_TEMP, maxt); + + int width = w_graph->allocation.width; + int height = w_graph->allocation.height; + + int g_width, g_height; + + /* horizontal and vertical offset of the graph */ + int g_xoff, g_yoff; + + cairo_surface_t *cst = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, + width, + height); + cairo_t *cr = cairo_create(cst); + cairo_t *cr_pixmap; + + char *str_btime = time_to_str(get_graph_begin_time_s(config)); + cairo_text_extents_t te_btime; + + char *str_etime = time_to_str(get_graph_end_time_s()); + cairo_text_extents_t te_etime; + + cairo_text_extents_t te_max, te_min; + + struct psensor **sensor_cur; + + draw_graph_background(cr, width, height, config); + + cairo_select_font_face(cr, + "sans-serif", + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size(cr, 10.0); + + cairo_text_extents(cr, str_etime, &te_etime); + cairo_text_extents(cr, str_btime, &te_btime); + cairo_text_extents(cr, strmax, &te_max); + cairo_text_extents(cr, strmin, &te_min); + + g_yoff = GRAPH_V_PADDING; + + g_height = height - GRAPH_V_PADDING; + if (te_etime.height > te_btime.height) + g_height -= GRAPH_V_PADDING + te_etime.height + GRAPH_V_PADDING; + else + g_height -= GRAPH_V_PADDING + te_btime.height + GRAPH_V_PADDING; + + if (te_min.width > te_max.width) + g_xoff = (2 * GRAPH_H_PADDING) + te_max.width; + else + g_xoff = (2 * GRAPH_H_PADDING) + te_min.width; + + g_width = width - g_xoff - GRAPH_H_PADDING; + + cairo_set_source_rgb(cr, + fgcolor->f_red, fgcolor->f_green, fgcolor->f_blue); + + /* draw graph begin time */ + cairo_move_to(cr, g_xoff, height - GRAPH_V_PADDING); + cairo_show_text(cr, str_btime); + free(str_btime); + + /* draw graph end time */ + cairo_move_to(cr, + width - te_etime.width - GRAPH_H_PADDING, + height - GRAPH_V_PADDING); + cairo_show_text(cr, str_etime); + free(str_etime); + + /* draw min and max temp */ + cairo_move_to(cr, GRAPH_H_PADDING, te_max.height + GRAPH_V_PADDING); + cairo_show_text(cr, strmax); + free(strmax); + + cairo_move_to(cr, + GRAPH_H_PADDING, height - (te_min.height / 2) - g_yoff); + cairo_show_text(cr, strmin); + free(strmin); + + draw_background_lines(cr, fgcolor, + g_width, g_height, + g_xoff, g_yoff, + mint, maxt); + + /* .. and finaly draws the temperature graphs */ + bt = get_graph_begin_time_s(config); + et = get_graph_end_time_s(); + + if (bt && et) { + sensor_cur = sensors; + while (*sensor_cur) { + struct psensor *s = *sensor_cur; + + if (s->enabled) { + double min, max; + + if (is_fan_type(s->type)) { + min = min_rpm; + max = max_rpm; + } else { + min = mint; + max = maxt; + } + + draw_sensor_curve(s, cr, + min, max, + bt, et, + g_width, g_height, + g_xoff, g_yoff); + } + + sensor_cur++; + } + } + + cr_pixmap = gdk_cairo_create(w_graph->window); + + if (cr_pixmap) { + + if (config->alpha_channel_enabled) + cairo_set_operator(cr_pixmap, CAIRO_OPERATOR_SOURCE); + + cairo_set_source_surface(cr_pixmap, cst, 0, 0); + cairo_paint(cr_pixmap); + } + + cairo_destroy(cr_pixmap); + cairo_surface_destroy(cst); + cairo_destroy(cr); +} diff --git a/src/graph.h b/src/graph.h new file mode 100644 index 0000000..77ed5ed --- /dev/null +++ b/src/graph.h @@ -0,0 +1,30 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#ifndef _PSENSOR_GRAPH_H_ +#define _PSENSOR_GRAPH_H_ + +#include + +#include "psensor.h" +#include "cfg.h" + +void graph_update(struct psensor **, GtkWidget *, struct config *config); + +#endif diff --git a/src/httpd/Makefile.am b/src/httpd/Makefile.am new file mode 100644 index 0000000..34c10fc --- /dev/null +++ b/src/httpd/Makefile.am @@ -0,0 +1,41 @@ +AM_CPPFLAGS = -pedantic -Werror -DDEFAULT_WWW_DIR=\""$(pkgdatadir)/www"\"\ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/lib \ + $(LUA_CFLAGS)\ + $(GTK_CFLAGS)\ + $(APPINDICATOR_CFLAGS)\ + $(GCONF_CFLAGS)\ + $(SENSORS_CFLAGS)\ + $(NVIDIA_CFLAGS)\ + $(CURL_CFLAGS)\ + $(JSON_CFLAGS)\ + $(GTOP_CFLAGS)\ + $(LIBMICROHTTPD_CFLAGS) + +LIBS = \ + $(top_srcdir)/src/plib/libplib.a \ + $(top_srcdir)/src/lib/libpsensor.a \ + $(LUA_LIBS)\ + $(GTK_LIBS)\ + $(APPINDICATOR_LIBS)\ + $(GCONF_LIBS)\ + $(SENSORS_LIBS)\ + $(NVIDIA_LIBS)\ + $(CURL_LIBS)\ + $(JSON_LIBS)\ + $(GTOP_LIBS)\ + $(LIBMICROHTTPD_LIBS) + +bin_PROGRAMS = psensor-server + +psensor_server_SOURCES = httpd.c httpd.h + + + + +dist_man_MANS = psensor-server.1 + + +psensor-server.1: httpd.c $(top_srcdir)/configure.ac + $(MAKE) $(AM_MAKEFLAGS) psensor-server$(EXEEXT) + help2man --output=psensor-server.1 ./psensor-server$(EXEEXT) \ No newline at end of file diff --git a/src/httpd/httpd.c b/src/httpd/httpd.c new file mode 100644 index 0000000..fccccac --- /dev/null +++ b/src/httpd/httpd.c @@ -0,0 +1,426 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_GTOP +#include +#endif + +#define lua_c + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + +#include "plib/url.h" +#include "httpd.h" +#include "psensor.h" +#include "hdd.h" +#include "nvidia.h" +#include "lmsensor.h" + +#define DEFAULT_PORT 3131 + + +#define PAGE_NOT_FOUND \ +"

Page not found - Go to Main page\ +

" + + +#define PSENSOR_SERVER_OPTIONS " --help display this help and exit\n\ + --version output version information and exit\n\ + --port=PORT webserver port\n\ + --wdir=DIR directory containing webserver pages" + +#define PSENSOR_SERVER_USAGE "Usage: psensor-server [OPTION]" + +#define PSENSOR_SERVER_DESCRIPTION \ +"HTTP server for retrieving hardware temperatures." + +#define PSENSOR_SERVER_CONTACT "Report bugs to: psensor@wpitchoune.net\n\ +Psensor home page: " + +#define PSENSOR_SERVER_COPYRIGHT \ +"Copyright (C) 2010-2011 wpitchoune@gmail.com\n\ +License GPLv2: GNU GPL version 2 or later \ +\n\ +This is free software: you are free to change and redistribute it.\n\ +There is NO WARRANTY, to the extent permitted by law." + +#define OPT_PORT "port" +#define OPT_HELP "help" +#define OPT_VERSION "version" +#define OPT_WWW_DIR "wdir" + +static struct option long_options[] = { + {OPT_VERSION, no_argument, 0, 'v'}, + {OPT_HELP, no_argument, 0, 'h'}, + {OPT_PORT, required_argument, 0, 'p'}, + {OPT_WWW_DIR, required_argument, 0, 'w'}, + {0, 0, 0, 0} +}; + +static char *www_dir = DEFAULT_WWW_DIR; + +#ifdef HAVE_GTOP +static float cpu_rate; +static glibtop_cpu *cpu; +#endif + +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + +json_object *sensor_to_json_object(struct psensor *s) +{ + json_object *mo; + json_object *obj = json_object_new_object(); + struct measure *m; + + json_object_object_add(obj, "id", json_object_new_string(s->id)); + json_object_object_add(obj, "name", json_object_new_string(s->name)); + json_object_object_add(obj, "type", json_object_new_int(s->type)); + json_object_object_add(obj, "min", json_object_new_double(s->min)); + json_object_object_add(obj, "max", json_object_new_double(s->max)); + + m = psensor_get_current_measure(s); + mo = json_object_new_object(); + json_object_object_add(mo, "value", json_object_new_double(m->value)); + json_object_object_add(mo, "time", + json_object_new_int((m->time).tv_sec)); + json_object_object_add(obj, "last_measure", mo); + + return obj; +} + +char *sensor_to_json_string(struct psensor *s) +{ + char *str; + json_object *obj = sensor_to_json_object(s); + + str = strdup(json_object_to_json_string(obj)); + + json_object_put(obj); + + return str; +} + +char *sensors_to_json_string(struct psensor **sensors) +{ + struct psensor **sensors_cur; + char *str; + + json_object *obj = json_object_new_array(); + + sensors_cur = sensors; + + while (*sensors_cur) { + struct psensor *s = *sensors_cur; + + json_object_array_add(obj, sensor_to_json_object(s)); + + sensors_cur++; + } + + str = strdup(json_object_to_json_string(obj)); + + json_object_put(obj); + + return str; +} + +char *lua_to_html_page(struct psensor **sensors, const char *url) +{ + struct psensor **s_cur; + char *page = NULL; + int i; + char *lua_fpath; + + lua_State *L = lua_open(); /* create state */ + + if (!L) + return NULL; + + luaL_openlibs(L); + +#ifdef HAVE_GTOP + lua_newtable(L); + lua_pushstring(L, "load"); + lua_pushnumber(L, cpu_rate); + lua_settable(L, -3); + lua_setglobal(L, "cpu"); +#endif + + lua_newtable(L); + + s_cur = sensors; + i = 0; + while (*s_cur) { + + lua_pushinteger(L, i); + + lua_newtable(L); + + lua_pushstring(L, "name"); + lua_pushstring(L, (*s_cur)->name); + lua_settable(L, -3); + + lua_pushstring(L, "measure_last"); + lua_pushnumber(L, psensor_get_current_value(*s_cur)); + lua_settable(L, -3); + + lua_pushstring(L, "measure_min"); + lua_pushnumber(L, (*s_cur)->min); + lua_settable(L, -3); + + lua_pushstring(L, "measure_max"); + lua_pushnumber(L, (*s_cur)->max); + lua_settable(L, -3); + + lua_pushstring(L, "id"); + lua_pushstring(L, (*s_cur)->id); + lua_settable(L, -3); + + lua_settable(L, -3); + + s_cur++; + i++; + } + + lua_setglobal(L, "sensors"); + + lua_fpath = malloc(strlen(www_dir) + strlen(url) + 1); + strcpy(lua_fpath, www_dir); + strcat(lua_fpath, url); + + if (!luaL_loadfile(L, lua_fpath) && !lua_pcall(L, 0, 1, 0)) + page = strdup(lua_tostring(L, -1)); + + lua_close(L); + + free(lua_fpath); + + return page; +} + +static int cbk_http_request(void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, + size_t *upload_data_size, void **ptr) +{ + struct psensor **sensors = cls; + static int dummy; + struct MHD_Response *response; + int ret; + char *nurl; + unsigned int resp_code; + char *page = NULL; + + if (strcmp(method, "GET")) + return MHD_NO; /* unexpected method */ + + if (&dummy != *ptr) { + /* The first time only the headers are valid, do not + respond in the first round... */ + *ptr = &dummy; + return MHD_YES; + } + + if (*upload_data_size) + return MHD_NO; /* upload data in a GET!? */ + + *ptr = NULL; /* clear context pointer */ + + nurl = url_normalize(url); + + pthread_mutex_lock(&mutex); + + if (!strcmp(nurl, URL_BASE_API_1_0_SENSORS)) { + page = sensors_to_json_string(sensors); + + } else if (!strncmp(nurl, URL_BASE_API_1_0_SENSORS, + strlen(URL_BASE_API_1_0_SENSORS)) + && nurl[strlen(URL_BASE_API_1_0_SENSORS)] == '/') { + + char *sid = nurl + strlen(URL_BASE_API_1_0_SENSORS) + 1; + struct psensor *s = psensor_list_get_by_id(sensors, sid); + + if (s) + page = sensor_to_json_string(s); + + } else { + page = lua_to_html_page(sensors, nurl); + } + + if (page) { + resp_code = MHD_HTTP_OK; + } else { + page = strdup(PAGE_NOT_FOUND); + resp_code = MHD_HTTP_NOT_FOUND; + } + + pthread_mutex_unlock(&mutex); + + response = MHD_create_response_from_data(strlen(page), + (void *)page, MHD_YES, MHD_NO); + + ret = MHD_queue_response(connection, resp_code, response); + MHD_destroy_response(response); + + free(nurl); + + return ret; +} + +int main(int argc, char *argv[]) +{ + float last_used = 0; + float last_total = 0; + struct MHD_Daemon *d; + struct psensor **sensors; + int port = DEFAULT_PORT; + + while (1) { + int c, option_index = 0; + + c = getopt_long(argc, argv, "vhpw:", long_options, + &option_index); + + if (c == -1) + break; + + switch (c) { + + case 0: + break; + + case 'w': + if (optarg) + www_dir = strdup(optarg); + break; + + case 'p': + if (optarg) + port = atoi(optarg); + + break; + + case 'h': + printf("%s\n%s\n\n%s\n\n%s\n", + PSENSOR_SERVER_USAGE, + PSENSOR_SERVER_DESCRIPTION, + PSENSOR_SERVER_OPTIONS, PSENSOR_SERVER_CONTACT); + exit(EXIT_SUCCESS); + + case 'v': + printf("psensor-server %s\n", VERSION); + printf("%s\n", PSENSOR_SERVER_COPYRIGHT); + + exit(EXIT_SUCCESS); + + case '?': + break; + + default: + abort(); + } + } + + if (optind != argc) { + fprintf(stderr, "Invalid usage.\n"); + exit(EXIT_FAILURE); + } + + if (!lmsensor_init()) { + fprintf(stderr, "failed to init lm-sensors\n"); + exit(EXIT_FAILURE); + } + + sensors = get_all_sensors(1); + + if (!sensors || !*sensors) { + fprintf(stderr, "no sensors detected\n"); + exit(EXIT_FAILURE); + } + + d = MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION, + port, + NULL, NULL, &cbk_http_request, sensors, + MHD_OPTION_END); + if (!d) { + fprintf(stderr, "Fail to create web server\n"); + exit(EXIT_FAILURE); + } + + printf("Web server started on port: %d\n", port); + printf("Psensor URL: http://localhost:%d\n", port); + + while (1) { + unsigned long int used = 0; + unsigned long int dt; + + pthread_mutex_lock(&mutex); + +#ifdef HAVE_GTOP + if (!cpu) + cpu = malloc(sizeof(glibtop_cpu)); + + glibtop_get_cpu(cpu); + + used = cpu->user + cpu->nice + cpu->sys; + + dt = cpu->total - last_total; + + if (dt) + cpu_rate = (used - last_used) / dt; + + last_used = used; + last_total = cpu->total; + + psensor_list_update_measures(sensors); +#endif + + pthread_mutex_unlock(&mutex); + sleep(5); + } + + MHD_stop_daemon(d); + +#ifdef HAVE_GTOP + free(cpu); +#endif + + return 0; +} diff --git a/src/httpd/httpd.h b/src/httpd/httpd.h new file mode 100644 index 0000000..c493c2b --- /dev/null +++ b/src/httpd/httpd.h @@ -0,0 +1,26 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#ifndef _PSENSOR_HTTPD_H_ +#define _PSENSOR_HTTPD_H_ + +#define URL_BASE_API_1_0 "/api/1.0" +#define URL_BASE_API_1_0_SENSORS "/api/1.0/sensors" + +#endif diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am new file mode 100644 index 0000000..eb10b2f --- /dev/null +++ b/src/lib/Makefile.am @@ -0,0 +1,15 @@ +noinst_LIBRARIES = libpsensor.a + +libpsensor_a_CFLAGS = -pedantic -Werror + +libpsensor_a_SOURCES = \ + measure.h measure.c \ + color.h color.c \ + psensor.h psensor.c\ + hdd.h hdd.c\ + nvidia.h \ + lmsensor.h lmsensor.c + +if NVIDIA +libpsensor_a_SOURCES += nvidia.c +endif diff --git a/src/lib/color.c b/src/lib/color.c new file mode 100644 index 0000000..e747701 --- /dev/null +++ b/src/lib/color.c @@ -0,0 +1,99 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include +#include +#include +#include + +#include "color.h" + +void +color_set(struct color *color, + unsigned int red, unsigned int green, unsigned int blue) +{ + color->red = red; + color->green = green; + color->blue = blue; + + color->f_red = ((double)color->red) / 65535; + color->f_green = ((double)color->green) / 65535; + color->f_blue = ((double)color->blue) / 65535; +} + +struct color *color_new(unsigned int red, unsigned int green, unsigned int blue) +{ + struct color *color = malloc(sizeof(struct color)); + + color_set(color, red, green, blue); + + return color; +} + +struct color *color_dup(struct color *color) +{ + return color_new(color->red, color->green, color->blue); +} + +int is_color(const char *str) +{ + int n = strlen(str); + int i; + + if (n != 13 || str[0] != '#') + return 0; + + for (i = 1; i < n; i++) + if (isxdigit(str[i]) == 0) + return 0; + + return 1; +} + +struct color *string_to_color(const char *str) +{ + char tmp[5]; + unsigned int red, green, blue; + + if (!is_color(str)) + return NULL; + + strncpy(tmp, str + 1, 4); + tmp[4] = '\0'; + red = strtol(tmp, NULL, 16); + + strncpy(tmp, str + 5, 4); + tmp[4] = '\0'; + green = strtol(tmp, NULL, 16); + + strncpy(tmp, str + 9, 4); + tmp[4] = '\0'; + blue = strtol(tmp, NULL, 16); + + return color_new(red, green, blue); +} + +char *color_to_string(struct color *color) +{ + char *str = malloc(1 + 12 + 1); + + sprintf(str, "#%.4x%.4x%.4x", color->red, color->green, color->blue); + + return str; +} diff --git a/src/lib/color.h b/src/lib/color.h new file mode 100644 index 0000000..2ac5525 --- /dev/null +++ b/src/lib/color.h @@ -0,0 +1,57 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#ifndef _PSENSOR_COLOR_H_ +#define _PSENSOR_COLOR_H_ + +/* Represents a RGB color. + + Contains integer and floating RGB representation to avoid useless +conversion. + + Uses color_set to maintain the coherence of the both + representation. +*/ +struct color { + /* rgb 0..65535 */ + unsigned int red; + unsigned int green; + unsigned int blue; + + /* rgb floating 0..1 */ + double f_red; + double f_green; + double f_blue; +}; + +struct color *color_new(unsigned int red, + unsigned int green, unsigned int blue); + +struct color *color_dup(struct color *); + +void color_set(struct color *, + unsigned int red, unsigned int green, unsigned int blue); + +int is_color(const char *str); + +struct color *string_to_color(const char *str); + +char *color_to_string(struct color *color); + +#endif diff --git a/src/lib/hdd.c b/src/lib/hdd.c new file mode 100644 index 0000000..27fd296 --- /dev/null +++ b/src/lib/hdd.c @@ -0,0 +1,256 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include +#include +#define _(str) gettext(str) + +/* + Following code is based on GNOME sensors applet code hddtemp-plugin.c + see http://sensors-applet.sourceforge.net/ +*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "psensor.h" + +#define HDDTEMP_SERVER_IP_ADDRESS "127.0.0.1" +#define HDDTEMP_PORT_NUMBER 7634 +#define HDDTEMP_OUTPUT_BUFFER_LENGTH 4048 + +struct hdd_info { + char *name; + int temp; +}; + +char *hdd_fetch() +{ + int sockfd; + ssize_t n = 1; + int output_length = 0; + char *pc; + char *buffer; + struct sockaddr_in address; + + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd == -1) { + fprintf(stderr, _("ERROR: hdd_fetch, failed to open socket\n")); + return NULL; + } + + address.sin_family = AF_INET; + address.sin_addr.s_addr = inet_addr(HDDTEMP_SERVER_IP_ADDRESS); + address.sin_port = htons(HDDTEMP_PORT_NUMBER); + + buffer = NULL; + + if (connect(sockfd, + (struct sockaddr *)&address, + (socklen_t) sizeof(address)) == -1) { + fprintf(stderr, + _("ERROR: hdd_fetch, failed to open connection\n")); + } else { + buffer = malloc(HDDTEMP_OUTPUT_BUFFER_LENGTH); + + pc = buffer; + while ((n = read(sockfd, + pc, + HDDTEMP_OUTPUT_BUFFER_LENGTH - + output_length)) > 0) { + + output_length += n; + pc = &pc[n]; + } + + buffer[output_length] = '\0'; + } + + close(sockfd); + + return buffer; +} + +int str_index(char *str, char d) +{ + char *c; + int i; + + if (!str || *str == '\0') + return -1; + + c = str; + + i = 0; + while (*c) { + if (*c == d) + return i; + i++; + c++; + } + + return -1; +} + +struct psensor *hdd_create_sensor(char *id, char *name, int values_max_length) +{ + return psensor_create(id, name, SENSOR_TYPE_HDD_TEMP, + values_max_length); +} + +char *next_hdd_info(char *string, struct hdd_info *info) +{ + char *c; + int idx_name_n, i, temp; + + if (!string || strlen(string) <= 5 /* at least 5 pipes */ + || string[0] != '|') + return NULL; + + /* skip first pipe */ + c = string + 1; + + /* name */ + idx_name_n = str_index(c, '|'); + + if (idx_name_n == -1) + return NULL; + c = c + idx_name_n + 1; + + /* skip label */ + i = str_index(c, '|'); + if (i == -1) + return NULL; + c = c + i + 1; + + /* temp */ + i = str_index(c, '|'); + if (i == -1) + return NULL; + temp = atoi(c); + c = c + i + 1; + + /* skip unit */ + i = str_index(c, '|'); + if (i == -1) + return NULL; + c = c + i + 1; + + info->name = malloc(idx_name_n + 1); + strncpy(info->name, string + 1, idx_name_n); + info->name[idx_name_n] = '\0'; + + info->temp = temp; + + return c; +} + +struct psensor **hdd_psensor_list_add(struct psensor **sensors, + int values_max_length) +{ + char *hddtemp_output = hdd_fetch(); + char *c; + struct hdd_info info; + struct psensor **result; + + if (!hddtemp_output) + return sensors; + + if (hddtemp_output[0] != '|') { + fprintf(stderr, + _("ERROR: wrong hdd string: %s"), hddtemp_output); + + free(hddtemp_output); + + return sensors; + } + + c = hddtemp_output; + + result = sensors; + + while (c && (c = next_hdd_info(c, &info))) { + struct psensor *sensor; + struct psensor **tmp_sensors; + + char *id = malloc(strlen("hdd ") + strlen(info.name) + 1); + strcpy(id, "hdd "); + strcat(id, info.name); + + sensor = hdd_create_sensor(id, info.name, values_max_length); + + tmp_sensors = psensor_list_add(result, sensor); + + if (result != sensors) + free(result); + + result = tmp_sensors; + } + + free(hddtemp_output); + + return result; +} + +void hdd_psensor_update(struct psensor **sensors, struct hdd_info *info) +{ + struct psensor **sensor_cur = sensors; + + while (*sensor_cur) { + if ((*sensor_cur)->type == SENSOR_TYPE_HDD_TEMP + && !strcmp((*sensor_cur)->id + 4, info->name)) + psensor_set_current_value(*sensor_cur, + (float)info->temp); + + sensor_cur++; + } +} + +void hdd_psensor_list_update(struct psensor **sensors) +{ + char *hddtemp_output = hdd_fetch(); + + if (!hddtemp_output) + return; + + if (hddtemp_output[0] == '|') { + + char *c = hddtemp_output; + struct hdd_info info; + info.name = NULL; + info.temp = 0; + + while (c && (c = next_hdd_info(c, &info))) { + + hdd_psensor_update(sensors, &info); + + free(info.name); + } + } else { + fprintf(stderr, + _("ERROR: wrong hdd string: %s\n"), hddtemp_output); + } + + free(hddtemp_output); +} diff --git a/src/lib/hdd.h b/src/lib/hdd.h new file mode 100644 index 0000000..0e31b49 --- /dev/null +++ b/src/lib/hdd.h @@ -0,0 +1,30 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#ifndef _PSENSOR_HDD_H_ +#define _PSENSOR_HDD_H_ + +#include "psensor.h" + +struct psensor **hdd_psensor_list_add(struct psensor **sensors, + int values_max_length); + +void hdd_psensor_list_update(struct psensor **sensors); + +#endif diff --git a/src/lib/lmsensor.c b/src/lib/lmsensor.c new file mode 100644 index 0000000..5149e80 --- /dev/null +++ b/src/lib/lmsensor.c @@ -0,0 +1,172 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include +#include +#define _(str) gettext(str) + +#include +#include +#include + +#include +#include + +#include "psensor.h" + +double +lmsensor_get_value(const sensors_chip_name *name, + const sensors_subfeature *sub) +{ + double val; + int err; + + err = sensors_get_value(name, sub->number, &val); + if (err) { + fprintf(stderr, + _("ERROR: Can't get value of subfeature %s: %s\n"), + sub->name, sensors_strerror(err)); + val = UNKNOWN_VALUE; + } + return val; +} + +double lmsensor_get_temp_input(struct psensor *sensor) +{ + const sensors_chip_name *chip = sensor->iname; + const sensors_feature *feature = sensor->feature; + + const sensors_subfeature *sf; + + sf = sensors_get_subfeature(chip, + feature, SENSORS_SUBFEATURE_TEMP_INPUT); + if (sf) + return lmsensor_get_value(chip, sf); + else + return UNKNOWN_VALUE; +} + +double lmsensor_get_fan_input(struct psensor *sensor) +{ + const sensors_chip_name *chip = sensor->iname; + const sensors_feature *feature = sensor->feature; + + const sensors_subfeature *sf; + + sf = sensors_get_subfeature(chip, + feature, SENSORS_SUBFEATURE_FAN_INPUT); + if (sf) + return lmsensor_get_value(chip, sf); + else + return UNKNOWN_VALUE; +} + +void lmsensor_psensor_list_update(struct psensor **sensors) +{ + struct psensor **s_ptr = sensors; + + while (*s_ptr) { + struct psensor *sensor = *s_ptr; + + if (sensor->type == SENSOR_TYPE_LMSENSOR_TEMP) + psensor_set_current_value + (sensor, lmsensor_get_temp_input(sensor)); + else if (sensor->type == SENSOR_TYPE_LMSENSOR_FAN) + psensor_set_current_value + (sensor, lmsensor_get_fan_input(sensor)); + + s_ptr++; + } +} + +struct psensor * +lmsensor_psensor_create(const sensors_chip_name *chip, + const sensors_feature *feature, + int values_max_length) +{ + char name[200]; + const sensors_subfeature *sf; + char *label; + int type; + char *id; + struct psensor *psensor; + sensors_subfeature_type fault_subfeature; + + if (sensors_snprintf_chip_name(name, 200, chip) < 0) + return NULL; + + if (feature->type == SENSORS_FEATURE_TEMP) { + fault_subfeature = SENSORS_SUBFEATURE_TEMP_FAULT; + + } else if (feature->type == SENSORS_FEATURE_FAN) { + fault_subfeature = SENSORS_SUBFEATURE_FAN_FAULT; + + } else { + fprintf(stderr, + _("ERROR: create_sensor, wrong feature type\n")); + return NULL; + } + + sf = sensors_get_subfeature(chip, feature, fault_subfeature); + if (sf && lmsensor_get_value(chip, sf)) + return NULL; + + label = sensors_get_label(chip, feature); + if (!label) + return NULL; + + type = 0; + if (feature->type == SENSORS_FEATURE_TEMP) + type = SENSOR_TYPE_LMSENSOR_TEMP; + else if (feature->type == SENSORS_FEATURE_FAN) + type = SENSOR_TYPE_LMSENSOR_FAN; + else + return NULL; + + id = malloc(strlen("lmsensor ") + 1 + strlen(name) + 1 + strlen(label) + + 1); + sprintf(id, "lmsensor %s %s", name, label); + + psensor = psensor_create(id, label, type, values_max_length); + + psensor->iname = chip; + psensor->feature = feature; + + if (feature->type == SENSORS_FEATURE_TEMP + && (lmsensor_get_temp_input(psensor) == UNKNOWN_VALUE)) { + free(psensor); + return NULL; + } + + return psensor; +} + +int lmsensor_init() +{ + int err = sensors_init(NULL); + + if (err) { + fprintf(stderr, + _("ERROR: lm-sensors initialization failure: %s\n"), + sensors_strerror(err)); + return 0; + } else { + return 1; + } +} diff --git a/src/lib/lmsensor.h b/src/lib/lmsensor.h new file mode 100644 index 0000000..3efe6ed --- /dev/null +++ b/src/lib/lmsensor.h @@ -0,0 +1,31 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#ifndef _PSENSOR_LMSENSOR_H_ +#define _PSENSOR_LMSENSOR_H_ + +#include + +struct psensor *lmsensor_psensor_create(const sensors_chip_name *chip, + const sensors_feature *feature, + int values_max_length); +int lmsensor_init(); +void lmsensor_psensor_list_update(struct psensor **sensors); + +#endif diff --git a/src/lib/measure.c b/src/lib/measure.c new file mode 100644 index 0000000..7dcacc3 --- /dev/null +++ b/src/lib/measure.c @@ -0,0 +1,48 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include +#include +#include "measure.h" + +struct measure *measures_create(int size) +{ + int i; + struct measure *result; + + result = malloc(size * sizeof(struct measure)); + + for (i = 0; i < size; i++) { + result[i].value = UNKNOWN_VALUE; + timerclear(&result[i].time); + } + + return result; +} + +void measures_free(struct measure *measures) +{ + free(measures); +} + +void measure_copy(struct measure *src, struct measure *dst) +{ + dst->time = src->time; + dst->value = src->value; +} diff --git a/src/lib/measure.h b/src/lib/measure.h new file mode 100644 index 0000000..f1380a5 --- /dev/null +++ b/src/lib/measure.h @@ -0,0 +1,39 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#ifndef _PSENSOR_MEASURE_H_ +#define _PSENSOR_MEASURE_H_ + +#include +#include + +#define UNKNOWN_VALUE DBL_MIN + +struct measure { + double value; + struct timeval time; +}; + +void measure_copy(struct measure *src, struct measure *dst); + +struct measure *measures_create(int size); + +void measures_free(struct measure *measures); + +#endif diff --git a/src/lib/nvidia.c b/src/lib/nvidia.c new file mode 100644 index 0000000..4a74bbf --- /dev/null +++ b/src/lib/nvidia.c @@ -0,0 +1,150 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include +#include +#define _(str) gettext(str) + +#include +#include +#include + +#include + +#include +#include + + +#include "psensor.h" + +Display *nvidia_sensors_dpy; + +int nvidia_get_sensor_temp(struct psensor *sensor) +{ + int temp; + Bool res; + + res = XNVCTRLQueryTargetAttribute(nvidia_sensors_dpy, + NV_CTRL_TARGET_TYPE_GPU, + sensor->nvidia_id, + 0, + NV_CTRL_GPU_CORE_TEMPERATURE, &temp); + + if (res == True) { + return temp; + } else { + fprintf(stderr, + _("ERROR: failed to retrieve nvidia temperature\n")); + return 0; + } +} + +struct psensor *nvidia_create_sensor(int id, int values_max_length) +{ + char name[200]; + char *sid; + struct psensor *s; + + sprintf(name, "GPU%d", id); + + sid = malloc(strlen("nvidia") + 1 + strlen(name) + 1); + sprintf(sid, "nvidia %s", name); + + s = psensor_create(sid, strdup(name), + SENSOR_TYPE_NVIDIA, values_max_length); + + s->nvidia_id = id; + + return s; +} + +int nvidia_init() +{ + int event_base, error_base; + int num_gpus; + + nvidia_sensors_dpy = XOpenDisplay(NULL); + + if (!nvidia_sensors_dpy) { + fprintf(stderr, _("ERROR: nvidia initialization failure\n")); + return 0; + } + + if (XNVCTRLQueryExtension(nvidia_sensors_dpy, &event_base, + &error_base)) { + if (XNVCTRLQueryTargetCount(nvidia_sensors_dpy, + NV_CTRL_TARGET_TYPE_GPU, + &num_gpus)) { + return num_gpus; + } + + } + + fprintf(stderr, _("ERROR: nvidia initialization failure: %d\n"), + error_base); + + return 0; +} + +void nvidia_psensor_list_update(struct psensor **sensors) +{ + struct psensor **s_ptr = sensors; + + while (*s_ptr) { + struct psensor *sensor = *s_ptr; + + if (sensor->type == SENSOR_TYPE_NVIDIA) { + int val = nvidia_get_sensor_temp(sensor); + + psensor_set_current_value(sensor, (double)val); + } + + s_ptr++; + } +} + +struct psensor **nvidia_psensor_list_add(struct psensor **sensors, + int values_max_length) +{ + int i; + int nvidia_gpus_count = nvidia_init(); + struct psensor **res = sensors; + + + if (!nvidia_gpus_count) { + fprintf(stderr, + _("ERROR: " + "no nvidia chips or initialization failure\n")); + } + + for (i = 0; i < nvidia_gpus_count; i++) { + struct psensor *sensor + = nvidia_create_sensor(i, values_max_length); + + struct psensor **tmp_psensors = psensor_list_add(res, + sensor); + + if (res != sensors) + free(res); + + res = tmp_psensors; + } + + return res; +} diff --git a/src/lib/nvidia.h b/src/lib/nvidia.h new file mode 100644 index 0000000..8a9e4e7 --- /dev/null +++ b/src/lib/nvidia.h @@ -0,0 +1,44 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#ifndef _PSENSOR_NVIDIA_H_ +#define _PSENSOR_NVIDIA_H_ + +#include "psensor.h" + +int nvidia_get_sensor_temp(struct psensor *); + +struct psensor *nvidia_create_sensor(int id, int values_max_length); + +int nvidia_init(); + +void nvidia_psensor_list_update(struct psensor **sensors); + + +/* + Adds NVIDIA sensors to a given list of sensors. + + Returns the new allocated list of sensors if sensors have been added otherwise + returns 'sensors'. The list is 'NULL' terminated. + */ +struct psensor **nvidia_psensor_list_add(struct psensor **sensors, + int values_max_length); + + +#endif diff --git a/src/lib/psensor.c b/src/lib/psensor.c new file mode 100644 index 0000000..f735a5e --- /dev/null +++ b/src/lib/psensor.c @@ -0,0 +1,422 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include +#include + +#include +#include + +#include "hdd.h" +#include "psensor.h" +#include "lmsensor.h" + +struct psensor *psensor_create(char *id, char *name, + unsigned int type, int values_max_length) +{ + struct psensor *psensor + = (struct psensor *)malloc(sizeof(struct psensor)); + + psensor->id = id; + psensor->name = name; + psensor->enabled = 1; + psensor->min = UNKNOWN_VALUE; + psensor->max = UNKNOWN_VALUE; + + psensor->type = type; + + psensor->values_max_length = values_max_length; + psensor->measures = measures_create(values_max_length); + + psensor->alarm_limit = 0; + + psensor->cb_alarm_raised = NULL; + psensor->cb_alarm_raised_data = NULL; + psensor->alarm_raised = 0; + + psensor->alarm_enabled = 0; + + psensor->url = NULL; + + psensor->color = NULL; + + return psensor; +} + +void psensor_values_resize(struct psensor *s, int new_size) +{ + struct measure *new_ms, *cur_ms; + int cur_size; + + cur_size = s->values_max_length; + cur_ms = s->measures; + new_ms = measures_create(new_size); + + if (cur_ms) { + int i; + for (i = 0; i < new_size - 1 && i < cur_size - 1; i++) + measure_copy(&cur_ms[cur_size - i - 1], + &new_ms[new_size - i - 1]); + + measures_free(s->measures); + } + + s->values_max_length = new_size; + s->measures = new_ms; +} + +void psensor_free(struct psensor *sensor) +{ + if (sensor) { + free(sensor->name); + free(sensor->id); + + if (sensor->color) + free(sensor->color); + + measures_free(sensor->measures); + + free(sensor->url); + + free(sensor); + } +} + +void psensor_list_free(struct psensor **sensors) +{ + struct psensor **sensor_cur; + + if (sensors) { + sensor_cur = sensors; + + while (*sensor_cur) { + psensor_free(*sensor_cur); + + sensor_cur++; + } + + free(sensors); + + sensors = NULL; + } +} + +int psensor_list_size(struct psensor **sensors) +{ + int size; + struct psensor **sensor_cur; + + if (!sensors) + return 0; + + size = 0; + sensor_cur = sensors; + + while (*sensor_cur) { + size++; + sensor_cur++; + } + return size; +} + +int psensor_list_contains_type(struct psensor **sensors, unsigned int type) +{ + struct psensor **s; + + if (!sensors) + return 0; + + s = sensors; + while (*s) { + if ((*s)->type == type) + return 1; + s++; + } + + return 0; +} + +struct psensor **psensor_list_add(struct psensor **sensors, + struct psensor *sensor) +{ + int size = psensor_list_size(sensors); + + struct psensor **result + = malloc((size + 1 + 1) * sizeof(struct psensor *)); + + if (sensors) + memcpy(result, sensors, size * sizeof(struct psensor *)); + + result[size] = sensor; + result[size + 1] = NULL; + + return result; +} + +struct psensor *psensor_list_get_by_id(struct psensor **sensors, const char *id) +{ + struct psensor **sensors_cur = sensors; + + while (*sensors_cur) { + if (!strcmp((*sensors_cur)->id, id)) + return *sensors_cur; + + sensors_cur++; + } + + return NULL; +} + +int is_temp_type(unsigned int type) +{ + return type & SENSOR_TYPE_TEMP; +} + +int is_fan_type(unsigned int type) +{ + return type & SENSOR_TYPE_FAN; +} + +char *psensor_value_to_string(unsigned int type, double value) +{ + /* should not be possible to exceed 20 characters with temp or + rpm values the .x part is never displayed */ + char *str = malloc(20); + + char *unit; + + if (is_temp_type(type)) + unit = "C"; + else + unit = ""; + + sprintf(str, "%.0f%s", value, unit); + + return str; +} + +void psensor_set_current_value(struct psensor *sensor, double value) +{ + struct timeval tv; + + if (gettimeofday(&tv, NULL) != 0) + timerclear(&tv); + + psensor_set_current_measure(sensor, value, tv); +} + +void +psensor_set_current_measure(struct psensor *s, + double v, struct timeval tv) +{ + memmove(s->measures, + &s->measures[1], + (s->values_max_length - 1) * sizeof(struct measure)); + + s->measures[s->values_max_length - 1].value = v; + s->measures[s->values_max_length - 1].time = tv; + + if (s->min == UNKNOWN_VALUE || v < s->min) + s->min = v; + + if (s->max == UNKNOWN_VALUE || v > s->max) + s->max = v; + + if (s->alarm_limit && s->alarm_enabled) { + if (v > s->alarm_limit) { + if (!s->alarm_raised && s->cb_alarm_raised) + s->cb_alarm_raised(s, + s->cb_alarm_raised_data); + + s->alarm_raised = 1; + } else { + s->alarm_raised = 0; + } + } +} + +double psensor_get_current_value(struct psensor *sensor) +{ + return sensor->measures[sensor->values_max_length - 1].value; +} + +struct measure *psensor_get_current_measure(struct psensor *sensor) +{ + return &sensor->measures[sensor->values_max_length - 1]; +} + +/* + Returns the minimal value of a given 'type' (SENSOR_TYPE_TEMP or + SENSOR_TYPE_FAN) + */ +double get_min_value(struct psensor **sensors, int type) +{ + double m = UNKNOWN_VALUE; + struct psensor **s = sensors; + + while (*s) { + struct psensor *sensor = *s; + + if (sensor->enabled && (sensor->type & type)) { + int i; + double t; + + for (i = 0; i < sensor->values_max_length; i++) { + t = sensor->measures[i].value; + + if (t == UNKNOWN_VALUE) + continue; + + if (m == UNKNOWN_VALUE || t < m) + m = t; + } + } + s++; + } + + return m; +} + +/* + Returns the maximal value of a given 'type' (SENSOR_TYPE_TEMP or + SENSOR_TYPE_FAN) + */ +static double get_max_value(struct psensor **sensors, int type) +{ + double m = UNKNOWN_VALUE; + struct psensor **s = sensors; + + while (*s) { + struct psensor *sensor = *s; + + if (sensor->enabled && (sensor->type & type)) { + int i; + double t; + for (i = 0; i < sensor->values_max_length; i++) { + t = sensor->measures[i].value; + + if (t == UNKNOWN_VALUE) + continue; + + if (m == UNKNOWN_VALUE || t > m) + m = t; + } + } + s++; + } + + return m; +} + +double get_min_temp(struct psensor **sensors) +{ + return get_min_value(sensors, SENSOR_TYPE_TEMP); +} + +double get_min_rpm(struct psensor **sensors) +{ + return get_min_value(sensors, SENSOR_TYPE_FAN); +} + +double get_max_rpm(struct psensor **sensors) +{ + return get_max_value(sensors, SENSOR_TYPE_FAN); +} + +double get_max_temp(struct psensor **sensors) +{ + return get_max_value(sensors, SENSOR_TYPE_TEMP); +} + +struct psensor **get_all_sensors(int values_max_length) +{ + struct psensor **psensors = NULL; + int count = 0; + const sensors_chip_name *chip; + int chip_nr = 0; + struct psensor **tmp_psensors; + const sensors_feature *feature; + struct psensor *psensor; + int i; + + while ((chip = sensors_get_detected_chips(NULL, &chip_nr))) { + i = 0; + while ((feature = sensors_get_features(chip, &i))) { + + if (feature->type == SENSORS_FEATURE_TEMP + || feature->type == SENSORS_FEATURE_FAN) { + + psensor = lmsensor_psensor_create + (chip, feature, values_max_length); + + if (psensor) { + tmp_psensors + = psensor_list_add(psensors, + psensor); + + free(psensors); + + psensors = tmp_psensors; + + count++; + } + } + } + } + + tmp_psensors = hdd_psensor_list_add(psensors, values_max_length); + + if (tmp_psensors != psensors) { + free(psensors); + psensors = tmp_psensors; + } + + if (!psensors) { /* there is no detected sensors */ + psensors = malloc(sizeof(struct psensor *)); + *psensors = NULL; + } + + return psensors; +} + +const char *psensor_type_to_str(unsigned int type) +{ + if (type & SENSOR_TYPE_REMOTE) + return "Remote"; + + if (type & SENSOR_TYPE_LMSENSOR_TEMP) + return "Temperature"; + + if (type & SENSOR_TYPE_LMSENSOR_FAN) + return "Fan"; + + if (type & SENSOR_TYPE_NVIDIA) + return "NVidia GPU Temperature"; + + if (type & SENSOR_TYPE_HDD_TEMP) + return "HDD Temperature"; + + return "N/A"; /* should not be possible */ +} + +void psensor_list_update_measures(struct psensor **sensors) +{ + lmsensor_psensor_list_update(sensors); + hdd_psensor_list_update(sensors); +} diff --git a/src/lib/psensor.h b/src/lib/psensor.h new file mode 100644 index 0000000..ad7364e --- /dev/null +++ b/src/lib/psensor.h @@ -0,0 +1,155 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#ifndef _PSENSOR_PSENSOR_H_ +#define _PSENSOR_PSENSOR_H_ + +#include "config.h" +#include + +#include "color.h" +#include "measure.h" + +enum psensor_type { + SENSOR_TYPE_TEMP = 0x0001, + SENSOR_TYPE_FAN = 0x0002, + SENSOR_TYPE_REMOTE = 0x0004, + + SENSOR_TYPE_LMSENSOR_TEMP = 0x0100 | SENSOR_TYPE_TEMP, + SENSOR_TYPE_NVIDIA = 0x0200 | SENSOR_TYPE_TEMP, + SENSOR_TYPE_HDD_TEMP = 0x0300 | SENSOR_TYPE_TEMP, + SENSOR_TYPE_LMSENSOR_FAN = 0x0400 | SENSOR_TYPE_FAN +}; + +struct psensor { + /* Human readable name of the sensor. It may not be uniq. */ + char *name; + + /* Uniq id of the sensor */ + char *id; + + /* lm-sensor */ + const sensors_chip_name *iname; + const sensors_feature *feature; + + /* Maximum length of 'values' */ + int values_max_length; + + /* Last registered measures of the sensor. Index 0 for the + oldest measure. */ + struct measure *measures; + + /* Color of the sensor used for the graph */ + struct color *color; + + /* Whether the sensor is displayed in the graph */ + int enabled; + + /* see psensor_type */ + unsigned int type; + + /* The maximum detected value of the sensor */ + double max; + + /* The minimum detected value of the sensor */ + double min; + + /* + Whether alarm alerts is enabled for this sensor + */ + int alarm_enabled; + + /* + An alarm is raised if the current sensor value is bigger. 0 + means no limit + */ + double alarm_limit; + + /* Whether the current value is bigger than 'alarm_limit'. */ + int alarm_raised; + + void (*cb_alarm_raised) (struct psensor *, void *); + void *cb_alarm_raised_data; + +#ifdef HAVE_NVIDIA + /* Nvidia id for the nvctrl */ + int nvidia_id; +#endif + + char *url; + +}; + +struct psensor *psensor_create(char *id, + char *name, unsigned int type, + int values_max_length); + +void psensor_values_resize(struct psensor *s, int new_size); + +void psensor_free(struct psensor *sensor); + +void psensor_list_free(struct psensor **sensors); +int psensor_list_size(struct psensor **sensors); + +struct psensor *psensor_list_get_by_id(struct psensor **sensors, + const char *id); + +/* + Return 1 if there is at least one sensor of a given type, else + returns 0 */ +int psensor_list_contains_type(struct psensor **sensors, unsigned int type); + +int is_temp_type(unsigned int type); +int is_fan_type(unsigned int type); + +double get_min_temp(struct psensor **sensors); +double get_max_temp(struct psensor **sensors); + +double get_min_rpm(struct psensor **sensors); +double get_max_rpm(struct psensor **sensors); + +/* + Converts the value of a sensor to a string. + + parameter 'type' is SENSOR_TYPE_LMSENSOR_TEMP, SENSOR_TYPE_NVIDIA, + or SENSOR_TYPE_LMSENSOR_FAN +*/ +char *psensor_value_to_string(unsigned int type, double value); + +struct psensor **get_all_sensors(int values_max_length); + +struct psensor **psensor_list_add(struct psensor **sensors, + struct psensor *sensor); + +void psensor_set_current_value(struct psensor *sensor, double value); +void psensor_set_current_measure(struct psensor *sensor, double value, + struct timeval tv); + +double psensor_get_current_value(struct psensor *sensor); + +struct measure *psensor_get_current_measure(struct psensor *sensor); + +/* + Returns a string representation of a psensor type. +*/ +const char *psensor_type_to_str(unsigned int type); + +void psensor_list_update_measures(struct psensor **sensors); + +#endif diff --git a/src/libpsensor_json/Makefile.am b/src/libpsensor_json/Makefile.am new file mode 100644 index 0000000..a85c829 --- /dev/null +++ b/src/libpsensor_json/Makefile.am @@ -0,0 +1,12 @@ +noinst_LIBRARIES = libpsensor_json.a + +libpsensor_json_a_CFLAGS = -pedantic -Werror + +libpsensor_json_a_SOURCES = \ + psensor_json.h psensor_json.c + +LIBS = ../lib/libpsensor.a \ + $(JSON_LIBS) + +AM_CPPFLAGS = $(JSON_CFLAGS) -I$(top_srcdir)/src/lib + diff --git a/src/libpsensor_json/psensor_json.c b/src/libpsensor_json/psensor_json.c new file mode 100644 index 0000000..b476439 --- /dev/null +++ b/src/libpsensor_json/psensor_json.c @@ -0,0 +1,99 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include + +#include "psensor_json.h" + +#define ATT_SENSOR_ID "id" +#define ATT_SENSOR_NAME "name" +#define ATT_SENSOR_TYPE "type" +#define ATT_SENSOR_MIN "min" +#define ATT_SENSOR_MAX "max" +#define ATT_SENSOR_LAST_MEASURE "last_measure" +#define ATT_MEASURE_VALUE "value" +#define ATT_MEASURE_TIME "time" + +json_object *sensor_to_json_object(struct psensor *s) +{ + json_object *mo; + json_object *obj = json_object_new_object(); + struct measure *m; + + json_object_object_add(obj, + ATT_SENSOR_ID, + json_object_new_string(s->id)); + json_object_object_add(obj, + ATT_SENSOR_NAME, + json_object_new_string(s->name)); + json_object_object_add(obj, + ATT_SENSOR_TYPE, json_object_new_int(s->type)); + json_object_object_add(obj, + ATT_SENSOR_MIN, json_object_new_double(s->min)); + json_object_object_add(obj, + ATT_SENSOR_MAX, json_object_new_double(s->max)); + + m = psensor_get_current_measure(s); + mo = json_object_new_object(); + json_object_object_add(mo, + ATT_MEASURE_VALUE, + json_object_new_double(m->value)); + json_object_object_add(mo, ATT_MEASURE_TIME, + json_object_new_int((m->time).tv_sec)); + json_object_object_add(obj, ATT_SENSOR_LAST_MEASURE, mo); + + return obj; +} + +char *sensor_to_json_string(struct psensor *s) +{ + char *str; + json_object *obj = sensor_to_json_object(s); + + str = strdup(json_object_to_json_string(obj)); + + json_object_put(obj); + + return str; +} + +char *sensors_to_json_string(struct psensor **sensors) +{ + struct psensor **sensors_cur; + char *str; + + json_object *obj = json_object_new_array(); + + sensors_cur = sensors; + + while (*sensors_cur) { + struct psensor *s = *sensors_cur; + + json_object_array_add(obj, sensor_to_json_object(s)); + + sensors_cur++; + } + + str = strdup(json_object_to_json_string(obj)); + + json_object_put(obj); + + return str; +} + diff --git a/src/libpsensor_json/psensor_json.h b/src/libpsensor_json/psensor_json.h new file mode 100644 index 0000000..c97e6f6 --- /dev/null +++ b/src/libpsensor_json/psensor_json.h @@ -0,0 +1,32 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#ifndef _PSENSOR_PSENSOR_JSON_H_ +#define _PSENSOR_PSENSOR_JSON_H_ + +#include + +#include "psensor.h" + +json_object *sensor_to_json_object(struct psensor *s); +char *sensor_to_json_string(struct psensor *s); +char *sensors_to_json_string(struct psensor **sensors); + + +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..3a092a8 --- /dev/null +++ b/src/main.c @@ -0,0 +1,420 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "config.h" + +#include "cfg.h" +#include "hdd.h" +#include "psensor.h" +#include "graph.h" +#include "ui.h" +#include "ui_sensorlist.h" +#include "ui_color.h" +#include "lmsensor.h" +#include "ui_pref.h" +#include "ui_graph.h" + +#ifdef HAVE_UNITY +#include "ui_unity.h" +#endif + +#ifdef HAVE_NVIDIA +#include "nvidia.h" +#endif + +#ifdef HAVE_REMOTE_SUPPORT +#include "rsensor.h" +#endif + +#if defined(HAVE_APPINDICATOR) || defined(HAVE_APPINDICATOR_029) +#include "ui_appindicator.h" +#endif + +#ifdef HAVE_LIBNOTIFY +#include "ui_notify.h" +#endif + +#include "compat.h" + +static const char *program_name; + +void print_version() +{ + printf("psensor %s\n", VERSION); + printf(_("Copyright (C) %s wpitchoune@gmail.com\n\ +License GPLv2: GNU GPL version 2 or later \ +\n\ +This is free software: you are free to change and redistribute it.\n\ +There is NO WARRANTY, to the extent permitted by law.\n"), + "2010-2011"); +} + +void print_help() +{ + printf(_("Usage: %s [OPTION]...\n"), program_name); + + puts(_("psensor is a GTK application for monitoring hardware sensors, " + "including temperatures and fan speeds.")); + + puts(""); + puts(_("Options:")); + puts(_("\ + -h, --help display this help and exit\n\ + -v, --version display version information and exit")); + + puts(""); + + puts(_("\ + -u, --url=URL \ +the URL of the psensor-server, example: http://hostname:3131")); + + puts(""); + + printf(_("Report bugs to: %s\n"), PACKAGE_BUGREPORT); + puts(""); + printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL); +} + +/* + Updates the size of the sensor values if different than the + configuration. + */ +void +update_psensor_values_size(struct psensor **sensors, struct config *cfg) +{ + struct psensor **cur; + + cur = sensors; + while (*cur) { + struct psensor *s = *cur; + + if (s->values_max_length != cfg->sensor_values_max_length) + psensor_values_resize(s, + cfg->sensor_values_max_length); + + cur++; + } +} + +void update_psensor_measures(struct ui_psensor *ui) +{ + struct psensor **sensors = ui->sensors; + struct config *cfg = ui->config; + + while (1) { + /*gdk_threads_enter();*/ + g_mutex_lock(ui->sensors_mutex); + + if (!sensors) + return; + + update_psensor_values_size(sensors, ui->config); + + psensor_list_update_measures(sensors); +#ifdef HAVE_REMOTE_SUPPORT + remote_psensor_list_update(sensors); +#endif +#ifdef HAVE_NVIDIA + nvidia_psensor_list_update(sensors); +#endif + + /*gdk_threads_leave();*/ + g_mutex_unlock(ui->sensors_mutex); + + sleep(cfg->sensor_update_interval); + } +} + +gboolean ui_refresh_thread(gpointer data) +{ + struct config *cfg; + gboolean ret; + struct ui_psensor *ui = (struct ui_psensor *)data; + + ret = TRUE; + cfg = ui->config; + + g_mutex_lock(ui->sensors_mutex); + /*gdk_threads_enter();*/ + + graph_update(ui->sensors, ui->w_graph, ui->config); + + ui_sensorlist_update(ui->ui_sensorlist); + +#if defined(HAVE_APPINDICATOR) || defined(HAVE_APPINDICATOR_029) + ui_appindicator_update(ui); +#endif + +#ifdef HAVE_UNITY + ui_unity_launcher_entry_update(ui->sensors); +#endif + + if (ui->graph_update_interval != cfg->graph_update_interval) { + ui->graph_update_interval = cfg->graph_update_interval; + ret = FALSE; + } + + g_mutex_unlock(ui->sensors_mutex); + /*gdk_threads_leave();*/ + + if (ret == FALSE) + g_timeout_add(1000 * ui->graph_update_interval, + ui_refresh_thread, ui); + + return ret; +} + +void cb_alarm_raised(struct psensor *sensor, void *data) +{ +#ifdef HAVE_LIBNOTIFY + if (sensor->enabled) + ui_notify(sensor, (struct ui_psensor *)data); +#endif +} + +void associate_colors(struct psensor **sensors) +{ + /* number of uniq colors */ +#define COLORS_COUNT 8 + + unsigned int colors[COLORS_COUNT][3] = { + {0x0000, 0x0000, 0x0000}, /* black */ + {0xffff, 0x0000, 0x0000}, /* red */ + {0x0000, 0.0000, 0xffff}, /* blue */ + {0x0000, 0xffff, 0x0000}, /* green */ + + {0x7fff, 0x7fff, 0x7fff}, /* grey */ + {0x7fff, 0x0000, 0x0000}, /* dark red */ + {0x0000, 0x0000, 0x7fff}, /* dark blue */ + {0x0000, 0x7fff, 0x0000} /* dark green */ + }; + + struct psensor **sensor_cur = sensors; + int i = 0; + while (*sensor_cur) { + struct color default_color; + color_set(&default_color, + colors[i % COLORS_COUNT][0], + colors[i % COLORS_COUNT][1], + colors[i % COLORS_COUNT][2]); + + (*sensor_cur)->color + = config_get_sensor_color((*sensor_cur)->id, + &default_color); + + sensor_cur++; + i++; + } +} + +void +associate_cb_alarm_raised(struct psensor **sensors, struct ui_psensor *ui) +{ + struct psensor **sensor_cur = sensors; + while (*sensor_cur) { + struct psensor *s = *sensor_cur; + + s->cb_alarm_raised = cb_alarm_raised; + s->cb_alarm_raised_data = ui; + + if (is_temp_type(s->type)) { + s->alarm_limit + = config_get_sensor_alarm_limit(s->id, 60); + s->alarm_enabled + = config_get_sensor_alarm_enabled(s->id); + } else { + s->alarm_limit = 0; + s->alarm_enabled = 0; + } + + sensor_cur++; + } +} + +void associate_preferences(struct psensor **sensors) +{ + struct psensor **sensor_cur = sensors; + while (*sensor_cur) { + char *n; + struct psensor *s = *sensor_cur; + + s->enabled = config_is_sensor_enabled(s->id); + + n = config_get_sensor_name(s->id); + + if (n) + s->name = n; + + sensor_cur++; + } +} + + +static struct option long_options[] = { + {"version", no_argument, 0, 'v'}, + {"help", no_argument, 0, 'h'}, + {"url", required_argument, 0, 'u'}, + {0, 0, 0, 0} +}; + +int main(int argc, char **argv) +{ + struct ui_psensor ui; + GError *error; + GThread *thread; + int err, optc; + char *url = NULL; + int cmdok = 1; + + program_name = argv[0]; + + setlocale(LC_ALL, ""); + +#if ENABLE_NLS + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); +#endif + + while ((optc = getopt_long(argc, argv, "vhu:", long_options, + NULL)) != -1) { + switch (optc) { + case 'u': + if (optarg) + url = strdup(optarg); + break; + case 'h': + print_help(); + exit(EXIT_SUCCESS); + case 'v': + print_version(); + exit(EXIT_SUCCESS); + default: + cmdok = 0; + break; + } + } + + if (!cmdok || optind != argc) { + fprintf(stderr, _("Try `%s --help' for more information.\n"), + program_name); + exit(EXIT_FAILURE); + } + + g_thread_init(NULL); + gdk_threads_init(); + /* gdk_threads_enter(); */ + + gtk_init(&argc, &argv); + +#ifdef HAVE_LIBNOTIFY + ui.notification_last_time = NULL; +#endif + + ui.sensors_mutex = g_mutex_new(); + + config_init(); + + ui.config = config_load(); + + err = lmsensor_init(); + if (!err) { + fprintf(stderr, _("ERROR: lmsensor init failure: %s\n"), + sensors_strerror(err)); + exit(EXIT_FAILURE); + } + + if (url) { +#ifdef HAVE_REMOTE_SUPPORT + rsensor_init(); + ui.sensors = get_remote_sensors(url, 600); +#else + fprintf(stderr, + _("ERROR: Not compiled with remote sensor support.\n")); + exit(EXIT_FAILURE); +#endif + } else { +#ifdef HAVE_NVIDIA + struct psensor **tmp; + + tmp = get_all_sensors(600); + ui.sensors = nvidia_psensor_list_add(tmp, 600); + + if (tmp != ui.sensors) + free(tmp); +#else + ui.sensors = get_all_sensors(600); +#endif + } + + associate_preferences(ui.sensors); + associate_colors(ui.sensors); + associate_cb_alarm_raised(ui.sensors, &ui); + + /* main window */ + ui.main_window = ui_window_create(&ui); + ui.main_box = NULL; + + /* drawing box */ + ui.w_graph = ui_graph_create(&ui); + + /* sensor list */ + ui.ui_sensorlist = ui_sensorlist_create(ui.sensors); + + ui_main_box_create(&ui); + + gtk_widget_show_all(ui.main_window); + + thread = g_thread_create((GThreadFunc) update_psensor_measures, + &ui, TRUE, &error); + + if (!thread) + g_error_free(error); + + ui.graph_update_interval = ui.config->graph_update_interval; + + g_timeout_add(1000 * ui.graph_update_interval, ui_refresh_thread, &ui); + +#if defined(HAVE_APPINDICATOR) || defined(HAVE_APPINDICATOR_029) + ui_appindicator_init(&ui); +#endif + + /* main loop */ + gtk_main(); + + sensors_cleanup(); + + psensor_list_free(ui.sensors); + + /* gdk_threads_leave(); */ + + return 0; +} diff --git a/src/plib/Makefile.am b/src/plib/Makefile.am new file mode 100644 index 0000000..6b6dd69 --- /dev/null +++ b/src/plib/Makefile.am @@ -0,0 +1,21 @@ + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src + +noinst_LIBRARIES = libplib.a + +libplib_a_CFLAGS = -pedantic + +libplib_a_SOURCES = \ + url.c url.h \ + plib_io.h plib_io.c + +if LUA +noinst_LIBRARIES += libplib_luatpl.a + +libplib_luatpl_a_CFLAGS = -pedantic \ + $(LUA_CFLAGS) + +libplib_luatpl_a_SOURCES = \ + plib_luatpl.h plib_luatpl.c +endif \ No newline at end of file diff --git a/src/plib/plib_io.c b/src/plib/plib_io.c new file mode 100644 index 0000000..7396ad0 --- /dev/null +++ b/src/plib/plib_io.c @@ -0,0 +1,297 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include +#include +#include +#include +#include + +#include "plib_io.h" + +int is_dir(const char *path) +{ + struct stat st; + + int ret = lstat(path, &st); + + if (ret == 0 && S_ISDIR(st.st_mode)) + return 1; + + return 0; +} + +int is_file(const char *path) +{ + struct stat st; + + int ret = lstat(path, &st); + + if (ret == 0 && S_ISREG(st.st_mode)) + return 1; + + return 0; +} + +char *dir_normalize(const char *dpath) +{ + char *npath; + int n; + + if (!dpath || !strlen(dpath)) + return NULL; + + npath = strdup(dpath); + + n = strlen(npath); + + if (n > 1 && npath[n - 1] == '/') + npath[n - 1] = '\0'; + + return npath; +} + +char **dir_list(const char *dpath, int (*filter) (const char *path)) +{ + struct dirent *ent; + DIR *dir; + char **paths; + int n; + + dir = opendir(dpath); + + if (!dir) + return NULL; + + n = 1; + paths = malloc(sizeof(void *)); + *paths = NULL; + + while ((ent = readdir(dir)) != NULL) { + char *fpath; + char *name = ent->d_name; + + if (!strcmp(name, ".") || !strcmp(name, "..")) + continue; + + fpath = malloc(strlen(dpath) + 1 + strlen(name) + 1); + + strcpy(fpath, dpath); + strcat(fpath, "/"); + strcat(fpath, name); + + if (!filter || filter(fpath)) { + char **npaths; + + n++; + npaths = malloc(n * sizeof(void *)); + memcpy(npaths + 1, paths, (n - 1) * sizeof(void *)); + free(paths); + paths = npaths; + *npaths = fpath; + + } else { + free(fpath); + } + } + + closedir(dir); + + return paths; +} + +void paths_free(char **paths) +{ + char **paths_cur; + + paths_cur = paths; + while (*paths_cur) { + free(*paths_cur); + + paths_cur++; + } + + free(paths); +} + +char *file_get_content(const char *fpath) +{ + long size; + + char *page; + + size = file_get_size(fpath); + + if (size == -1) { + page = NULL; + + } else if (size == 0) { + page = malloc(1); + *page = '\0'; + + } else { + FILE *fp = fopen(fpath, "rb"); + if (fp) { + page = malloc(size + 1); + if (!page || size != fread(page, 1, size, fp)) { + free(page); + return NULL; + } + + *(page + size) = '\0'; + + fclose(fp); + } else { + page = NULL; + } + } + + return page; +} + +long file_get_size(const char *path) +{ + FILE *fp; + + if (!is_file(path)) + return -1; + + fp = fopen(path, "rb"); + if (fp) { + long size; + + if (fseek(fp, 0, SEEK_END) == -1) + return -1; + + size = ftell(fp); + + fclose(fp); + + return size; + } + + return -1; +} + +#define FCOPY_BUF_SZ 4096 +static int FILE_copy(FILE *src, FILE *dst) +{ + int ret = 0; + char *buf = malloc(FCOPY_BUF_SZ); + int n; + + if (!buf) + return FILE_COPY_ERROR_ALLOC_BUFFER; + + while (!ret) { + n = fread(buf, 1, FCOPY_BUF_SZ, src); + if (n) { + if (fwrite(buf, 1, n, dst) != n) + ret = FILE_COPY_ERROR_WRITE; + } else { + if (!feof(src)) + ret = FILE_COPY_ERROR_READ; + else + break; + } + } + + free(buf); + + return ret; +} + +int +file_copy(const char *src, const char *dst) +{ + FILE *fsrc, *fdst; + int ret = 0; + + fsrc = fopen(src, "r"); + + if (fsrc) { + fdst = fopen(dst, "w+"); + + if (fdst) { + ret = FILE_copy(fsrc, fdst); + fclose(fdst); + } else { + ret = FILE_COPY_ERROR_OPEN_DST; + } + + fclose(fsrc); + } else { + ret = FILE_COPY_ERROR_OPEN_SRC; + } + + return ret; +} + +char *path_append(const char *dir, const char *path) +{ + char *ret, *ndir; + + ndir = dir_normalize(dir); + + if (!ndir && (!path || !strlen(path))) + ret = NULL; + + else if (!ndir) { + ret = strdup(path); + + } else if (!path || !strlen(path)) { + return ndir; + + } else { + ret = malloc(strlen(ndir) + 1 + strlen(path) + 1); + strcpy(ret, ndir); + strcat(ret, "/"); + strcat(ret, path); + } + + free(ndir); + + return ret; +} + +void +file_copy_print_error(int code, const char *src, const char *dst) +{ + switch (code) { + case 0: + break; + case FILE_COPY_ERROR_OPEN_SRC: + printf("File copy error: failed to open %s.\n", src); + break; + case FILE_COPY_ERROR_OPEN_DST: + printf("File copy error: failed to open %s.\n", dst); + break; + case FILE_COPY_ERROR_READ: + printf("File copy error: failed to read %s.\n", src); + break; + case FILE_COPY_ERROR_WRITE: + printf("File copy error: failed to write %s.\n", src); + break; + case FILE_COPY_ERROR_ALLOC_BUFFER: + printf("File copy error: failed to allocate buffer.\n"); + break; + default: + printf("File copy error: unknown error %d.\n", code); + } +} diff --git a/src/plib/plib_io.h b/src/plib/plib_io.h new file mode 100644 index 0000000..a20d76e --- /dev/null +++ b/src/plib/plib_io.h @@ -0,0 +1,71 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#ifndef _P_IO_H +#define _P_IO_H + +/* Returns '1' if a given 'path' denotates a directory else returns + 0 */ +int is_dir(const char *path); + +/* Returns '1' if a given 'path' denotates a file else returns + 0 */ +int is_file(const char *path); + +/* Returns a normalized path */ +char *path_normalize(const char *dpath); + +/* Returns the null-terminated entries of a directory */ +char **dir_list(const char *dpath, int (*filter) (const char *path)); +void paths_free(char **paths); + +char *path_append(const char *dir, const char *path); + +/* + Returns the size of a file. + Returns '-1' if the size cannot be retrieved or not a file. +*/ +long file_get_size(const char *path); + +/* + Returns the content of a file. + Returns 'NULL' if the file cannot be read or failed to allocate + enough memory. + Returns an empty string if the file exists but is empty. +*/ +char *file_get_content(const char *path); + +enum file_copy_error { + FILE_COPY_ERROR_OPEN_SRC = 1, + FILE_COPY_ERROR_OPEN_DST, + FILE_COPY_ERROR_READ, + FILE_COPY_ERROR_WRITE, + FILE_COPY_ERROR_ALLOC_BUFFER +}; + +void file_copy_print_error(int code, const char *src, const char *dst); + +/* + Copy a file. + + Returns '0' if sucessfull, otherwise return the error code. +*/ +int file_copy(const char *src, const char *dst); + +#endif diff --git a/src/plib/plib_luatpl.c b/src/plib/plib_luatpl.c new file mode 100644 index 0000000..dd46213 --- /dev/null +++ b/src/plib/plib_luatpl.c @@ -0,0 +1,157 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ +#include +#include +#define _(str) gettext(str) + +#include +#include + +#include +#include + +#include "plib_luatpl.h" + +char * +luatpl_generate(const char *lua, + int (*init) (lua_State *, void *), + void *init_data, + struct luatpl_error *err) +{ + lua_State *L; + char *page = NULL; + + L = lua_open(); + if (!L) { + err->code = LUATPL_ERROR_LUA_STATE_OPEN; + return NULL; + } + + luaL_openlibs(L); + + if (!init || init(L, init_data)) { + + if (!luaL_loadfile(L, lua)) { + if (!lua_pcall(L, 0, 1, 0)) { + if (lua_isstring(L, -1)) + page = strdup(lua_tostring(L, -1)); + else + err->code = + LUATPL_ERROR_WRONG_RETURN_TYPE; + } else { + err->code = LUATPL_ERROR_LUA_EXECUTION; + err->message = strdup(lua_tostring(L, -1)); + } + } else { + err->code = LUATPL_ERROR_LUA_FILE_LOAD; + } + } else { + err->code = LUATPL_ERROR_INIT; + } + + lua_close(L); + + return page; +} + +int +luatpl_generate_file(const char *lua, + int (*init) (lua_State *, void *), + void *init_data, + const char *dst_path, + struct luatpl_error *err) +{ + FILE *f; + int ret; + char *content; + + ret = 1; + + content = luatpl_generate(lua, init, init_data, err); + + if (content) { + f = fopen(dst_path, "w"); + + if (f) { + if (fputs(content, f) == EOF) + ret = 0; + + if (fclose(f) == EOF) + ret = 0; + } else { + ret = 0; + } + + free(content); + } else { + ret = 0; + } + + return ret; +} + +void +luatpl_fprint_error(FILE *stream, + const struct luatpl_error *err, + const char *lua, + const char *dst_path) +{ + if (!err || !err->code) + return ; + + switch (err->code) { + case LUATPL_ERROR_LUA_FILE_LOAD: + fprintf(stream, + _("LUATPL Error: failed to load Lua script: %s.\n"), + lua); + break; + + case LUATPL_ERROR_INIT: + fprintf(stream, + _("LUATPL Error: failed to call init function: %s.\n"), + lua); + break; + + case LUATPL_ERROR_LUA_EXECUTION: + fprintf(stream, + _("LUATPL Error:" + "failed to execute Lua script (%s): %s.\n"), + lua, err->message); + break; + + case LUATPL_ERROR_WRONG_RETURN_TYPE: + fprintf(stream, + _("LUATPL Error:" + "lua script (%s) returned a wrong type.\n"), + lua); + break; + + case LUATPL_ERROR_LUA_STATE_OPEN: + fprintf(stream, + _("LUATPL Error:" + "failed to open lua state.\n")); + break; + + + default: + fprintf(stream, + _("LUATPL Error: code: %d.\n"), + err->code); + } +} diff --git a/src/plib/plib_luatpl.h b/src/plib/plib_luatpl.h new file mode 100644 index 0000000..4e36856 --- /dev/null +++ b/src/plib/plib_luatpl.h @@ -0,0 +1,78 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#ifndef _P_LUATPL_H +#define _P_LUATPL_H + +#include + +#define LUATPL_ERROR_LUA_FILE_LOAD 1 +#define LUATPL_ERROR_INIT 2 +#define LUATPL_ERROR_LUA_EXECUTION 3 +#define LUATPL_ERROR_WRONG_RETURN_TYPE 4 +#define LUATPL_ERROR_LUA_STATE_OPEN 5 + +struct luatpl_error { + unsigned int code; + + char *message; +}; + +/* + Generates a string which is the result of a Lua script execution. + + The string is retrieved from the top element of the Lua stack + after the Lua script execution. + + If not 'NULL' the 'init' function is called after Lua environment + setup and before Lua script execution. This function typically puts + input information for the Lua script into the stack. + + 'init_data' is passed to the second parameter of 'init' function + + Returns the generated string on success, or NULL on error. + */ +char *luatpl_generate(const char *lua, + int (*init) (lua_State *, void *), + void *init_data, + struct luatpl_error *err); + +/* + Generates a file which is the result of a Lua script execution. + + See luatpl_generate function for 'init', 'init_data', and 'err' + parameters. + + 'dst_path' is the path of the generated file + + Returns '1' on success, or '0' on error. + + */ +int luatpl_generate_file(const char *lua, + int (*init) (lua_State *, void *), + void *init_data, + const char *dst_path, + struct luatpl_error *err); + +void luatpl_fprint_error(FILE *stream, + const struct luatpl_error *err, + const char *lua, + const char *dst_path); + +#endif diff --git a/src/plib/url.c b/src/plib/url.c new file mode 100644 index 0000000..7a87adf --- /dev/null +++ b/src/plib/url.c @@ -0,0 +1,75 @@ +/* + Copyright (C) 2010 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +/* + Part of the following code is based on: + http://www.geekhideout.com/urlcode.shtml +*/ + +#include "plib/url.h" + +#include +#include +#include + +char *url_normalize(const char *url) +{ + int n = strlen(url); + char *ret = strdup(url); + + if (url[n - 1] == '/') + ret[n - 1] = '\0'; + + return ret; +} + +char to_hex(char code) +{ + static char hex[] = "0123456789abcdef"; + return hex[code & 0x0f]; +} + +/* + Returns a url-encoded version of str. +*/ +char *url_encode(const char *str) +{ + char *c, *buf, *pbuf; + + buf = malloc(strlen(str) * 3 + 1); + pbuf = buf; + + c = (char *)str; + + while (*c) { + + if (isalnum(*c) || + *c == '.' || *c == '_' || *c == '-' || *c == '~') + *pbuf++ = *c; + else { + *pbuf++ = '%'; + *pbuf++ = to_hex(*c >> 4); + *pbuf++ = to_hex(*c & 0x0f); + } + c++; + } + *pbuf = '\0'; + + return buf; +} diff --git a/src/plib/url.h b/src/plib/url.h new file mode 100644 index 0000000..53a1406 --- /dev/null +++ b/src/plib/url.h @@ -0,0 +1,26 @@ +/* + Copyright (C) 2010 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#ifndef _PLIB_URL_H_ +#define _PLIB_URL_H_ + +char *url_encode(const char *str); +char *url_normalize(const char *url); + +#endif diff --git a/src/rsensor.c b/src/rsensor.c new file mode 100644 index 0000000..7f13c30 --- /dev/null +++ b/src/rsensor.c @@ -0,0 +1,221 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include +#include +#define _(str) gettext(str) + +#include "plib/url.h" +#include "server/server.h" + +#include +#include +#include + +#include +#include + +#include "rsensor.h" + +struct ucontent { + char *data; + size_t len; +}; + +static CURL *curl; + +size_t cbk_curl(void *buffer, size_t size, size_t nmemb, void *userp) +{ + size_t realsize = size * nmemb; + struct ucontent *mem = (struct ucontent *)userp; + + mem->data = realloc(mem->data, mem->len + realsize + 1); + + memcpy(&(mem->data[mem->len]), buffer, realsize); + mem->len += realsize; + mem->data[mem->len] = 0; + + return realsize; +} + +char *create_api_1_0_sensors_url(const char *base_url) +{ + char *nurl = url_normalize(base_url); + int n = strlen(nurl) + strlen(URL_BASE_API_1_0_SENSORS) + 1; + char *ret = malloc(n); + + strcpy(ret, nurl); + strcat(ret, URL_BASE_API_1_0_SENSORS); + + free(nurl); + + return ret; +} + +struct psensor *json_object_to_psensor(json_object * o, + const char *sensors_url, + int values_max_length) +{ + json_object *oid; + json_object *oname; + json_object *otype; + struct psensor *s; + char *eid; + char *url; + + oid = json_object_object_get(o, "id"); + oname = json_object_object_get(o, "name"); + otype = json_object_object_get(o, "type"); + + eid = url_encode(json_object_get_string(oid)); + url = malloc(strlen(sensors_url) + 1 + strlen(eid) + 1); + sprintf(url, "%s/%s", sensors_url, eid); + + s = psensor_create(strdup(url), + strdup(json_object_get_string(oname)), + json_object_get_int(otype) | SENSOR_TYPE_REMOTE, + values_max_length); + s->url = url; + + free(eid); + + return s; +} + +void rsensor_init() +{ + curl = curl_easy_init(); +} + +void rsensor_end() +{ + curl_easy_cleanup(curl); +} + +json_object *get_json_object(const char *url) +{ + + struct ucontent chunk; + json_object *obj = NULL; + + if (!curl) + return NULL; + + chunk.data = malloc(1); + chunk.len = 0; + + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 0); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, cbk_curl); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); + + if (curl_easy_perform(curl) == CURLE_OK) + obj = json_tokener_parse(chunk.data); + else + fprintf(stderr, _("ERROR: Fail to connect to: %s\n"), url); + + free(chunk.data); + + return obj; +} + +struct psensor **get_remote_sensors(const char *server_url, + int values_max_length) +{ + struct psensor **sensors = NULL; + char *url; + json_object *obj; + + url = create_api_1_0_sensors_url(server_url); + + obj = get_json_object(url); + + if (obj && !is_error(obj)) { + int i; + int n = json_object_array_length(obj); + sensors = malloc((n + 1) * sizeof(struct psensor *)); + + for (i = 0; i < n; i++) { + struct psensor *s = json_object_to_psensor + (json_object_array_get_idx(obj, i), + url, + values_max_length); + sensors[i] = s; + } + + sensors[n] = NULL; + + json_object_put(obj); + } else { + fprintf(stderr, _("ERROR: Invalid content: %s\n"), url); + } + + free(url); + + if (!sensors) { + sensors = malloc(sizeof(struct psensor *)); + *sensors = NULL; + } + + return sensors; +} + +void remote_psensor_update(struct psensor *s) +{ + json_object *obj = get_json_object(s->url); + + if (obj && !is_error(obj)) { + json_object *om; + + om = json_object_object_get(obj, "last_measure"); + + if (!is_error(obj)) { + json_object *ov, *ot; + struct timeval tv; + + ov = json_object_object_get(om, "value"); + ot = json_object_object_get(om, "time"); + + tv.tv_sec = json_object_get_int(ot); + tv.tv_usec = 0; + + psensor_set_current_measure + (s, json_object_get_double(ov), tv);; + } + + json_object_put(obj); + } else { + printf(_("ERROR: Invalid JSON: %s\n"), s->url); + } + +} + +void remote_psensor_list_update(struct psensor **sensors) +{ + struct psensor **cur = sensors; + + while (*cur) { + struct psensor *s = *cur; + + if (s->type & SENSOR_TYPE_REMOTE) + remote_psensor_update(s); + + cur++; + } +} diff --git a/src/rsensor.h b/src/rsensor.h new file mode 100644 index 0000000..bd7a44b --- /dev/null +++ b/src/rsensor.h @@ -0,0 +1,32 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#ifndef _PSENSOR_RSENSOR_H_ +#define _PSENSOR_RSENSOR_H_ + +#include "psensor.h" + +struct psensor **get_remote_sensors(const char *server_url, + int values_max_length); + +void remote_psensor_list_update(struct psensor **sensors); + +void rsensor_init(); + +#endif diff --git a/src/server/Makefile.am b/src/server/Makefile.am new file mode 100644 index 0000000..73192bf --- /dev/null +++ b/src/server/Makefile.am @@ -0,0 +1,42 @@ +bin_PROGRAMS = psensor-server +psensor_server_SOURCES = server.c server.h + +AM_CPPFLAGS = -pedantic -Werror -DDEFAULT_WWW_DIR=\""$(pkgdatadir)/www"\"\ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/libpsensor_json \ + $(SENSORS_CFLAGS)\ + $(JSON_CFLAGS)\ + $(LIBMICROHTTPD_CFLAGS) + +DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@ + +LIBS = \ + ../plib/libplib.a \ + ../lib/libpsensor.a \ + ../libpsensor_json/libpsensor_json.a \ + $(SENSORS_LIBS) \ + $(JSON_LIBS) \ + $(LIBMICROHTTPD_LIBS) + +if LUA +AM_CPPFLAGS += $(LUA_CFLAGS) +LIBS += ../plib/libplib_luatpl.a \ + $(LUA_LIBS) +psensor_server_SOURCES += server_lua.h server_lua.c +endif + +if GTOP +AM_CPPFLAGS += $(GTOP_CFLAGS) +LIBS += $(GTOP_LIBS) +psensor_server_SOURCES += sysinfo.h sysinfo.c +AM_LDFLAGS = -Wl,--as-needed +endif + + + +dist_man_MANS = psensor-server.1 +EXTRA_DIST = description.txt +psensor-server.1: server.c $(top_srcdir)/configure.ac + $(MAKE) $(AM_MAKEFLAGS) psensor-server$(EXEEXT) + help2man --include=description.txt -N --name="Temperature and system monitoring Web server" --output=psensor-server.1 ./psensor-server$(EXEEXT) \ No newline at end of file diff --git a/src/server/description.txt b/src/server/description.txt new file mode 100644 index 0000000..309bfed --- /dev/null +++ b/src/server/description.txt @@ -0,0 +1,33 @@ +[DESCRIPTION] + +/psensor-server is a/ + +It provides a JSON Web service which can be used by psensor(1) to +monitor remotely the hardware sensors of a computer. + +It can provide information about: + * the temperature of the motherboard and CPU sensors (using lm\-sensors). + * the temperature of the Hard Disk Drives (using hddtemp). + * the rotation speed of the fans (using lm\-sensors). + +The entry point of the JSON Web service is: +http://hostname:3131/api/1.0/sensors. + +It is also possible to connect to the psensor\-server with a browser, a +simple Web page is displaying the sensors information and the CPU +usage. + +If run in debug mode, psensor\-server can be stopped by sending an HTTP +request with the URL 'http://hostname:port/api/1.0/server/stop'. + +[WARNING] + +psensor\-server does not provide any way to restrict the connection to +the HTTP server, worst, no effort has been made against malicious HTTP +attacks. You should make the psensor\-server port available only to a +network or computer you trust by using the usual network security +tools of the system (for example, iptables(8) ). + +[SEE ALSO] + +psensor(1), sensors(1), sensors\-detect(8) diff --git a/src/server/server.c b/src/server/server.c new file mode 100644 index 0000000..673964c --- /dev/null +++ b/src/server/server.c @@ -0,0 +1,428 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ +#include +#include +#define _(str) gettext(str) + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_GTOP +#include "sysinfo.h" +#endif + +#ifdef HAVE_LUA +#include "server_lua.h" +#endif + +#include "psensor_json.h" +#include "plib/url.h" +#include "plib/plib_io.h" +#include "server.h" + +static const char *program_name; + +#define DEFAULT_PORT 3131 + +#define PAGE_NOT_FOUND (_("

\ +Page not found - Go to Main page\ +

")) + +static struct option long_options[] = { + {"version", no_argument, 0, 'v'}, + {"help", no_argument, 0, 'h'}, + {"port", required_argument, 0, 'p'}, + {"wdir", required_argument, 0, 'w'}, + {"debug", no_argument, 0, 'd'}, + {0, 0, 0, 0} +}; + +static struct server_data server_data; + +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + +static int debug; + +static int server_stop_requested; + +void print_version() +{ + printf("psensor-server %s\n", VERSION); + printf(_("Copyright (C) %s wpitchoune@gmail.com\n\ +License GPLv2: GNU GPL version 2 or later \ +\n\ +This is free software: you are free to change and redistribute it.\n\ +There is NO WARRANTY, to the extent permitted by law.\n"), + "2010-2011"); +} + +void print_help() +{ + printf(_("Usage: %s [OPTION]...\n"), program_name); + + puts(_("psensor-server is an HTTP server " + "for monitoring hardware sensors remotely.")); + + puts(""); + puts("Options:"); + puts(_("\ + -h, --help display this help and exit\n\ + -v, --version display version information and exit")); + + puts(""); + + puts(_("\ + -d,--debug run in debug mode\n\ + -p,--port=PORT webserver port\n\ + -w,--wdir=DIR directory containing webserver pages")); + + puts(""); + + printf(_("Report bugs to: %s\n"), PACKAGE_BUGREPORT); + puts(""); + printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL); +} + +/* + Returns '1' if the path denotates a Lua file, otherwise returns 0. + */ +int is_path_lua(const char *path) +{ + char *dot = rindex(path, '.'); + + if (dot && !strcasecmp(dot, ".lua")) + return 1; + + return 0; +} + +/* + Returns the file path corresponding to a given URL +*/ +char *get_path(const char *url, const char *www_dir) +{ + const char *p; + char *res; + + if (!strlen(url) || !strcmp(url, ".") || !strcmp(url, "/")) + p = "/index.lua"; + else + p = url; + + res = malloc(strlen(www_dir)+strlen(p)+1); + + strcpy(res, www_dir); + strcat(res, p); + + return res; +} + +static int +file_reader(void *cls, uint64_t pos, char *buf, int max) +{ + FILE *file = cls; + + fseek(file, pos, SEEK_SET); + return fread(buf, 1, max, file); +} + +struct MHD_Response * +create_response_api(const char *nurl, + const char *method, + unsigned int *rp_code) +{ + char *page = NULL; + + if (!strcmp(nurl, URL_BASE_API_1_0_SENSORS)) { + + page = sensors_to_json_string(server_data.sensors); + + } else if (!strncmp(nurl, URL_BASE_API_1_0_SENSORS, + strlen(URL_BASE_API_1_0_SENSORS)) + && nurl[strlen(URL_BASE_API_1_0_SENSORS)] == '/') { + + const char *sid = nurl + strlen(URL_BASE_API_1_0_SENSORS) + 1; + + struct psensor *s + = psensor_list_get_by_id(server_data.sensors, sid); + + if (s) + page = sensor_to_json_string(s); + + } else if (debug && !strcmp(nurl, URL_API_1_0_SERVER_STOP)) { + + server_stop_requested = 1; + page = strdup(_("

" + "Server stop requested

")); + } + + if (page) { + *rp_code = MHD_HTTP_OK; + + return MHD_create_response_from_data + (strlen(page), page, MHD_YES, MHD_NO); + } + + return NULL; +} + +struct MHD_Response * +create_response_lua(const char *nurl, + const char *method, + unsigned int *rp_code, + const char *fpath) +{ +#ifdef HAVE_LUA + char *page = lua_to_html_page(&server_data, fpath); + + if (page) { + *rp_code = MHD_HTTP_OK; + + return MHD_create_response_from_data + (strlen(page), page, MHD_YES, MHD_NO); + } +#endif + + return NULL; +} + +struct MHD_Response * +create_response_file(const char *nurl, + const char *method, + unsigned int *rp_code, + const char *fpath) +{ + if (is_file(fpath)) { + FILE *file = fopen(fpath, "rb"); + + if (file) { + struct stat buf; + + stat(fpath, &buf); + *rp_code = MHD_HTTP_OK; + + if (!buf.st_size) { + fclose(file); + return MHD_create_response_from_data + (0, NULL, MHD_NO, MHD_NO); + } + + return MHD_create_response_from_callback + (buf.st_size, + 32 * 1024, + &file_reader, + file, + (MHD_ContentReaderFreeCallback)&fclose); + + } + } + + return NULL; +} + +struct MHD_Response * +create_response(const char *nurl, const char *method, unsigned int *rp_code) +{ + struct MHD_Response *resp = NULL; + + if (!strncmp(nurl, URL_BASE_API_1_0, strlen(URL_BASE_API_1_0))) { + resp = create_response_api(nurl, method, rp_code); + } else { + char *fpath = get_path(nurl, server_data.www_dir); + + if (is_path_lua(fpath)) + resp = create_response_lua + (nurl, method, rp_code, fpath); + else + resp = create_response_file + (nurl, method, rp_code, fpath); + + free(fpath); + } + + if (resp) { + return resp; + } else { + char *page = strdup(PAGE_NOT_FOUND); + *rp_code = MHD_HTTP_NOT_FOUND; + + return MHD_create_response_from_data + (strlen(page), page, MHD_YES, MHD_NO); + } +} + +static int +cbk_http_request(void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, + size_t *upload_data_size, void **ptr) +{ + static int dummy; + struct MHD_Response *response; + int ret; + char *nurl; + unsigned int resp_code; + char *page = NULL; + + if (strcmp(method, "GET")) + return MHD_NO; + + if (&dummy != *ptr) { + /* The first time only the headers are valid, do not + respond in the first round... */ + *ptr = &dummy; + return MHD_YES; + } + + if (*upload_data_size) + return MHD_NO; + + *ptr = NULL; /* clear context pointer */ + + if (debug) + printf(_("HTTP Request: %s\n"), url); + + nurl = url_normalize(url); + + pthread_mutex_lock(&mutex); + response = create_response(nurl, method, &resp_code); + pthread_mutex_unlock(&mutex); + + ret = MHD_queue_response(connection, resp_code, response); + MHD_destroy_response(response); + + free(nurl); + + return ret; +} + +int main(int argc, char *argv[]) +{ + struct MHD_Daemon *d; + int port = DEFAULT_PORT; + int optc; + int cmdok = 1; + + program_name = argv[0]; + + setlocale(LC_ALL, ""); + +#if ENABLE_NLS + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); +#endif + + server_data.www_dir = DEFAULT_WWW_DIR; + + while ((optc = getopt_long(argc, argv, + "vhp:w:d", long_options, NULL)) != -1) { + switch (optc) { + case 'w': + if (optarg) + server_data.www_dir = strdup(optarg); + break; + case 'p': + if (optarg) + port = atoi(optarg); + break; + case 'h': + print_help(); + exit(EXIT_SUCCESS); + case 'v': + print_version(); + exit(EXIT_SUCCESS); + case 'd': + debug = 1; + break; + default: + cmdok = 0; + break; + } + } + + if (!cmdok || optind != argc) { + fprintf(stderr, _("Try `%s --help' for more information.\n"), + program_name); + exit(EXIT_FAILURE); + } + + if (!lmsensor_init()) { + fprintf(stderr, _("ERROR: failed to init lm-sensors\n")); + exit(EXIT_FAILURE); + } + + server_data.sensors = get_all_sensors(1); + + if (!*server_data.sensors) + fprintf(stderr, _("ERROR: no sensors detected\n")); + + d = MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION, + port, + NULL, NULL, &cbk_http_request, server_data.sensors, + MHD_OPTION_END); + if (!d) { + fprintf(stderr, _("ERROR: Fail to create web server\n")); + exit(EXIT_FAILURE); + } + + printf(_("Web server started on port: %d\n"), port); + printf(_("WWW directory: %s\n"), server_data.www_dir); + printf(_("URL: http://localhost:%d\n"), port); + + while (!server_stop_requested) { + pthread_mutex_lock(&mutex); + +#ifdef HAVE_GTOP + sysinfo_update(&server_data.psysinfo); +#endif + psensor_list_update_measures(server_data.sensors); + + pthread_mutex_unlock(&mutex); + sleep(5); + } + + MHD_stop_daemon(d); + + /* sanity cleanup for valgrind */ + psensor_list_free(server_data.sensors); + free(server_data.www_dir); + sensors_cleanup(); + +#ifdef HAVE_GTOP + sysinfo_cleanup(); +#endif + + return EXIT_SUCCESS; +} diff --git a/src/server/server.h b/src/server/server.h new file mode 100644 index 0000000..2770844 --- /dev/null +++ b/src/server/server.h @@ -0,0 +1,36 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#ifndef _PSENSOR_SERVER_H_ +#define _PSENSOR_SERVER_H_ + +#include "psensor.h" +#include "sysinfo.h" + +#define URL_BASE_API_1_0 "/api/1.0" +#define URL_BASE_API_1_0_SENSORS "/api/1.0/sensors" +#define URL_API_1_0_SERVER_STOP "/api/1.0/server/stop" + +struct server_data { + struct psensor **sensors; + struct psysinfo psysinfo; + char *www_dir; +}; + +#endif diff --git a/src/server/server_lua.c b/src/server/server_lua.c new file mode 100644 index 0000000..3d9eb38 --- /dev/null +++ b/src/server/server_lua.c @@ -0,0 +1,163 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include +#include + +#include "server_lua.h" + +#include "plib/plib_luatpl.h" + +int init_lua(lua_State *L, void *data) +{ + struct server_data *server_data = data; + struct psensor **s_cur; + struct psensor **sensors = server_data->sensors; + int i; + static float load_scale = 1 << SI_LOAD_SHIFT; + + lua_newtable(L); + +#ifdef HAVE_GTOP + lua_pushstring(L, "load"); + lua_pushnumber(L, server_data->psysinfo.cpu_rate); + lua_settable(L, -3); +#endif + + lua_pushstring(L, "uptime"); + lua_pushnumber(L, server_data->psysinfo.sysinfo.uptime); + lua_settable(L, -3); + + lua_pushstring(L, "load_1mn"); + lua_pushnumber(L, server_data->psysinfo.sysinfo.loads[0] / load_scale); + lua_settable(L, -3); + + lua_pushstring(L, "load_5mn"); + lua_pushnumber(L, server_data->psysinfo.sysinfo.loads[1] / load_scale); + lua_settable(L, -3); + + lua_pushstring(L, "load_15mn"); + lua_pushnumber(L, server_data->psysinfo.sysinfo.loads[2] / load_scale); + lua_settable(L, -3); + + lua_pushstring(L, "freeram"); + lua_pushnumber(L, server_data->psysinfo.sysinfo.freeram); + lua_settable(L, -3); + + lua_pushstring(L, "sharedram"); + lua_pushnumber(L, server_data->psysinfo.sysinfo.sharedram); + lua_settable(L, -3); + + lua_pushstring(L, "bufferram"); + lua_pushnumber(L, server_data->psysinfo.sysinfo.bufferram); + lua_settable(L, -3); + + lua_pushstring(L, "totalswap"); + lua_pushnumber(L, server_data->psysinfo.sysinfo.totalswap); + lua_settable(L, -3); + + lua_pushstring(L, "freeswap"); + lua_pushnumber(L, server_data->psysinfo.sysinfo.freeswap); + lua_settable(L, -3); + + lua_pushstring(L, "procs"); + lua_pushnumber(L, server_data->psysinfo.sysinfo.procs); + lua_settable(L, -3); + + lua_pushstring(L, "totalhigh"); + lua_pushnumber(L, server_data->psysinfo.sysinfo.totalhigh); + lua_settable(L, -3); + + lua_pushstring(L, "freehigh"); + lua_pushnumber(L, server_data->psysinfo.sysinfo.freehigh); + lua_settable(L, -3); + + lua_pushstring(L, "totalram"); + lua_pushnumber(L, server_data->psysinfo.sysinfo.totalram); + lua_settable(L, -3); + + lua_pushstring(L, "mem_unit"); + lua_pushnumber(L, server_data->psysinfo.sysinfo.mem_unit); + lua_settable(L, -3); + + + lua_setglobal(L, "sysinfo"); + + + lua_newtable(L); + + s_cur = sensors; + i = 1; + while (*s_cur) { + lua_pushnumber(L, i); + + lua_newtable(L); + + lua_pushstring(L, "name"); + lua_pushstring(L, (*s_cur)->name); + lua_settable(L, -3); + + lua_pushstring(L, "measure_last"); + lua_pushnumber(L, psensor_get_current_value(*s_cur)); + lua_settable(L, -3); + + lua_pushstring(L, "measure_min"); + lua_pushnumber(L, (*s_cur)->min); + lua_settable(L, -3); + + lua_pushstring(L, "measure_max"); + lua_pushnumber(L, (*s_cur)->max); + lua_settable(L, -3); + + lua_settable(L, -3); + + s_cur++; + i++; + } + + lua_setglobal(L, "sensors"); + + lua_pushstring(L, VERSION); + lua_setglobal(L, "psensor_version"); + + return 1; +} + +char *lua_to_html_page(struct server_data *server_data, const char *fpath) +{ + char *page = NULL; + struct luatpl_error err; + + err.message = NULL; + + page = luatpl_generate(fpath, + init_lua, + server_data, + &err); + + if (!page) { + luatpl_fprint_error(stderr, + &err, + fpath, + "outstring"); + free(err.message); + } + + return page; +} diff --git a/src/server/server_lua.h b/src/server/server_lua.h new file mode 100644 index 0000000..d3e4e9f --- /dev/null +++ b/src/server/server_lua.h @@ -0,0 +1,27 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#ifndef _PSENSOR_SERVER_LUA_H_ +#define _PSENSOR_SERVER_LUA_H_ + +#include "server.h" + +char *lua_to_html_page(struct server_data *data, const char *fpath); + +#endif diff --git a/src/server/sysinfo.c b/src/server/sysinfo.c new file mode 100644 index 0000000..cef2872 --- /dev/null +++ b/src/server/sysinfo.c @@ -0,0 +1,60 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include +#include +#include + +#include "sysinfo.h" + +static glibtop_cpu *cpu; +static float last_used; +static float last_total; + + +void sysinfo_update(struct psysinfo *info) +{ + unsigned long int used = 0; + unsigned long int dt; + + /* cpu */ + if (!cpu) + cpu = malloc(sizeof(glibtop_cpu)); + + glibtop_get_cpu(cpu); + + used = cpu->user + cpu->nice + cpu->sys; + + dt = cpu->total - last_total; + + if (dt) + info->cpu_rate = (used - last_used) / dt; + + last_used = used; + last_total = cpu->total; + + /* memory */ + sysinfo(&info->sysinfo); +} + +void sysinfo_cleanup() +{ + if (cpu) + free(cpu); +} diff --git a/src/server/sysinfo.h b/src/server/sysinfo.h new file mode 100644 index 0000000..c62ec25 --- /dev/null +++ b/src/server/sysinfo.h @@ -0,0 +1,34 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#ifndef _PSENSOR_SYSINFO_H_ +#define _PSENSOR_SYSINFO_H_ + +#include + +struct psysinfo { + float cpu_rate; + + struct sysinfo sysinfo; +}; + +void sysinfo_update(struct psysinfo *sysinfo); +void sysinfo_cleanup(); + +#endif diff --git a/src/ui.c b/src/ui.c new file mode 100644 index 0000000..5f038fd --- /dev/null +++ b/src/ui.c @@ -0,0 +1,126 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include "cfg.h" +#include "ui.h" +#include "ui_graph.h" +#include "ui_sensorlist.h" + +void on_destroy(GtkWidget *widget, gpointer data) +{ + ui_psensor_exit(); +} + +void ui_psensor_exit() +{ + gtk_main_quit(); +} + +GtkWidget *ui_window_create(struct ui_psensor * ui) +{ + GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + GdkScreen *screen; + GdkColormap *colormap; + GdkPixbuf *icon; + GtkIconTheme *icon_theme; + + gtk_window_set_default_size(GTK_WINDOW(window), 800, 200); + + gtk_window_set_title(GTK_WINDOW(window), + _("Psensor - Temperature Monitor")); + gtk_window_set_role(GTK_WINDOW(window), "psensor"); + + screen = gtk_widget_get_screen(window); + + if (ui->config->alpha_channel_enabled + && gdk_screen_is_composited(screen)) { + + colormap = gdk_screen_get_rgba_colormap(screen); + if (colormap) + gtk_widget_set_colormap(window, colormap); + else + ui->config->alpha_channel_enabled = 0; + } else { + ui->config->alpha_channel_enabled = 0; + } + + icon_theme = gtk_icon_theme_get_default(); + icon = gtk_icon_theme_load_icon(icon_theme, "psensor", 48, 0, NULL); + if (icon) + gtk_window_set_icon(GTK_WINDOW(window), icon); + else + fprintf(stderr, _("ERROR: Failed to load psensor icon.\n")); + + g_signal_connect(window, "destroy", G_CALLBACK(on_destroy), ui); + + gtk_window_set_decorated(GTK_WINDOW(window), + ui->config->window_decoration_enabled); + + gtk_window_set_keep_below(GTK_WINDOW(window), + ui->config->window_keep_below_enabled); + + return window; +} + +void ui_main_box_create(struct ui_psensor *ui) +{ + struct config *cfg; + GtkWidget *w_sensorlist; + + cfg = ui->config; + + if (ui->main_box) { + ui_sensorlist_create_widget(ui->ui_sensorlist); + + gtk_container_remove(GTK_CONTAINER(ui->main_window), + ui->main_box); + + ui->w_graph = ui_graph_create(ui); + ui->w_sensorlist = ui->ui_sensorlist->widget; + } + + if (cfg->sensorlist_position == SENSORLIST_POSITION_RIGHT + || cfg->sensorlist_position == SENSORLIST_POSITION_LEFT) + ui->main_box = gtk_hpaned_new(); + else + ui->main_box = gtk_vpaned_new(); + + w_sensorlist = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w_sensorlist), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + gtk_container_add(GTK_CONTAINER(w_sensorlist), + ui->ui_sensorlist->widget); + + gtk_container_add(GTK_CONTAINER(ui->main_window), ui->main_box); + + if (cfg->sensorlist_position == SENSORLIST_POSITION_RIGHT + || cfg->sensorlist_position == SENSORLIST_POSITION_BOTTOM) { + gtk_paned_pack1(GTK_PANED(ui->main_box), + GTK_WIDGET(ui->w_graph), TRUE, TRUE); + gtk_paned_pack2(GTK_PANED(ui->main_box), + w_sensorlist, FALSE, TRUE); + } else { + gtk_paned_pack1(GTK_PANED(ui->main_box), + w_sensorlist, FALSE, TRUE); + gtk_paned_pack2(GTK_PANED(ui->main_box), + GTK_WIDGET(ui->w_graph), TRUE, TRUE); + } + + gtk_widget_show_all(ui->main_box); +} diff --git a/src/ui.h b/src/ui.h new file mode 100644 index 0000000..24778dd --- /dev/null +++ b/src/ui.h @@ -0,0 +1,77 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#ifndef _PSENSOR_UI_H_ +#define _PSENSOR_UI_H_ + +#include "config.h" + +#include +#include + +#if defined(HAVE_APPINDICATOR) || defined(HAVE_APPINDICATOR_029) +#include +#endif + +#include "psensor.h" + +struct ui_psensor { + struct psensor **sensors; + + GtkWidget *w_graph; + + struct ui_sensorlist *ui_sensorlist; + + struct config *config; + + GtkWidget *main_window; + + GtkWidget *main_box; + + GtkWidget *w_sensorlist; + + int graph_update_interval; + + GMutex *sensors_mutex; + +#ifdef HAVE_LIBNOTIFY + /* + * Time of the last notification + */ + struct timeval *notification_last_time; +#endif + +#if defined(HAVE_APPINDICATOR) || defined(HAVE_APPINDICATOR_029) + AppIndicator *indicator; +#endif +}; + +void ui_main_box_create(struct ui_psensor *); + +/* + Must be called to terminate Psensor UI. +*/ +void ui_psensor_exit(); + +/* + Creates the main GTK window +*/ +GtkWidget *ui_window_create(struct ui_psensor * ui); + +#endif diff --git a/src/ui_appindicator.c b/src/ui_appindicator.c new file mode 100644 index 0000000..d68bc42 --- /dev/null +++ b/src/ui_appindicator.c @@ -0,0 +1,131 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include +#include +#include + +#include +#include + +#include "psensor.h" +#include "ui.h" +#include "ui_appindicator.h" +#include "ui_pref.h" + +static void cb_appindicator_show(gpointer data, + guint cb_action, + GtkWidget *item) +{ + struct ui_psensor *ui = (struct ui_psensor *)data; + + gtk_window_present(GTK_WINDOW(ui->main_window)); +} + +static void cb_appindicator_quit(gpointer data, + guint cb_action, + GtkWidget *item) +{ + ui_psensor_exit(data); +} + +static void cb_appindicator_preferences(gpointer data, + guint cb_action, + GtkWidget *item) +{ +#ifdef HAVE_APPINDICATOR_029 + gdk_threads_enter(); +#endif + + ui_pref_dialog_run((struct ui_psensor *)data); + +#ifdef HAVE_APPINDICATOR_029 + gdk_threads_leave(); +#endif +} + +static GtkItemFactoryEntry menu_items[] = { + {"/Show", + NULL, cb_appindicator_show, 0, ""}, + {"/Preferences", + NULL, cb_appindicator_preferences, 0, ""}, + {"/sep1", + NULL, NULL, 0, ""}, + {"/Quit", + "", cb_appindicator_quit, 0, "", GTK_STOCK_QUIT}, +}; + +static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]); +GtkWidget *ui_appindicator_get_menu(struct ui_psensor *ui) +{ + GtkItemFactory *item_factory; + + item_factory = gtk_item_factory_new(GTK_TYPE_MENU, "
", NULL); + + gtk_item_factory_create_items(item_factory, + nmenu_items, menu_items, ui); + return gtk_item_factory_get_widget(item_factory, "
"); +} + +void ui_appindicator_update(struct ui_psensor *ui) +{ + struct psensor **sensor_cur = ui->sensors; + AppIndicatorStatus status; + int attention = 0; + + if (!ui->indicator) + return; + + while (*sensor_cur) { + struct psensor *s = *sensor_cur; + + if (s->alarm_enabled && s->alarm_raised) { + attention = 1; + break; + } + + sensor_cur++; + } + + status = app_indicator_get_status(ui->indicator); + + if (!attention && status == APP_INDICATOR_STATUS_ATTENTION) + app_indicator_set_status + (ui->indicator, APP_INDICATOR_STATUS_ACTIVE); + + if (attention && status == APP_INDICATOR_STATUS_ACTIVE) + app_indicator_set_status + (ui->indicator, APP_INDICATOR_STATUS_ATTENTION); +} + +void ui_appindicator_init(struct ui_psensor *ui) +{ + GtkWidget *indicatormenu; + + ui->indicator + = app_indicator_new("psensor", + "psensor", + APP_INDICATOR_CATEGORY_APPLICATION_STATUS); + + app_indicator_set_status(ui->indicator, APP_INDICATOR_STATUS_ACTIVE); + app_indicator_set_attention_icon(ui->indicator, "psensor_hot"); + + indicatormenu = ui_appindicator_get_menu(ui); + app_indicator_set_menu(ui->indicator, GTK_MENU(indicatormenu)); +} diff --git a/src/ui_appindicator.h b/src/ui_appindicator.h new file mode 100644 index 0000000..409671b --- /dev/null +++ b/src/ui_appindicator.h @@ -0,0 +1,28 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#ifndef _PSENSOR_UI_APPINDICATOR_H_ +#define _PSENSOR_UI_APPINDICATOR_H_ + +#include "ui.h" + +void ui_appindicator_init(struct ui_psensor *ui); +void ui_appindicator_update(struct ui_psensor *ui); + +#endif diff --git a/src/ui_color.c b/src/ui_color.c new file mode 100644 index 0000000..fa715a5 --- /dev/null +++ b/src/ui_color.c @@ -0,0 +1,54 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include + +#include "ui_color.h" + +int ui_change_color(const char *title, struct color *col) +{ + GdkColor color; + GtkColorSelection *colorsel; + int res; + GtkColorSelectionDialog *colordlg; + + color.red = col->red; + color.green = col->green; + color.blue = col->blue; + + colordlg = (GtkColorSelectionDialog *) + gtk_color_selection_dialog_new(title); + + colorsel = GTK_COLOR_SELECTION(colordlg->colorsel); + + gtk_color_selection_set_previous_color(colorsel, &color); + gtk_color_selection_set_current_color(colorsel, &color); + + res = gtk_dialog_run(GTK_DIALOG(colordlg)); + + if (res == GTK_RESPONSE_OK) { + gtk_color_selection_get_current_color(colorsel, &color); + + color_set(col, color.red, color.green, color.blue); + } + + gtk_widget_destroy(GTK_WIDGET(colordlg)); + + return res == GTK_RESPONSE_OK; +} diff --git a/src/ui_color.h b/src/ui_color.h new file mode 100644 index 0000000..c20a4b5 --- /dev/null +++ b/src/ui_color.h @@ -0,0 +1,32 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#ifndef _PSENSOR_UI_COLOR_H_ +#define _PSENSOR_UI_COLOR_H_ + +#include "color.h" + +/* + UI to change a given color. + + Returns 1 if the color has been modified. + */ +int ui_change_color(const char *title, struct color *col); + +#endif diff --git a/src/ui_graph.c b/src/ui_graph.c new file mode 100644 index 0000000..7c1a2c1 --- /dev/null +++ b/src/ui_graph.c @@ -0,0 +1,94 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include "graph.h" +#include "ui_graph.h" +#include "ui_pref.h" + +static void +cb_preferences(gpointer data, guint callback_action, GtkWidget *item) +{ + ui_pref_dialog_run((struct ui_psensor *)data); +} + +static GtkItemFactoryEntry menu_items[] = { + {N_("/Preferences"), + NULL, cb_preferences, 0, ""}, + + {"/sep1", + NULL, NULL, 0, ""}, + + {N_("/Quit"), + "", ui_psensor_exit, 0, "", GTK_STOCK_QUIT}, +}; + +static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]); + +GtkWidget *ui_graph_create_popupmenu(struct ui_psensor *ui) +{ + GtkItemFactory *item_factory; + + item_factory = gtk_item_factory_new(GTK_TYPE_MENU, "
", NULL); + gtk_item_factory_create_items(item_factory, + nmenu_items, menu_items, ui); + return gtk_item_factory_get_widget(item_factory, "
"); +} + +int on_graph_clicked(GtkWidget *widget, GdkEventButton *event, gpointer data) +{ + GtkWidget *menu; + + if (event->type != GDK_BUTTON_PRESS) + return FALSE; + + menu = ui_graph_create_popupmenu((struct ui_psensor *)data); + + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, + event->button, event->time); + + return TRUE; +} + +gboolean +on_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data) +{ + struct ui_psensor *ui_psensor = (struct ui_psensor *)data; + + graph_update(ui_psensor->sensors, + ui_psensor->w_graph, ui_psensor->config); + + return FALSE; +} + +GtkWidget *ui_graph_create(struct ui_psensor * ui) +{ + GtkWidget *w_graph; + + w_graph = gtk_drawing_area_new(); + + g_signal_connect(G_OBJECT(w_graph), + "expose-event", G_CALLBACK(on_expose_event), ui); + + gtk_widget_add_events(w_graph, GDK_BUTTON_PRESS_MASK); + gtk_signal_connect(GTK_OBJECT(w_graph), + "button_press_event", + (GCallback) on_graph_clicked, ui); + + return w_graph; +} diff --git a/src/ui_graph.h b/src/ui_graph.h new file mode 100644 index 0000000..4df9ef5 --- /dev/null +++ b/src/ui_graph.h @@ -0,0 +1,29 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#ifndef _PSENSOR_UI_GRAPH_H_ +#define _PSENSOR_UI_GRAPH_H_ + +#include + +#include "ui.h" + +GtkWidget *ui_graph_create(struct ui_psensor * ui); + +#endif diff --git a/src/ui_notify.c b/src/ui_notify.c new file mode 100644 index 0000000..29d9636 --- /dev/null +++ b/src/ui_notify.c @@ -0,0 +1,80 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include +#include +#include + +#include + +#include "ui.h" +#include "ui_notify.h" + +void ui_notify(struct psensor *sensor, struct ui_psensor *ui) +{ + struct timeval *t = malloc(sizeof(struct timeval)); + char *name; + NotifyNotification *notif; + + if (gettimeofday(t, NULL) != 0) { + fprintf(stderr, _("ERROR: failed gettimeofday\n")); + free(t); + + return; + } + + if (!ui->notification_last_time) { + /* first notification */ + ui->notification_last_time = t; + } else { + + if (t->tv_sec - ui->notification_last_time->tv_sec < 60) { + /* last notification less than 1mn ago */ + free(t); + return; + } else { + /* last notification more than 1mn ago */ + free(ui->notification_last_time); + ui->notification_last_time = t; + } + } + + if (notify_is_initted() == FALSE) + notify_init("psensor"); + + if (notify_is_initted() == TRUE) { + name = strdup(sensor->name); + +#ifdef NOTIFY_VERSION_MAJOR + notif = notify_notification_new(_("Temperature alert"), + name, + NULL); +#else + notif = notify_notification_new(_("Temperature alert"), + name, + NULL, + GTK_WIDGET(ui->main_window)); +#endif + + + notify_notification_show(notif, NULL); + + g_object_unref(notif); + } +} diff --git a/src/ui_notify.h b/src/ui_notify.h new file mode 100644 index 0000000..94b37db --- /dev/null +++ b/src/ui_notify.h @@ -0,0 +1,28 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#ifndef _PSENSOR_UI_NOTIFY_H_ +#define _PSENSOR_UI_NOTIFY_H_ + +#include "psensor.h" +#include "ui.h" + +void ui_notify(struct psensor *sensor, struct ui_psensor *ui); + +#endif diff --git a/src/ui_pref.c b/src/ui_pref.c new file mode 100644 index 0000000..809ee20 --- /dev/null +++ b/src/ui_pref.c @@ -0,0 +1,182 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include +#include + +#include "ui.h" +#include "cfg.h" +#include "ui_pref.h" +#include "ui_color.h" +#include "compat.h" + +GdkColor *color_to_gdkcolor(struct color *color) +{ + GdkColor *c = malloc(sizeof(GdkColor)); + + c->red = color->red; + c->green = color->green; + c->blue = color->blue; + + return c; +} + +void ui_pref_dialog_run(struct ui_psensor *ui) +{ + GtkDialog *diag; + gint result; + struct config *cfg; + GtkBuilder *builder; + guint ok; + GError *error = NULL; + GdkColor *color_fg, *color_bg; + GtkColorButton *w_color_fg, *w_color_bg; + GtkHScale *w_bg_opacity; + GtkSpinButton *w_update_interval, *w_monitoring_duration, + *w_s_update_interval; + GtkComboBox *w_sensorlist_pos; + GtkToggleButton *w_hide_window_decoration, *w_keep_window_below; + + cfg = ui->config; + + builder = gtk_builder_new(); + + ok = gtk_builder_add_from_file + (builder, + PACKAGE_DATA_DIR G_DIR_SEPARATOR_S "psensor-pref.glade", + &error); + + if (!ok) { + g_warning("%s", error->message); + g_free(error); + return ; + } + + diag = GTK_DIALOG(gtk_builder_get_object(builder, "dialog1")); + + color_fg = color_to_gdkcolor(cfg->graph_fgcolor); + w_color_fg = GTK_COLOR_BUTTON(gtk_builder_get_object(builder, + "color_fg")); + gtk_color_button_set_color(w_color_fg, color_fg); + + color_bg = color_to_gdkcolor(cfg->graph_bgcolor); + w_color_bg = GTK_COLOR_BUTTON(gtk_builder_get_object(builder, + "color_bg")); + gtk_color_button_set_color(w_color_bg, color_bg); + + w_bg_opacity = GTK_HSCALE(gtk_builder_get_object(builder, + "bg_opacity")); + gtk_range_set_value(GTK_RANGE(w_bg_opacity), cfg->graph_bg_alpha); + + w_update_interval = GTK_SPIN_BUTTON(gtk_builder_get_object + (builder, "update_interval")); + gtk_spin_button_set_value(w_update_interval, + cfg->graph_update_interval); + + w_s_update_interval + = GTK_SPIN_BUTTON(gtk_builder_get_object + (builder, "sensor_update_interval")); + gtk_spin_button_set_value(w_s_update_interval, + cfg->sensor_update_interval); + + w_monitoring_duration + = GTK_SPIN_BUTTON(gtk_builder_get_object + (builder, "monitoring_duration")); + gtk_spin_button_set_value(w_monitoring_duration, + cfg->graph_monitoring_duration); + + w_sensorlist_pos = GTK_COMBO_BOX + (gtk_builder_get_object(builder, + "sensors_list_position")); + gtk_combo_box_set_active(w_sensorlist_pos, cfg->sensorlist_position); + + w_hide_window_decoration = GTK_TOGGLE_BUTTON + (gtk_builder_get_object(builder, + "hide_window_decoration")); + gtk_toggle_button_set_active(w_hide_window_decoration, + !cfg->window_decoration_enabled); + + w_keep_window_below = GTK_TOGGLE_BUTTON + (gtk_builder_get_object(builder, + "keep_window_below")); + gtk_toggle_button_set_active(w_keep_window_below, + cfg->window_keep_below_enabled); + + result = gtk_dialog_run(diag); + + if (result == GTK_RESPONSE_ACCEPT) { + double value; + GdkColor color; + + g_mutex_lock(ui->sensors_mutex); + + gtk_color_button_get_color(w_color_fg, &color); + color_set(cfg->graph_fgcolor, + color.red, color.green, color.blue); + + gtk_color_button_get_color(w_color_bg, &color); + color_set(cfg->graph_bgcolor, + color.red, color.green, color.blue); + + value = gtk_range_get_value(GTK_RANGE(w_bg_opacity)); + cfg->graph_bg_alpha = value; + + if (value == 1.0) + cfg->alpha_channel_enabled = 0; + else + cfg->alpha_channel_enabled = 1; + + cfg->sensorlist_position + = gtk_combo_box_get_active(w_sensorlist_pos); + + cfg->window_decoration_enabled = + !gtk_toggle_button_get_active(w_hide_window_decoration); + + cfg->window_keep_below_enabled + = gtk_toggle_button_get_active(w_keep_window_below); + + gtk_window_set_decorated(GTK_WINDOW(ui->main_window), + cfg->window_decoration_enabled); + + gtk_window_set_keep_below(GTK_WINDOW(ui->main_window), + cfg->window_keep_below_enabled); + + cfg->sensor_update_interval + = gtk_spin_button_get_value_as_int(w_s_update_interval); + + cfg->graph_update_interval = gtk_spin_button_get_value_as_int + (w_update_interval); + + cfg->graph_monitoring_duration + = gtk_spin_button_get_value_as_int + (w_monitoring_duration); + + cfg->sensor_values_max_length + = (cfg->graph_monitoring_duration * 60) / + cfg->sensor_update_interval; + + config_save(cfg); + + g_mutex_unlock(ui->sensors_mutex); + + ui_main_box_create(ui); + } + g_object_unref(G_OBJECT(builder)); + gtk_widget_destroy(GTK_WIDGET(diag)); +} diff --git a/src/ui_pref.h b/src/ui_pref.h new file mode 100644 index 0000000..f3cd95f --- /dev/null +++ b/src/ui_pref.h @@ -0,0 +1,28 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#ifndef _PSENSOR_UI_PREF_H_ +#define _PSENSOR_UI_PREF_H_ + +#include "ui.h" + +void ui_pref_dialog_run(struct ui_psensor *); +GdkColor *color_to_gdkcolor(struct color *color); + +#endif diff --git a/src/ui_sensorlist.c b/src/ui_sensorlist.c new file mode 100644 index 0000000..0bade13 --- /dev/null +++ b/src/ui_sensorlist.c @@ -0,0 +1,492 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + + +#include +#include + +#include "ui.h" +#include "ui_pref.h" +#include "ui_sensorlist.h" +#include "cfg.h" +#include "ui_color.h" +#include "compat.h" + +enum { + COL_NAME = 0, + COL_TEMP, + COL_TEMP_MIN, + COL_TEMP_MAX, + COL_COLOR, + COL_COLOR_STR, + COL_ENABLED, + COL_EMPTY, + COLS_COUNT +}; + +struct cb_data { + struct ui_sensorlist *ui_sensorlist; + struct psensor *sensor; +}; + +int col_index_to_col(int idx) +{ + if (idx == 5) + return COL_ENABLED; + else if (idx > 5) + return -1; + + return idx; +} + +void ui_sensorlist_update(struct ui_sensorlist *list) +{ + GtkTreeIter iter; + GtkTreeModel *model + = gtk_tree_view_get_model(GTK_TREE_VIEW(list->widget)); + gboolean valid = gtk_tree_model_get_iter_first(model, &iter); + struct psensor **sensor = list->sensors; + + while (valid && *sensor) { + struct psensor *s = *sensor; + + char *str; + + str = psensor_value_to_string(s->type, + s->measures[s->values_max_length - + 1].value); + gtk_list_store_set(GTK_LIST_STORE(model), &iter, COL_TEMP, str, + -1); + free(str); + + str = psensor_value_to_string(s->type, s->min); + gtk_list_store_set(GTK_LIST_STORE(model), &iter, + COL_TEMP_MIN, str, -1); + free(str); + + str = psensor_value_to_string(s->type, s->max); + gtk_list_store_set(GTK_LIST_STORE(model), &iter, + COL_TEMP_MAX, str, -1); + free(str); + + valid = gtk_tree_model_iter_next(model, &iter); + sensor++; + } +} + +/* + * Returns the sensor corresponding to the x/y position + * in the table. + * + * if none. + */ +struct psensor *ui_sensorlist_get_sensor_at_pos(GtkTreeView * view, + int x, + int y, struct psensor **sensors) +{ + GtkTreePath *path; + + gtk_tree_view_get_path_at_pos(view, x, y, &path, NULL, NULL, NULL); + + if (path) { + gint *i = gtk_tree_path_get_indices(path); + if (i) + return *(sensors + *i); + } + return NULL; +} + +/* + * Returns the index of the column corresponding + * to the x position in the table. + * + * -1 if none + */ +int ui_sensorlist_get_col_index_at_pos(GtkTreeView *view, int x) +{ + GList *columns = gtk_tree_view_get_columns(view); + GList *node; + int colx = 0; + int coli = 0; + + for (node = columns; node; node = node->next) { + GtkTreeViewColumn *checkcol = (GtkTreeViewColumn *) node->data; + + if (x >= colx && x < (colx + checkcol->width)) + return coli; + else + colx += checkcol->width; + + coli++; + } + + return -1; +} + +void ui_sensorlist_update_sensors_preferences(struct ui_sensorlist *list) +{ + GtkTreeIter iter; + GtkTreeModel *model + = gtk_tree_view_get_model(GTK_TREE_VIEW(list->widget)); + gboolean valid = gtk_tree_model_get_iter_first(model, &iter); + struct psensor **sensor = list->sensors; + + while (valid && *sensor) { + GdkColor color; + gchar *scolor; + + color.red = (*sensor)->color->red; + color.green = (*sensor)->color->green; + color.blue = (*sensor)->color->blue; + + scolor = gdk_color_to_string(&color); + + gtk_list_store_set(GTK_LIST_STORE(model), + &iter, COL_NAME, (*sensor)->name, -1); + + gtk_list_store_set(GTK_LIST_STORE(model), + &iter, COL_COLOR_STR, scolor, -1); + + gtk_list_store_set(GTK_LIST_STORE(model), + &iter, COL_ENABLED, (*sensor)->enabled, -1); + + free(scolor); + + valid = gtk_tree_model_iter_next(model, &iter); + sensor++; + } +} + +static void cb_sensor_settings_activated(GtkWidget *menu_item, gpointer data) +{ + struct cb_data *cb_data = data; + struct psensor *sensor = cb_data->sensor; + GtkDialog *diag; + gint result; + GtkBuilder *builder; + GError *error = NULL; + GtkLabel *w_id, *w_type; + GtkEntry *w_name; + GtkToggleButton *w_draw, *w_alarm; + GtkColorButton *w_color; + GtkSpinButton *w_temp_limit; + GdkColor *color; + guint ok; + + builder = gtk_builder_new(); + + ok = gtk_builder_add_from_file + (builder, + PACKAGE_DATA_DIR G_DIR_SEPARATOR_S "sensor-edit.glade", + &error); + + if (!ok) { + g_warning("%s", error->message); + g_free(error); + return ; + } + + w_id = GTK_LABEL(gtk_builder_get_object(builder, "sensor_id")); + gtk_label_set_text(w_id, sensor->id); + + w_type = GTK_LABEL(gtk_builder_get_object(builder, "sensor_type")); + gtk_label_set_text(w_type, psensor_type_to_str(sensor->type)); + + w_name = GTK_ENTRY(gtk_builder_get_object(builder, "sensor_name")); + gtk_entry_set_text(w_name, sensor->name); + + w_draw = GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, + "sensor_draw")); + gtk_toggle_button_set_active(w_draw, sensor->enabled); + + color = color_to_gdkcolor(sensor->color); + w_color = GTK_COLOR_BUTTON(gtk_builder_get_object(builder, + "sensor_color")); + gtk_color_button_set_color(w_color, color); + + w_alarm = GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, + "sensor_alarm")); + w_temp_limit + = GTK_SPIN_BUTTON(gtk_builder_get_object(builder, + "sensor_temp_limit")); + + if (is_temp_type(sensor->type)) { + gtk_toggle_button_set_active(w_alarm, sensor->alarm_enabled); + gtk_spin_button_set_value(w_temp_limit, sensor->alarm_limit); + } else { + gtk_widget_set_sensitive(GTK_WIDGET(w_alarm), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(w_temp_limit), FALSE); + } + + diag = GTK_DIALOG(gtk_builder_get_object(builder, "dialog1")); + result = gtk_dialog_run(diag); + + if (result == GTK_RESPONSE_ACCEPT) { + + free(sensor->name); + sensor->name = strdup(gtk_entry_get_text(w_name)); + config_set_sensor_name(sensor->id, sensor->name); + + sensor->enabled = gtk_toggle_button_get_active(w_draw); + config_set_sensor_enabled(sensor->id, sensor->enabled); + + sensor->alarm_limit = gtk_spin_button_get_value(w_temp_limit); + config_set_sensor_alarm_limit(sensor->id, sensor->alarm_limit); + + sensor->alarm_enabled = gtk_toggle_button_get_active(w_alarm); + config_set_sensor_alarm_enabled(sensor->id, + sensor->alarm_enabled); + + gtk_color_button_get_color(w_color, color); + color_set(sensor->color, color->red, color->green, color->blue); + config_set_sensor_color(sensor->id, sensor->color); + + ui_sensorlist_update_sensors_preferences + (cb_data->ui_sensorlist); + } + + g_object_unref(G_OBJECT(builder)); + + gtk_widget_destroy(GTK_WIDGET(diag)); +} + +GtkWidget *create_sensor_popup(struct ui_sensorlist *ui_sensorlist, + struct psensor *sensor) +{ + GtkWidget *menu; + GtkWidget *item; + GtkWidget *separator; + struct cb_data *data; + + menu = gtk_menu_new(); + + item = gtk_menu_item_new_with_label(sensor->name); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + + separator = gtk_separator_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), separator); + + item = gtk_menu_item_new_with_label(_("Preferences")); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + + data = malloc(sizeof(struct cb_data)); + data->ui_sensorlist = ui_sensorlist; + data->sensor = sensor; + + g_signal_connect(item, + "activate", + G_CALLBACK(cb_sensor_settings_activated), data); + + gtk_widget_show_all(menu); + + return menu; +} + +int +cb_sensor_line_clicked(GtkWidget *widget, + GdkEventButton *event, gpointer data) +{ + struct ui_sensorlist *list = (struct ui_sensorlist *)data; + GtkTreeView *view = GTK_TREE_VIEW(list->widget); + + struct psensor *sensor = ui_sensorlist_get_sensor_at_pos(view, + event->x, + event->y, + list->sensors); + + if (sensor) { + int coli = col_index_to_col(ui_sensorlist_get_col_index_at_pos + (view, event->x)); + + if (coli == COL_COLOR) { + if (ui_change_color(_("Select foreground color"), + sensor->color)) { + ui_sensorlist_update_sensors_preferences(list); + config_set_sensor_color(sensor->id, + sensor->color); + } + } else if (coli >= 0 && coli != COL_ENABLED) { + GtkWidget *menu = create_sensor_popup(list, + sensor); + + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, + event->button, event->time); + + } + + } + return FALSE; +} + +void +ui_sensorlist_on_toggled(GtkCellRendererToggle *cell, + gchar *path_str, gpointer data) +{ + struct ui_sensorlist *list = (struct ui_sensorlist *)data; + GtkTreeModel *model + = gtk_tree_view_get_model(GTK_TREE_VIEW(list->widget)); + GtkTreeIter iter; + GtkTreePath *path = gtk_tree_path_new_from_string(path_str); + gboolean fixed; + gint *i; + + gtk_tree_model_get_iter(model, &iter, path); + gtk_tree_model_get(model, &iter, COL_ENABLED, &fixed, -1); + + fixed ^= 1; + + i = gtk_tree_path_get_indices(path); + if (i) { + int n = *i; + struct psensor **sensor = list->sensors; + while (n--) + sensor++; + (*sensor)->enabled = fixed; + config_set_sensor_enabled((*sensor)->id, (*sensor)->enabled); + } + + gtk_list_store_set(GTK_LIST_STORE(model), + &iter, COL_ENABLED, fixed, -1); + + gtk_tree_path_free(path); +} + +void ui_sensorlist_create_widget(struct ui_sensorlist *ui) +{ + GtkListStore *store; + GtkCellRenderer *renderer; + struct psensor **sensor_cur; + struct psensor **sensors; + + sensors = ui->sensors; + + store = gtk_list_store_new(COLS_COUNT, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_BOOLEAN, G_TYPE_STRING); + + if (ui->widget) + gtk_widget_destroy(ui->widget); + + ui->widget = + GTK_WIDGET(gtk_tree_view_new_with_model(GTK_TREE_MODEL(store))); + + gtk_tree_selection_set_mode(gtk_tree_view_get_selection + (GTK_TREE_VIEW(ui->widget)), + GTK_SELECTION_NONE); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(ui->widget), + -1, + _("Sensor"), + renderer, + "text", COL_NAME, NULL); + + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(ui->widget), + -1, + _("Current"), + renderer, + "text", COL_TEMP, NULL); + + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(ui->widget), + -1, + _("Min"), + renderer, + "text", COL_TEMP_MIN, NULL); + + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(ui->widget), + -1, + _("Max"), + renderer, + "text", COL_TEMP_MAX, NULL); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(ui->widget), + -1, + _("Color"), + renderer, + "text", COL_COLOR, + "background", COL_COLOR_STR, + NULL); + + g_signal_connect(ui->widget, + "button-press-event", + (GCallback) cb_sensor_line_clicked, ui); + + renderer = gtk_cell_renderer_toggle_new(); + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(ui->widget), + -1, + _("Enabled"), + renderer, + "active", COL_ENABLED, + NULL); + g_signal_connect(G_OBJECT(renderer), "toggled", + (GCallback) ui_sensorlist_on_toggled, ui); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(ui->widget), + -1, + "", + renderer, + "text", COL_EMPTY, NULL); + + sensor_cur = sensors; + while (*sensor_cur) { + GtkTreeIter iter; + GdkColor color; + gchar *scolor; + + color.red = (*sensor_cur)->color->red; + color.green = (*sensor_cur)->color->green; + color.blue = (*sensor_cur)->color->blue; + + scolor = gdk_color_to_string(&color); + + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, + COL_NAME, (*sensor_cur)->name, + COL_TEMP, _("N/A"), + COL_TEMP_MIN, _("N/A"), + COL_TEMP_MAX, _("N/A"), + COL_COLOR_STR, scolor, + COL_ENABLED, (*sensor_cur)->enabled, -1); + + free(scolor); + + sensor_cur++; + } +} + +struct ui_sensorlist *ui_sensorlist_create(struct psensor **sensors) +{ + struct ui_sensorlist *list; + + list = malloc(sizeof(struct ui_sensorlist)); + list->sensors = sensors; + list->widget = NULL; + + ui_sensorlist_create_widget(list); + + return list; + +} diff --git a/src/ui_sensorlist.h b/src/ui_sensorlist.h new file mode 100644 index 0000000..421550e --- /dev/null +++ b/src/ui_sensorlist.h @@ -0,0 +1,42 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#ifndef _PSENSOR_UI_SENSORLIST_H_ +#define _PSENSOR_UI_SENSORLIST_H_ + +#include + +#include "psensor.h" + +struct ui_sensorlist { + GtkWidget *widget; + + struct psensor **sensors; +}; + +struct ui_sensorlist *ui_sensorlist_create(struct psensor **); + +/* Update values current/min/max */ +void ui_sensorlist_update(struct ui_sensorlist *list); + +void ui_sensorlist_update_sensors_preferences(struct ui_sensorlist *); + +void ui_sensorlist_create_widget(struct ui_sensorlist *ui); + +#endif diff --git a/src/unity/Makefile.am b/src/unity/Makefile.am new file mode 100644 index 0000000..2e7c8ea --- /dev/null +++ b/src/unity/Makefile.am @@ -0,0 +1,14 @@ +if UNITY +noinst_LIBRARIES = libpsensor_unity.a + +# no -pedantic because not supported by dee.h +libpsensor_unity_a_CFLAGS = -Wall -Werror + +libpsensor_unity_a_SOURCES = \ + ui_unity.h ui_unity.c + +AM_CPPFLAGS = $(UNITY_CFLAGS) \ + -I$(top_srcdir)/src/lib + +LIBS += $(UNITY_LIBS) +endif \ No newline at end of file diff --git a/src/unity/ui_unity.c b/src/unity/ui_unity.c new file mode 100644 index 0000000..aff0b7a --- /dev/null +++ b/src/unity/ui_unity.c @@ -0,0 +1,43 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include + +#include "psensor.h" + +static int initialized; +static UnityLauncherEntry *psensor_entry; + +void ui_unity_launcher_entry_update(struct psensor **sensors) +{ + if (!initialized) { + psensor_entry = unity_launcher_entry_get_for_desktop_file + ("psensor.desktop"); + unity_launcher_entry_set_count_visible(psensor_entry, TRUE); + unity_launcher_entry_set_count(psensor_entry, 0); + initialized = 1; + } + + if (sensors && *sensors) { + struct psensor *s = *sensors; + double v = psensor_get_current_value(s); + + unity_launcher_entry_set_count(psensor_entry, v); + } +} diff --git a/src/unity/ui_unity.h b/src/unity/ui_unity.h new file mode 100644 index 0000000..f4ddddc --- /dev/null +++ b/src/unity/ui_unity.h @@ -0,0 +1,27 @@ +/* + Copyright (C) 2010-2011 wpitchoune@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#ifndef _PSENSOR_UI_UNITY_H_ +#define _PSENSOR_UI_UNITY_H_ + +#include "psensor.h" + +void ui_unity_launcher_entry_update(struct psensor **sensors); + +#endif diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..1acf104 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,5 @@ +check-local: checkpatch.pl + find $(top_srcdir)/src -name \*.c -exec $(srcdir)/checkpatch.pl -q --no-tree -emacs -f {} \; + find $(top_srcdir)/src -name \*.h -exec $(srcdir)/checkpatch.pl -q --no-tree -emacs -f {} \; + +EXTRA_DIST = checkpatch.pl \ No newline at end of file diff --git a/tests/checkpatch.pl b/tests/checkpatch.pl new file mode 100755 index 0000000..0b0defa --- /dev/null +++ b/tests/checkpatch.pl @@ -0,0 +1,2823 @@ +#!/usr/bin/perl -w + +# This script has been copied from Linux Kernel sources. +# +# (c) 2001, Dave Jones. (the file handling bit) +# (c) 2005, Joel Schopp (the ugly bit) +# (c) 2007,2008, Andy Whitcroft (new conditions, test suite) +# (c) 2008,2009, Andy Whitcroft +# Licensed under the terms of the GNU GPL License version 2 + +use strict; + +my $P = $0; +$P =~ s@.*/@@g; + +my $V = '0.30'; + +use Getopt::Long qw(:config no_auto_abbrev); + +my $quiet = 0; +my $tree = 1; +my $chk_signoff = 1; +my $chk_patch = 1; +my $tst_only; +my $emacs = 0; +my $terse = 0; +my $file = 0; +my $check = 0; +my $summary = 1; +my $mailback = 0; +my $summary_file = 0; +my $root; +my %debug; +my $help = 0; + +sub help { + my ($exitcode) = @_; + + print << "EOM"; +Usage: $P [OPTION]... [FILE]... +Version: $V + +Options: + -q, --quiet quiet + --no-tree run without a kernel tree + --no-signoff do not check for 'Signed-off-by' line + --patch treat FILE as patchfile (default) + --emacs emacs compile window format + --terse one line per report + -f, --file treat FILE as regular source file + --subjective, --strict enable more subjective tests + --root=PATH PATH to the kernel tree root + --no-summary suppress the per-file summary + --mailback only produce a report in case of warnings/errors + --summary-file include the filename in summary + --debug KEY=[0|1] turn on/off debugging of KEY, where KEY is one of + 'values', 'possible', 'type', and 'attr' (default + is all off) + --test-only=WORD report only warnings/errors containing WORD + literally + -h, --help, --version display this help and exit + +When FILE is - read standard input. +EOM + + exit($exitcode); +} + +GetOptions( + 'q|quiet+' => \$quiet, + 'tree!' => \$tree, + 'signoff!' => \$chk_signoff, + 'patch!' => \$chk_patch, + 'emacs!' => \$emacs, + 'terse!' => \$terse, + 'f|file!' => \$file, + 'subjective!' => \$check, + 'strict!' => \$check, + 'root=s' => \$root, + 'summary!' => \$summary, + 'mailback!' => \$mailback, + 'summary-file!' => \$summary_file, + + 'debug=s' => \%debug, + 'test-only=s' => \$tst_only, + 'h|help' => \$help, + 'version' => \$help +) or help(1); + +help(0) if ($help); + +my $exit = 0; + +if ($#ARGV < 0) { + print "$P: no input files\n"; + exit(1); +} + +my $dbg_values = 0; +my $dbg_possible = 0; +my $dbg_type = 0; +my $dbg_attr = 0; +for my $key (keys %debug) { + ## no critic + eval "\${dbg_$key} = '$debug{$key}';"; + die "$@" if ($@); +} + +if ($terse) { + $emacs = 1; + $quiet++; +} + +if ($tree) { + if (defined $root) { + if (!top_of_kernel_tree($root)) { + die "$P: $root: --root does not point at a valid tree\n"; + } + } else { + if (top_of_kernel_tree('.')) { + $root = '.'; + } elsif ($0 =~ m@(.*)/scripts/[^/]*$@ && + top_of_kernel_tree($1)) { + $root = $1; + } + } + + if (!defined $root) { + print "Must be run from the top-level dir. of a kernel tree\n"; + exit(2); + } +} + +my $emitted_corrupt = 0; + +our $Ident = qr{ + [A-Za-z_][A-Za-z\d_]* + (?:\s*\#\#\s*[A-Za-z_][A-Za-z\d_]*)* + }x; +our $Storage = qr{extern|static|asmlinkage}; +our $Sparse = qr{ + __user| + __kernel| + __force| + __iomem| + __must_check| + __init_refok| + __kprobes| + __ref + }x; + +# Notes to $Attribute: +# We need \b after 'init' otherwise 'initconst' will cause a false positive in a check +our $Attribute = qr{ + const| + __read_mostly| + __kprobes| + __(?:mem|cpu|dev|)(?:initdata|initconst|init\b)| + ____cacheline_aligned| + ____cacheline_aligned_in_smp| + ____cacheline_internodealigned_in_smp| + __weak + }x; +our $Modifier; +our $Inline = qr{inline|__always_inline|noinline}; +our $Member = qr{->$Ident|\.$Ident|\[[^]]*\]}; +our $Lval = qr{$Ident(?:$Member)*}; + +our $Constant = qr{(?:[0-9]+|0x[0-9a-fA-F]+)[UL]*}; +our $Assignment = qr{(?:\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=)}; +our $Compare = qr{<=|>=|==|!=|<|>}; +our $Operators = qr{ + <=|>=|==|!=| + =>|->|<<|>>|<|>|!|~| + &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|% + }x; + +our $NonptrType; +our $Type; +our $Declare; + +our $UTF8 = qr { + [\x09\x0A\x0D\x20-\x7E] # ASCII + | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte + | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs + | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte + | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates + | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 + | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 + | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 +}x; + +our $typeTypedefs = qr{(?x: + (?:__)?(?:u|s|be|le)(?:8|16|32|64)| + atomic_t +)}; + +our $logFunctions = qr{(?x: + printk| + pr_(debug|dbg|vdbg|devel|info|warning|err|notice|alert|crit|emerg|cont)| + dev_(printk|dbg|vdbg|info|warn|err|notice|alert|crit|emerg|WARN)| + WARN| + panic +)}; + +our @typeList = ( + qr{void}, + qr{(?:unsigned\s+)?char}, + qr{(?:unsigned\s+)?short}, + qr{(?:unsigned\s+)?int}, + qr{(?:unsigned\s+)?long}, + qr{(?:unsigned\s+)?long\s+int}, + qr{(?:unsigned\s+)?long\s+long}, + qr{(?:unsigned\s+)?long\s+long\s+int}, + qr{unsigned}, + qr{float}, + qr{double}, + qr{bool}, + qr{struct\s+$Ident}, + qr{union\s+$Ident}, + qr{enum\s+$Ident}, + qr{${Ident}_t}, + qr{${Ident}_handler}, + qr{${Ident}_handler_fn}, +); +our @modifierList = ( + qr{fastcall}, +); + +sub build_types { + my $mods = "(?x: \n" . join("|\n ", @modifierList) . "\n)"; + my $all = "(?x: \n" . join("|\n ", @typeList) . "\n)"; + $Modifier = qr{(?:$Attribute|$Sparse|$mods)}; + $NonptrType = qr{ + (?:$Modifier\s+|const\s+)* + (?: + (?:typeof|__typeof__)\s*\(\s*\**\s*$Ident\s*\)| + (?:$typeTypedefs\b)| + (?:${all}\b) + ) + (?:\s+$Modifier|\s+const)* + }x; + $Type = qr{ + $NonptrType + (?:[\s\*]+\s*const|[\s\*]+|(?:\s*\[\s*\])+)? + (?:\s+$Inline|\s+$Modifier)* + }x; + $Declare = qr{(?:$Storage\s+)?$Type}; +} +build_types(); + +$chk_signoff = 0 if ($file); + +my @dep_includes = (); +my @dep_functions = (); +my $removal = "Documentation/feature-removal-schedule.txt"; +if ($tree && -f "$root/$removal") { + open(my $REMOVE, '<', "$root/$removal") || + die "$P: $removal: open failed - $!\n"; + while (<$REMOVE>) { + if (/^Check:\s+(.*\S)/) { + for my $entry (split(/[, ]+/, $1)) { + if ($entry =~ m@include/(.*)@) { + push(@dep_includes, $1); + + } elsif ($entry !~ m@/@) { + push(@dep_functions, $entry); + } + } + } + } + close($REMOVE); +} + +my @rawlines = (); +my @lines = (); +my $vname; +for my $filename (@ARGV) { + my $FILE; + if ($file) { + open($FILE, '-|', "diff -u /dev/null $filename") || + die "$P: $filename: diff failed - $!\n"; + } elsif ($filename eq '-') { + open($FILE, '<&STDIN'); + } else { + open($FILE, '<', "$filename") || + die "$P: $filename: open failed - $!\n"; + } + if ($filename eq '-') { + $vname = 'Your patch'; + } else { + $vname = $filename; + } + while (<$FILE>) { + chomp; + push(@rawlines, $_); + } + close($FILE); + if (!process($filename)) { + $exit = 1; + } + @rawlines = (); + @lines = (); +} + +exit($exit); + +sub top_of_kernel_tree { + my ($root) = @_; + + my @tree_check = ( + "COPYING", "CREDITS", "Kbuild", "MAINTAINERS", "Makefile", + "README", "Documentation", "arch", "include", "drivers", + "fs", "init", "ipc", "kernel", "lib", "scripts", + ); + + foreach my $check (@tree_check) { + if (! -e $root . '/' . $check) { + return 0; + } + } + return 1; +} + +sub expand_tabs { + my ($str) = @_; + + my $res = ''; + my $n = 0; + for my $c (split(//, $str)) { + if ($c eq "\t") { + $res .= ' '; + $n++; + for (; ($n % 8) != 0; $n++) { + $res .= ' '; + } + next; + } + $res .= $c; + $n++; + } + + return $res; +} +sub copy_spacing { + (my $res = shift) =~ tr/\t/ /c; + return $res; +} + +sub line_stats { + my ($line) = @_; + + # Drop the diff line leader and expand tabs + $line =~ s/^.//; + $line = expand_tabs($line); + + # Pick the indent from the front of the line. + my ($white) = ($line =~ /^(\s*)/); + + return (length($line), length($white)); +} + +my $sanitise_quote = ''; + +sub sanitise_line_reset { + my ($in_comment) = @_; + + if ($in_comment) { + $sanitise_quote = '*/'; + } else { + $sanitise_quote = ''; + } +} +sub sanitise_line { + my ($line) = @_; + + my $res = ''; + my $l = ''; + + my $qlen = 0; + my $off = 0; + my $c; + + # Always copy over the diff marker. + $res = substr($line, 0, 1); + + for ($off = 1; $off < length($line); $off++) { + $c = substr($line, $off, 1); + + # Comments we are wacking completly including the begin + # and end, all to $;. + if ($sanitise_quote eq '' && substr($line, $off, 2) eq '/*') { + $sanitise_quote = '*/'; + + substr($res, $off, 2, "$;$;"); + $off++; + next; + } + if ($sanitise_quote eq '*/' && substr($line, $off, 2) eq '*/') { + $sanitise_quote = ''; + substr($res, $off, 2, "$;$;"); + $off++; + next; + } + if ($sanitise_quote eq '' && substr($line, $off, 2) eq '//') { + $sanitise_quote = '//'; + + substr($res, $off, 2, $sanitise_quote); + $off++; + next; + } + + # A \ in a string means ignore the next character. + if (($sanitise_quote eq "'" || $sanitise_quote eq '"') && + $c eq "\\") { + substr($res, $off, 2, 'XX'); + $off++; + next; + } + # Regular quotes. + if ($c eq "'" || $c eq '"') { + if ($sanitise_quote eq '') { + $sanitise_quote = $c; + + substr($res, $off, 1, $c); + next; + } elsif ($sanitise_quote eq $c) { + $sanitise_quote = ''; + } + } + + #print "c<$c> SQ<$sanitise_quote>\n"; + if ($off != 0 && $sanitise_quote eq '*/' && $c ne "\t") { + substr($res, $off, 1, $;); + } elsif ($off != 0 && $sanitise_quote eq '//' && $c ne "\t") { + substr($res, $off, 1, $;); + } elsif ($off != 0 && $sanitise_quote && $c ne "\t") { + substr($res, $off, 1, 'X'); + } else { + substr($res, $off, 1, $c); + } + } + + if ($sanitise_quote eq '//') { + $sanitise_quote = ''; + } + + # The pathname on a #include may be surrounded by '<' and '>'. + if ($res =~ /^.\s*\#\s*include\s+\<(.*)\>/) { + my $clean = 'X' x length($1); + $res =~ s@\<.*\>@<$clean>@; + + # The whole of a #error is a string. + } elsif ($res =~ /^.\s*\#\s*(?:error|warning)\s+(.*)\b/) { + my $clean = 'X' x length($1); + $res =~ s@(\#\s*(?:error|warning)\s+).*@$1$clean@; + } + + return $res; +} + +sub ctx_statement_block { + my ($linenr, $remain, $off) = @_; + my $line = $linenr - 1; + my $blk = ''; + my $soff = $off; + my $coff = $off - 1; + my $coff_set = 0; + + my $loff = 0; + + my $type = ''; + my $level = 0; + my @stack = (); + my $p; + my $c; + my $len = 0; + + my $remainder; + while (1) { + @stack = (['', 0]) if ($#stack == -1); + + #warn "CSB: blk<$blk> remain<$remain>\n"; + # If we are about to drop off the end, pull in more + # context. + if ($off >= $len) { + for (; $remain > 0; $line++) { + last if (!defined $lines[$line]); + next if ($lines[$line] =~ /^-/); + $remain--; + $loff = $len; + $blk .= $lines[$line] . "\n"; + $len = length($blk); + $line++; + last; + } + # Bail if there is no further context. + #warn "CSB: blk<$blk> off<$off> len<$len>\n"; + if ($off >= $len) { + last; + } + } + $p = $c; + $c = substr($blk, $off, 1); + $remainder = substr($blk, $off); + + #warn "CSB: c<$c> type<$type> level<$level> remainder<$remainder> coff_set<$coff_set>\n"; + + # Handle nested #if/#else. + if ($remainder =~ /^#\s*(?:ifndef|ifdef|if)\s/) { + push(@stack, [ $type, $level ]); + } elsif ($remainder =~ /^#\s*(?:else|elif)\b/) { + ($type, $level) = @{$stack[$#stack - 1]}; + } elsif ($remainder =~ /^#\s*endif\b/) { + ($type, $level) = @{pop(@stack)}; + } + + # Statement ends at the ';' or a close '}' at the + # outermost level. + if ($level == 0 && $c eq ';') { + last; + } + + # An else is really a conditional as long as its not else if + if ($level == 0 && $coff_set == 0 && + (!defined($p) || $p =~ /(?:\s|\}|\+)/) && + $remainder =~ /^(else)(?:\s|{)/ && + $remainder !~ /^else\s+if\b/) { + $coff = $off + length($1) - 1; + $coff_set = 1; + #warn "CSB: mark coff<$coff> soff<$soff> 1<$1>\n"; + #warn "[" . substr($blk, $soff, $coff - $soff + 1) . "]\n"; + } + + if (($type eq '' || $type eq '(') && $c eq '(') { + $level++; + $type = '('; + } + if ($type eq '(' && $c eq ')') { + $level--; + $type = ($level != 0)? '(' : ''; + + if ($level == 0 && $coff < $soff) { + $coff = $off; + $coff_set = 1; + #warn "CSB: mark coff<$coff>\n"; + } + } + if (($type eq '' || $type eq '{') && $c eq '{') { + $level++; + $type = '{'; + } + if ($type eq '{' && $c eq '}') { + $level--; + $type = ($level != 0)? '{' : ''; + + if ($level == 0) { + last; + } + } + $off++; + } + # We are truly at the end, so shuffle to the next line. + if ($off == $len) { + $loff = $len + 1; + $line++; + $remain--; + } + + my $statement = substr($blk, $soff, $off - $soff + 1); + my $condition = substr($blk, $soff, $coff - $soff + 1); + + #warn "STATEMENT<$statement>\n"; + #warn "CONDITION<$condition>\n"; + + #print "coff<$coff> soff<$off> loff<$loff>\n"; + + return ($statement, $condition, + $line, $remain + 1, $off - $loff + 1, $level); +} + +sub statement_lines { + my ($stmt) = @_; + + # Strip the diff line prefixes and rip blank lines at start and end. + $stmt =~ s/(^|\n)./$1/g; + $stmt =~ s/^\s*//; + $stmt =~ s/\s*$//; + + my @stmt_lines = ($stmt =~ /\n/g); + + return $#stmt_lines + 2; +} + +sub statement_rawlines { + my ($stmt) = @_; + + my @stmt_lines = ($stmt =~ /\n/g); + + return $#stmt_lines + 2; +} + +sub statement_block_size { + my ($stmt) = @_; + + $stmt =~ s/(^|\n)./$1/g; + $stmt =~ s/^\s*{//; + $stmt =~ s/}\s*$//; + $stmt =~ s/^\s*//; + $stmt =~ s/\s*$//; + + my @stmt_lines = ($stmt =~ /\n/g); + my @stmt_statements = ($stmt =~ /;/g); + + my $stmt_lines = $#stmt_lines + 2; + my $stmt_statements = $#stmt_statements + 1; + + if ($stmt_lines > $stmt_statements) { + return $stmt_lines; + } else { + return $stmt_statements; + } +} + +sub ctx_statement_full { + my ($linenr, $remain, $off) = @_; + my ($statement, $condition, $level); + + my (@chunks); + + # Grab the first conditional/block pair. + ($statement, $condition, $linenr, $remain, $off, $level) = + ctx_statement_block($linenr, $remain, $off); + #print "F: c<$condition> s<$statement> remain<$remain>\n"; + push(@chunks, [ $condition, $statement ]); + if (!($remain > 0 && $condition =~ /^\s*(?:\n[+-])?\s*(?:if|else|do)\b/s)) { + return ($level, $linenr, @chunks); + } + + # Pull in the following conditional/block pairs and see if they + # could continue the statement. + for (;;) { + ($statement, $condition, $linenr, $remain, $off, $level) = + ctx_statement_block($linenr, $remain, $off); + #print "C: c<$condition> s<$statement> remain<$remain>\n"; + last if (!($remain > 0 && $condition =~ /^(?:\s*\n[+-])*\s*(?:else|do)\b/s)); + #print "C: push\n"; + push(@chunks, [ $condition, $statement ]); + } + + return ($level, $linenr, @chunks); +} + +sub ctx_block_get { + my ($linenr, $remain, $outer, $open, $close, $off) = @_; + my $line; + my $start = $linenr - 1; + my $blk = ''; + my @o; + my @c; + my @res = (); + + my $level = 0; + my @stack = ($level); + for ($line = $start; $remain > 0; $line++) { + next if ($rawlines[$line] =~ /^-/); + $remain--; + + $blk .= $rawlines[$line]; + + # Handle nested #if/#else. + if ($rawlines[$line] =~ /^.\s*#\s*(?:ifndef|ifdef|if)\s/) { + push(@stack, $level); + } elsif ($rawlines[$line] =~ /^.\s*#\s*(?:else|elif)\b/) { + $level = $stack[$#stack - 1]; + } elsif ($rawlines[$line] =~ /^.\s*#\s*endif\b/) { + $level = pop(@stack); + } + + foreach my $c (split(//, $rawlines[$line])) { + ##print "C<$c>L<$level><$open$close>O<$off>\n"; + if ($off > 0) { + $off--; + next; + } + + if ($c eq $close && $level > 0) { + $level--; + last if ($level == 0); + } elsif ($c eq $open) { + $level++; + } + } + + if (!$outer || $level <= 1) { + push(@res, $rawlines[$line]); + } + + last if ($level == 0); + } + + return ($level, @res); +} +sub ctx_block_outer { + my ($linenr, $remain) = @_; + + my ($level, @r) = ctx_block_get($linenr, $remain, 1, '{', '}', 0); + return @r; +} +sub ctx_block { + my ($linenr, $remain) = @_; + + my ($level, @r) = ctx_block_get($linenr, $remain, 0, '{', '}', 0); + return @r; +} +sub ctx_statement { + my ($linenr, $remain, $off) = @_; + + my ($level, @r) = ctx_block_get($linenr, $remain, 0, '(', ')', $off); + return @r; +} +sub ctx_block_level { + my ($linenr, $remain) = @_; + + return ctx_block_get($linenr, $remain, 0, '{', '}', 0); +} +sub ctx_statement_level { + my ($linenr, $remain, $off) = @_; + + return ctx_block_get($linenr, $remain, 0, '(', ')', $off); +} + +sub ctx_locate_comment { + my ($first_line, $end_line) = @_; + + # Catch a comment on the end of the line itself. + my ($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*(?:\\\s*)?$@); + return $current_comment if (defined $current_comment); + + # Look through the context and try and figure out if there is a + # comment. + my $in_comment = 0; + $current_comment = ''; + for (my $linenr = $first_line; $linenr < $end_line; $linenr++) { + my $line = $rawlines[$linenr - 1]; + #warn " $line\n"; + if ($linenr == $first_line and $line =~ m@^.\s*\*@) { + $in_comment = 1; + } + if ($line =~ m@/\*@) { + $in_comment = 1; + } + if (!$in_comment && $current_comment ne '') { + $current_comment = ''; + } + $current_comment .= $line . "\n" if ($in_comment); + if ($line =~ m@\*/@) { + $in_comment = 0; + } + } + + chomp($current_comment); + return($current_comment); +} +sub ctx_has_comment { + my ($first_line, $end_line) = @_; + my $cmt = ctx_locate_comment($first_line, $end_line); + + ##print "LINE: $rawlines[$end_line - 1 ]\n"; + ##print "CMMT: $cmt\n"; + + return ($cmt ne ''); +} + +sub raw_line { + my ($linenr, $cnt) = @_; + + my $offset = $linenr - 1; + $cnt++; + + my $line; + while ($cnt) { + $line = $rawlines[$offset++]; + next if (defined($line) && $line =~ /^-/); + $cnt--; + } + + return $line; +} + +sub cat_vet { + my ($vet) = @_; + my ($res, $coded); + + $res = ''; + while ($vet =~ /([^[:cntrl:]]*)([[:cntrl:]]|$)/g) { + $res .= $1; + if ($2 ne '') { + $coded = sprintf("^%c", unpack('C', $2) + 64); + $res .= $coded; + } + } + $res =~ s/$/\$/; + + return $res; +} + +my $av_preprocessor = 0; +my $av_pending; +my @av_paren_type; +my $av_pend_colon; + +sub annotate_reset { + $av_preprocessor = 0; + $av_pending = '_'; + @av_paren_type = ('E'); + $av_pend_colon = 'O'; +} + +sub annotate_values { + my ($stream, $type) = @_; + + my $res; + my $var = '_' x length($stream); + my $cur = $stream; + + print "$stream\n" if ($dbg_values > 1); + + while (length($cur)) { + @av_paren_type = ('E') if ($#av_paren_type < 0); + print " <" . join('', @av_paren_type) . + "> <$type> <$av_pending>" if ($dbg_values > 1); + if ($cur =~ /^(\s+)/o) { + print "WS($1)\n" if ($dbg_values > 1); + if ($1 =~ /\n/ && $av_preprocessor) { + $type = pop(@av_paren_type); + $av_preprocessor = 0; + } + + } elsif ($cur =~ /^($Type)\s*(?:$Ident|,|\)|\()/) { + print "DECLARE($1)\n" if ($dbg_values > 1); + $type = 'T'; + + } elsif ($cur =~ /^($Modifier)\s*/) { + print "MODIFIER($1)\n" if ($dbg_values > 1); + $type = 'T'; + + } elsif ($cur =~ /^(\#\s*define\s*$Ident)(\(?)/o) { + print "DEFINE($1,$2)\n" if ($dbg_values > 1); + $av_preprocessor = 1; + push(@av_paren_type, $type); + if ($2 ne '') { + $av_pending = 'N'; + } + $type = 'E'; + + } elsif ($cur =~ /^(\#\s*(?:undef\s*$Ident|include\b))/o) { + print "UNDEF($1)\n" if ($dbg_values > 1); + $av_preprocessor = 1; + push(@av_paren_type, $type); + + } elsif ($cur =~ /^(\#\s*(?:ifdef|ifndef|if))/o) { + print "PRE_START($1)\n" if ($dbg_values > 1); + $av_preprocessor = 1; + + push(@av_paren_type, $type); + push(@av_paren_type, $type); + $type = 'E'; + + } elsif ($cur =~ /^(\#\s*(?:else|elif))/o) { + print "PRE_RESTART($1)\n" if ($dbg_values > 1); + $av_preprocessor = 1; + + push(@av_paren_type, $av_paren_type[$#av_paren_type]); + + $type = 'E'; + + } elsif ($cur =~ /^(\#\s*(?:endif))/o) { + print "PRE_END($1)\n" if ($dbg_values > 1); + + $av_preprocessor = 1; + + # Assume all arms of the conditional end as this + # one does, and continue as if the #endif was not here. + pop(@av_paren_type); + push(@av_paren_type, $type); + $type = 'E'; + + } elsif ($cur =~ /^(\\\n)/o) { + print "PRECONT($1)\n" if ($dbg_values > 1); + + } elsif ($cur =~ /^(__attribute__)\s*\(?/o) { + print "ATTR($1)\n" if ($dbg_values > 1); + $av_pending = $type; + $type = 'N'; + + } elsif ($cur =~ /^(sizeof)\s*(\()?/o) { + print "SIZEOF($1)\n" if ($dbg_values > 1); + if (defined $2) { + $av_pending = 'V'; + } + $type = 'N'; + + } elsif ($cur =~ /^(if|while|for)\b/o) { + print "COND($1)\n" if ($dbg_values > 1); + $av_pending = 'E'; + $type = 'N'; + + } elsif ($cur =~/^(case)/o) { + print "CASE($1)\n" if ($dbg_values > 1); + $av_pend_colon = 'C'; + $type = 'N'; + + } elsif ($cur =~/^(return|else|goto|typeof|__typeof__)\b/o) { + print "KEYWORD($1)\n" if ($dbg_values > 1); + $type = 'N'; + + } elsif ($cur =~ /^(\()/o) { + print "PAREN('$1')\n" if ($dbg_values > 1); + push(@av_paren_type, $av_pending); + $av_pending = '_'; + $type = 'N'; + + } elsif ($cur =~ /^(\))/o) { + my $new_type = pop(@av_paren_type); + if ($new_type ne '_') { + $type = $new_type; + print "PAREN('$1') -> $type\n" + if ($dbg_values > 1); + } else { + print "PAREN('$1')\n" if ($dbg_values > 1); + } + + } elsif ($cur =~ /^($Ident)\s*\(/o) { + print "FUNC($1)\n" if ($dbg_values > 1); + $type = 'V'; + $av_pending = 'V'; + + } elsif ($cur =~ /^($Ident\s*):(?:\s*\d+\s*(,|=|;))?/) { + if (defined $2 && $type eq 'C' || $type eq 'T') { + $av_pend_colon = 'B'; + } elsif ($type eq 'E') { + $av_pend_colon = 'L'; + } + print "IDENT_COLON($1,$type>$av_pend_colon)\n" if ($dbg_values > 1); + $type = 'V'; + + } elsif ($cur =~ /^($Ident|$Constant)/o) { + print "IDENT($1)\n" if ($dbg_values > 1); + $type = 'V'; + + } elsif ($cur =~ /^($Assignment)/o) { + print "ASSIGN($1)\n" if ($dbg_values > 1); + $type = 'N'; + + } elsif ($cur =~/^(;|{|})/) { + print "END($1)\n" if ($dbg_values > 1); + $type = 'E'; + $av_pend_colon = 'O'; + + } elsif ($cur =~/^(,)/) { + print "COMMA($1)\n" if ($dbg_values > 1); + $type = 'C'; + + } elsif ($cur =~ /^(\?)/o) { + print "QUESTION($1)\n" if ($dbg_values > 1); + $type = 'N'; + + } elsif ($cur =~ /^(:)/o) { + print "COLON($1,$av_pend_colon)\n" if ($dbg_values > 1); + + substr($var, length($res), 1, $av_pend_colon); + if ($av_pend_colon eq 'C' || $av_pend_colon eq 'L') { + $type = 'E'; + } else { + $type = 'N'; + } + $av_pend_colon = 'O'; + + } elsif ($cur =~ /^(\[)/o) { + print "CLOSE($1)\n" if ($dbg_values > 1); + $type = 'N'; + + } elsif ($cur =~ /^(-(?![->])|\+(?!\+)|\*|\&\&|\&)/o) { + my $variant; + + print "OPV($1)\n" if ($dbg_values > 1); + if ($type eq 'V') { + $variant = 'B'; + } else { + $variant = 'U'; + } + + substr($var, length($res), 1, $variant); + $type = 'N'; + + } elsif ($cur =~ /^($Operators)/o) { + print "OP($1)\n" if ($dbg_values > 1); + if ($1 ne '++' && $1 ne '--') { + $type = 'N'; + } + + } elsif ($cur =~ /(^.)/o) { + print "C($1)\n" if ($dbg_values > 1); + } + if (defined $1) { + $cur = substr($cur, length($1)); + $res .= $type x length($1); + } + } + + return ($res, $var); +} + +sub possible { + my ($possible, $line) = @_; + my $notPermitted = qr{(?: + ^(?: + $Modifier| + $Storage| + $Type| + DEFINE_\S+ + )$| + ^(?: + goto| + return| + case| + else| + asm|__asm__| + do + )(?:\s|$)| + ^(?:typedef|struct|enum)\b + )}x; + warn "CHECK<$possible> ($line)\n" if ($dbg_possible > 2); + if ($possible !~ $notPermitted) { + # Check for modifiers. + $possible =~ s/\s*$Storage\s*//g; + $possible =~ s/\s*$Sparse\s*//g; + if ($possible =~ /^\s*$/) { + + } elsif ($possible =~ /\s/) { + $possible =~ s/\s*$Type\s*//g; + for my $modifier (split(' ', $possible)) { + if ($modifier !~ $notPermitted) { + warn "MODIFIER: $modifier ($possible) ($line)\n" if ($dbg_possible); + push(@modifierList, $modifier); + } + } + + } else { + warn "POSSIBLE: $possible ($line)\n" if ($dbg_possible); + push(@typeList, $possible); + } + build_types(); + } else { + warn "NOTPOSS: $possible ($line)\n" if ($dbg_possible > 1); + } +} + +my $prefix = ''; + +sub report { + if (defined $tst_only && $_[0] !~ /\Q$tst_only\E/) { + return 0; + } + my $line = $prefix . $_[0]; + + $line = (split('\n', $line))[0] . "\n" if ($terse); + + push(our @report, $line); + + return 1; +} +sub report_dump { + our @report; +} +sub ERROR { + if (report("ERROR: $_[0]\n")) { + our $clean = 0; + our $cnt_error++; + } +} +sub WARN { + if (report("WARNING: $_[0]\n")) { + our $clean = 0; + our $cnt_warn++; + } +} +sub CHK { + if ($check && report("CHECK: $_[0]\n")) { + our $clean = 0; + our $cnt_chk++; + } +} + +sub check_absolute_file { + my ($absolute, $herecurr) = @_; + my $file = $absolute; + + ##print "absolute<$absolute>\n"; + + # See if any suffix of this path is a path within the tree. + while ($file =~ s@^[^/]*/@@) { + if (-f "$root/$file") { + ##print "file<$file>\n"; + last; + } + } + if (! -f _) { + return 0; + } + + # It is, so see if the prefix is acceptable. + my $prefix = $absolute; + substr($prefix, -length($file)) = ''; + + ##print "prefix<$prefix>\n"; + if ($prefix ne ".../") { + WARN("use relative pathname instead of absolute in changelog text\n" . $herecurr); + } +} + +sub process { + my $filename = shift; + + my $linenr=0; + my $prevline=""; + my $prevrawline=""; + my $stashline=""; + my $stashrawline=""; + + my $length; + my $indent; + my $previndent=0; + my $stashindent=0; + + our $clean = 1; + my $signoff = 0; + my $is_patch = 0; + + our @report = (); + our $cnt_lines = 0; + our $cnt_error = 0; + our $cnt_warn = 0; + our $cnt_chk = 0; + + # Trace the real file/line as we go. + my $realfile = ''; + my $realline = 0; + my $realcnt = 0; + my $here = ''; + my $in_comment = 0; + my $comment_edge = 0; + my $first_line = 0; + my $p1_prefix = ''; + + my $prev_values = 'E'; + + # suppression flags + my %suppress_ifbraces; + my %suppress_whiletrailers; + my %suppress_export; + + # Pre-scan the patch sanitizing the lines. + # Pre-scan the patch looking for any __setup documentation. + # + my @setup_docs = (); + my $setup_docs = 0; + + sanitise_line_reset(); + my $line; + foreach my $rawline (@rawlines) { + $linenr++; + $line = $rawline; + + if ($rawline=~/^\+\+\+\s+(\S+)/) { + $setup_docs = 0; + if ($1 =~ m@Documentation/kernel-parameters.txt$@) { + $setup_docs = 1; + } + #next; + } + if ($rawline=~/^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { + $realline=$1-1; + if (defined $2) { + $realcnt=$3+1; + } else { + $realcnt=1+1; + } + $in_comment = 0; + + # Guestimate if this is a continuing comment. Run + # the context looking for a comment "edge". If this + # edge is a close comment then we must be in a comment + # at context start. + my $edge; + my $cnt = $realcnt; + for (my $ln = $linenr + 1; $cnt > 0; $ln++) { + next if (defined $rawlines[$ln - 1] && + $rawlines[$ln - 1] =~ /^-/); + $cnt--; + #print "RAW<$rawlines[$ln - 1]>\n"; + last if (!defined $rawlines[$ln - 1]); + if ($rawlines[$ln - 1] =~ m@(/\*|\*/)@ && + $rawlines[$ln - 1] !~ m@"[^"]*(?:/\*|\*/)[^"]*"@) { + ($edge) = $1; + last; + } + } + if (defined $edge && $edge eq '*/') { + $in_comment = 1; + } + + # Guestimate if this is a continuing comment. If this + # is the start of a diff block and this line starts + # ' *' then it is very likely a comment. + if (!defined $edge && + $rawlines[$linenr] =~ m@^.\s*(?:\*\*+| \*)(?:\s|$)@) + { + $in_comment = 1; + } + + ##print "COMMENT:$in_comment edge<$edge> $rawline\n"; + sanitise_line_reset($in_comment); + + } elsif ($realcnt && $rawline =~ /^(?:\+| |$)/) { + # Standardise the strings and chars within the input to + # simplify matching -- only bother with positive lines. + $line = sanitise_line($rawline); + } + push(@lines, $line); + + if ($realcnt > 1) { + $realcnt-- if ($line =~ /^(?:\+| |$)/); + } else { + $realcnt = 0; + } + + #print "==>$rawline\n"; + #print "-->$line\n"; + + if ($setup_docs && $line =~ /^\+/) { + push(@setup_docs, $line); + } + } + + $prefix = ''; + + $realcnt = 0; + $linenr = 0; + foreach my $line (@lines) { + $linenr++; + + my $rawline = $rawlines[$linenr - 1]; + +#extract the line range in the file after the patch is applied + if ($line=~/^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { + $is_patch = 1; + $first_line = $linenr + 1; + $realline=$1-1; + if (defined $2) { + $realcnt=$3+1; + } else { + $realcnt=1+1; + } + annotate_reset(); + $prev_values = 'E'; + + %suppress_ifbraces = (); + %suppress_whiletrailers = (); + %suppress_export = (); + next; + +# track the line number as we move through the hunk, note that +# new versions of GNU diff omit the leading space on completely +# blank context lines so we need to count that too. + } elsif ($line =~ /^( |\+|$)/) { + $realline++; + $realcnt-- if ($realcnt != 0); + + # Measure the line length and indent. + ($length, $indent) = line_stats($rawline); + + # Track the previous line. + ($prevline, $stashline) = ($stashline, $line); + ($previndent, $stashindent) = ($stashindent, $indent); + ($prevrawline, $stashrawline) = ($stashrawline, $rawline); + + #warn "line<$line>\n"; + + } elsif ($realcnt == 1) { + $realcnt--; + } + + my $hunk_line = ($realcnt != 0); + +#make up the handle for any error we report on this line + $prefix = "$filename:$realline: " if ($emacs && $file); + $prefix = "$filename:$linenr: " if ($emacs && !$file); + + $here = "#$linenr: " if (!$file); + $here = "#$realline: " if ($file); + + # extract the filename as it passes + if ($line=~/^\+\+\+\s+(\S+)/) { + $realfile = $1; + $realfile =~ s@^([^/]*)/@@; + + $p1_prefix = $1; + if (!$file && $tree && $p1_prefix ne '' && + -e "$root/$p1_prefix") { + WARN("patch prefix '$p1_prefix' exists, appears to be a -p0 patch\n"); + } + + if ($realfile =~ m@^include/asm/@) { + ERROR("do not modify files in include/asm, change architecture specific files in include/asm-\n" . "$here$rawline\n"); + } + next; + } + + $here .= "FILE: $realfile:$realline:" if ($realcnt != 0); + + my $hereline = "$here\n$rawline\n"; + my $herecurr = "$here\n$rawline\n"; + my $hereprev = "$here\n$prevrawline\n$rawline\n"; + + $cnt_lines++ if ($realcnt != 0); + +#check the patch for a signoff: + if ($line =~ /^\s*signed-off-by:/i) { + # This is a signoff, if ugly, so do not double report. + $signoff++; + if (!($line =~ /^\s*Signed-off-by:/)) { + WARN("Signed-off-by: is the preferred form\n" . + $herecurr); + } + if ($line =~ /^\s*signed-off-by:\S/i) { + WARN("space required after Signed-off-by:\n" . + $herecurr); + } + } + +# Check for wrappage within a valid hunk of the file + if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) { + ERROR("patch seems to be corrupt (line wrapped?)\n" . + $herecurr) if (!$emitted_corrupt++); + } + +# Check for absolute kernel paths. + if ($tree) { + while ($line =~ m{(?:^|\s)(/\S*)}g) { + my $file = $1; + + if ($file =~ m{^(.*?)(?::\d+)+:?$} && + check_absolute_file($1, $herecurr)) { + # + } else { + check_absolute_file($file, $herecurr); + } + } + } + +# UTF-8 regex found at http://www.w3.org/International/questions/qa-forms-utf-8.en.php + if (($realfile =~ /^$/ || $line =~ /^\+/) && + $rawline !~ m/^$UTF8*$/) { + my ($utf8_prefix) = ($rawline =~ /^($UTF8*)/); + + my $blank = copy_spacing($rawline); + my $ptr = substr($blank, 0, length($utf8_prefix)) . "^"; + my $hereptr = "$hereline$ptr\n"; + + ERROR("Invalid UTF-8, patch and commit message should be encoded in UTF-8\n" . $hereptr); + } + +# ignore non-hunk lines and lines being removed + next if (!$hunk_line || $line =~ /^-/); + +#trailing whitespace + if ($line =~ /^\+.*\015/) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + ERROR("DOS line endings\n" . $herevet); + + } elsif ($rawline =~ /^\+.*\S\s+$/ || $rawline =~ /^\+\s+$/) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + ERROR("trailing whitespace\n" . $herevet); + } + +# check for Kconfig help text having a real description + if ($realfile =~ /Kconfig/ && + $line =~ /\+?\s*(---)?help(---)?$/) { + my $length = 0; + for (my $l = $linenr; defined($lines[$l]); $l++) { + my $f = $lines[$l]; + $f =~ s/#.*//; + $f =~ s/^\s+//; + next if ($f =~ /^$/); + last if ($f =~ /^\s*config\s/); + $length++; + } + WARN("please write a paragraph that describes the config symbol fully\n" . $herecurr) if ($length < 4); + } + +# check we are in a valid source file if not then ignore this hunk + next if ($realfile !~ /\.(h|c|s|S|pl|sh)$/); + +#80 column limit + if ($line =~ /^\+/ && $prevrawline !~ /\/\*\*/ && + $rawline !~ /^.\s*\*\s*\@$Ident\s/ && + $line !~ /^\+\s*$logFunctions\s*\(\s*(?:KERN_\S+\s*)?"[X\t]*"\s*(?:,|\)\s*;)\s*$/ && + $length > 80) + { + WARN("line over 80 characters\n" . $herecurr); + } + +# check for spaces before a quoted newline + if ($rawline =~ /^.*\".*\s\\n/) { + WARN("unnecessary whitespace before a quoted newline\n" . $herecurr); + } + +# check for adding lines without a newline. + if ($line =~ /^\+/ && defined $lines[$linenr] && $lines[$linenr] =~ /^\\ No newline at end of file/) { + WARN("adding a line without newline at end of file\n" . $herecurr); + } + +# Blackfin: use hi/lo macros + if ($realfile =~ m@arch/blackfin/.*\.S$@) { + if ($line =~ /\.[lL][[:space:]]*=.*&[[:space:]]*0x[fF][fF][fF][fF]/) { + my $herevet = "$here\n" . cat_vet($line) . "\n"; + ERROR("use the LO() macro, not (... & 0xFFFF)\n" . $herevet); + } + if ($line =~ /\.[hH][[:space:]]*=.*>>[[:space:]]*16/) { + my $herevet = "$here\n" . cat_vet($line) . "\n"; + ERROR("use the HI() macro, not (... >> 16)\n" . $herevet); + } + } + +# check we are in a valid source file C or perl if not then ignore this hunk + next if ($realfile !~ /\.(h|c|pl)$/); + +# at the beginning of a line any tabs must come first and anything +# more than 8 must use tabs. + if ($rawline =~ /^\+\s* \t\s*\S/ || + $rawline =~ /^\+\s* \s*/) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + ERROR("code indent should use tabs where possible\n" . $herevet); + } + +# check for space before tabs. + if ($rawline =~ /^\+/ && $rawline =~ / \t/) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + WARN("please, no space before tabs\n" . $herevet); + } + +# check we are in a valid C source file if not then ignore this hunk + next if ($realfile !~ /\.(h|c)$/); + +# check for RCS/CVS revision markers + if ($rawline =~ /^\+.*\$(Revision|Log|Id)(?:\$|)/) { + WARN("CVS style keyword markers, these will _not_ be updated\n". $herecurr); + } + +# Blackfin: don't use __builtin_bfin_[cs]sync + if ($line =~ /__builtin_bfin_csync/) { + my $herevet = "$here\n" . cat_vet($line) . "\n"; + ERROR("use the CSYNC() macro in asm/blackfin.h\n" . $herevet); + } + if ($line =~ /__builtin_bfin_ssync/) { + my $herevet = "$here\n" . cat_vet($line) . "\n"; + ERROR("use the SSYNC() macro in asm/blackfin.h\n" . $herevet); + } + +# Check for potential 'bare' types + my ($stat, $cond, $line_nr_next, $remain_next, $off_next, + $realline_next); + if ($realcnt && $line =~ /.\s*\S/) { + ($stat, $cond, $line_nr_next, $remain_next, $off_next) = + ctx_statement_block($linenr, $realcnt, 0); + $stat =~ s/\n./\n /g; + $cond =~ s/\n./\n /g; + + # Find the real next line. + $realline_next = $line_nr_next; + if (defined $realline_next && + (!defined $lines[$realline_next - 1] || + substr($lines[$realline_next - 1], $off_next) =~ /^\s*$/)) { + $realline_next++; + } + + my $s = $stat; + $s =~ s/{.*$//s; + + # Ignore goto labels. + if ($s =~ /$Ident:\*$/s) { + + # Ignore functions being called + } elsif ($s =~ /^.\s*$Ident\s*\(/s) { + + } elsif ($s =~ /^.\s*else\b/s) { + + # declarations always start with types + } elsif ($prev_values eq 'E' && $s =~ /^.\s*(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?((?:\s*$Ident)+?)\b(?:\s+$Sparse)?\s*\**\s*(?:$Ident|\(\*[^\)]*\))(?:\s*$Modifier)?\s*(?:;|=|,|\()/s) { + my $type = $1; + $type =~ s/\s+/ /g; + possible($type, "A:" . $s); + + # definitions in global scope can only start with types + } elsif ($s =~ /^.(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?($Ident)\b\s*(?!:)/s) { + possible($1, "B:" . $s); + } + + # any (foo ... *) is a pointer cast, and foo is a type + while ($s =~ /\(($Ident)(?:\s+$Sparse)*[\s\*]+\s*\)/sg) { + possible($1, "C:" . $s); + } + + # Check for any sort of function declaration. + # int foo(something bar, other baz); + # void (*store_gdt)(x86_descr_ptr *); + if ($prev_values eq 'E' && $s =~ /^(.(?:typedef\s*)?(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*(?:\b$Ident|\(\*\s*$Ident\))\s*)\(/s) { + my ($name_len) = length($1); + + my $ctx = $s; + substr($ctx, 0, $name_len + 1, ''); + $ctx =~ s/\)[^\)]*$//; + + for my $arg (split(/\s*,\s*/, $ctx)) { + if ($arg =~ /^(?:const\s+)?($Ident)(?:\s+$Sparse)*\s*\**\s*(:?\b$Ident)?$/s || $arg =~ /^($Ident)$/s) { + + possible($1, "D:" . $s); + } + } + } + + } + +# +# Checks which may be anchored in the context. +# + +# Check for switch () and associated case and default +# statements should be at the same indent. + if ($line=~/\bswitch\s*\(.*\)/) { + my $err = ''; + my $sep = ''; + my @ctx = ctx_block_outer($linenr, $realcnt); + shift(@ctx); + for my $ctx (@ctx) { + my ($clen, $cindent) = line_stats($ctx); + if ($ctx =~ /^\+\s*(case\s+|default:)/ && + $indent != $cindent) { + $err .= "$sep$ctx\n"; + $sep = ''; + } else { + $sep = "[...]\n"; + } + } + if ($err ne '') { + ERROR("switch and case should be at the same indent\n$hereline$err"); + } + } + +# if/while/etc brace do not go on next line, unless defining a do while loop, +# or if that brace on the next line is for something else + if ($line =~ /(.*)\b((?:if|while|for|switch)\s*\(|do\b|else\b)/ && $line !~ /^.\s*\#/) { + my $pre_ctx = "$1$2"; + + my ($level, @ctx) = ctx_statement_level($linenr, $realcnt, 0); + my $ctx_cnt = $realcnt - $#ctx - 1; + my $ctx = join("\n", @ctx); + + my $ctx_ln = $linenr; + my $ctx_skip = $realcnt; + + while ($ctx_skip > $ctx_cnt || ($ctx_skip == $ctx_cnt && + defined $lines[$ctx_ln - 1] && + $lines[$ctx_ln - 1] =~ /^-/)) { + ##print "SKIP<$ctx_skip> CNT<$ctx_cnt>\n"; + $ctx_skip-- if (!defined $lines[$ctx_ln - 1] || $lines[$ctx_ln - 1] !~ /^-/); + $ctx_ln++; + } + + #print "realcnt<$realcnt> ctx_cnt<$ctx_cnt>\n"; + #print "pre<$pre_ctx>\nline<$line>\nctx<$ctx>\nnext<$lines[$ctx_ln - 1]>\n"; + + if ($ctx !~ /{\s*/ && defined($lines[$ctx_ln -1]) && $lines[$ctx_ln - 1] =~ /^\+\s*{/) { + ERROR("that open brace { should be on the previous line\n" . + "$here\n$ctx\n$lines[$ctx_ln - 1]\n"); + } + if ($level == 0 && $pre_ctx !~ /}\s*while\s*\($/ && + $ctx =~ /\)\s*\;\s*$/ && + defined $lines[$ctx_ln - 1]) + { + my ($nlength, $nindent) = line_stats($lines[$ctx_ln - 1]); + if ($nindent > $indent) { + WARN("trailing semicolon indicates no statements, indent implies otherwise\n" . + "$here\n$ctx\n$lines[$ctx_ln - 1]\n"); + } + } + } + +# Check relative indent for conditionals and blocks. + if ($line =~ /\b(?:(?:if|while|for)\s*\(|do\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) { + my ($s, $c) = ($stat, $cond); + + substr($s, 0, length($c), ''); + + # Make sure we remove the line prefixes as we have + # none on the first line, and are going to readd them + # where necessary. + $s =~ s/\n./\n/gs; + + # Find out how long the conditional actually is. + my @newlines = ($c =~ /\n/gs); + my $cond_lines = 1 + $#newlines; + + # We want to check the first line inside the block + # starting at the end of the conditional, so remove: + # 1) any blank line termination + # 2) any opening brace { on end of the line + # 3) any do (...) { + my $continuation = 0; + my $check = 0; + $s =~ s/^.*\bdo\b//; + $s =~ s/^\s*{//; + if ($s =~ s/^\s*\\//) { + $continuation = 1; + } + if ($s =~ s/^\s*?\n//) { + $check = 1; + $cond_lines++; + } + + # Also ignore a loop construct at the end of a + # preprocessor statement. + if (($prevline =~ /^.\s*#\s*define\s/ || + $prevline =~ /\\\s*$/) && $continuation == 0) { + $check = 0; + } + + my $cond_ptr = -1; + $continuation = 0; + while ($cond_ptr != $cond_lines) { + $cond_ptr = $cond_lines; + + # If we see an #else/#elif then the code + # is not linear. + if ($s =~ /^\s*\#\s*(?:else|elif)/) { + $check = 0; + } + + # Ignore: + # 1) blank lines, they should be at 0, + # 2) preprocessor lines, and + # 3) labels. + if ($continuation || + $s =~ /^\s*?\n/ || + $s =~ /^\s*#\s*?/ || + $s =~ /^\s*$Ident\s*:/) { + $continuation = ($s =~ /^.*?\\\n/) ? 1 : 0; + if ($s =~ s/^.*?\n//) { + $cond_lines++; + } + } + } + + my (undef, $sindent) = line_stats("+" . $s); + my $stat_real = raw_line($linenr, $cond_lines); + + # Check if either of these lines are modified, else + # this is not this patch's fault. + if (!defined($stat_real) || + $stat !~ /^\+/ && $stat_real !~ /^\+/) { + $check = 0; + } + if (defined($stat_real) && $cond_lines > 1) { + $stat_real = "[...]\n$stat_real"; + } + + #print "line<$line> prevline<$prevline> indent<$indent> sindent<$sindent> check<$check> continuation<$continuation> s<$s> cond_lines<$cond_lines> stat_real<$stat_real> stat<$stat>\n"; + + if ($check && (($sindent % 8) != 0 || + ($sindent <= $indent && $s ne ''))) { + WARN("suspect code indent for conditional statements ($indent, $sindent)\n" . $herecurr . "$stat_real\n"); + } + } + + # Track the 'values' across context and added lines. + my $opline = $line; $opline =~ s/^./ /; + my ($curr_values, $curr_vars) = + annotate_values($opline . "\n", $prev_values); + $curr_values = $prev_values . $curr_values; + if ($dbg_values) { + my $outline = $opline; $outline =~ s/\t/ /g; + print "$linenr > .$outline\n"; + print "$linenr > $curr_values\n"; + print "$linenr > $curr_vars\n"; + } + $prev_values = substr($curr_values, -1); + +#ignore lines not being added + if ($line=~/^[^\+]/) {next;} + +# TEST: allow direct testing of the type matcher. + if ($dbg_type) { + if ($line =~ /^.\s*$Declare\s*$/) { + ERROR("TEST: is type\n" . $herecurr); + } elsif ($dbg_type > 1 && $line =~ /^.+($Declare)/) { + ERROR("TEST: is not type ($1 is)\n". $herecurr); + } + next; + } +# TEST: allow direct testing of the attribute matcher. + if ($dbg_attr) { + if ($line =~ /^.\s*$Modifier\s*$/) { + ERROR("TEST: is attr\n" . $herecurr); + } elsif ($dbg_attr > 1 && $line =~ /^.+($Modifier)/) { + ERROR("TEST: is not attr ($1 is)\n". $herecurr); + } + next; + } + +# check for initialisation to aggregates open brace on the next line + if ($line =~ /^.\s*{/ && + $prevline =~ /(?:^|[^=])=\s*$/) { + ERROR("that open brace { should be on the previous line\n" . $hereprev); + } + +# +# Checks which are anchored on the added line. +# + +# check for malformed paths in #include statements (uses RAW line) + if ($rawline =~ m{^.\s*\#\s*include\s+[<"](.*)[">]}) { + my $path = $1; + if ($path =~ m{//}) { + ERROR("malformed #include filename\n" . + $herecurr); + } + } + +# no C99 // comments + if ($line =~ m{//}) { + ERROR("do not use C99 // comments\n" . $herecurr); + } + # Remove C99 comments. + $line =~ s@//.*@@; + $opline =~ s@//.*@@; + +# EXPORT_SYMBOL should immediately follow the thing it is exporting, consider +# the whole statement. +#print "APW <$lines[$realline_next - 1]>\n"; + if (defined $realline_next && + exists $lines[$realline_next - 1] && + !defined $suppress_export{$realline_next} && + ($lines[$realline_next - 1] =~ /EXPORT_SYMBOL.*\((.*)\)/ || + $lines[$realline_next - 1] =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { + my $name = $1; + if ($stat !~ /(?: + \n.}\s*$| + ^.DEFINE_$Ident\(\Q$name\E\)| + ^.DECLARE_$Ident\(\Q$name\E\)| + ^.LIST_HEAD\(\Q$name\E\)| + ^.(?:$Storage\s+)?$Type\s*\(\s*\*\s*\Q$name\E\s*\)\s*\(| + \b\Q$name\E(?:\s+$Attribute)*\s*(?:;|=|\[|\() + )/x) { +#print "FOO A<$lines[$realline_next - 1]> stat<$stat> name<$name>\n"; + $suppress_export{$realline_next} = 2; + } else { + $suppress_export{$realline_next} = 1; + } + } + if (!defined $suppress_export{$linenr} && + $prevline =~ /^.\s*$/ && + ($line =~ /EXPORT_SYMBOL.*\((.*)\)/ || + $line =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { +#print "FOO B <$lines[$linenr - 1]>\n"; + $suppress_export{$linenr} = 2; + } + if (defined $suppress_export{$linenr} && + $suppress_export{$linenr} == 2) { + WARN("EXPORT_SYMBOL(foo); should immediately follow its function/variable\n" . $herecurr); + } + +# check for external initialisers. + if ($line =~ /^.$Type\s*$Ident\s*(?:\s+$Modifier)*\s*=\s*(0|NULL|false)\s*;/) { + ERROR("do not initialise externals to 0 or NULL\n" . + $herecurr); + } +# check for static initialisers. + if ($line =~ /\bstatic\s.*=\s*(0|NULL|false)\s*;/) { + ERROR("do not initialise statics to 0 or NULL\n" . + $herecurr); + } + +# check for new typedefs, only function parameters and sparse annotations +# make sense. + if ($line =~ /\btypedef\s/ && + $line !~ /\btypedef\s+$Type\s*\(\s*\*?$Ident\s*\)\s*\(/ && + $line !~ /\btypedef\s+$Type\s+$Ident\s*\(/ && + $line !~ /\b$typeTypedefs\b/ && + $line !~ /\b__bitwise(?:__|)\b/) { + WARN("do not add new typedefs\n" . $herecurr); + } + +# * goes on variable not on type + # (char*[ const]) + if ($line =~ m{\($NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)\)}) { + my ($from, $to) = ($1, $1); + + # Should start with a space. + $to =~ s/^(\S)/ $1/; + # Should not end with a space. + $to =~ s/\s+$//; + # '*'s should not have spaces between. + while ($to =~ s/\*\s+\*/\*\*/) { + } + + #print "from<$from> to<$to>\n"; + if ($from ne $to) { + ERROR("\"(foo$from)\" should be \"(foo$to)\"\n" . $herecurr); + } + } elsif ($line =~ m{\b$NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)($Ident)}) { + my ($from, $to, $ident) = ($1, $1, $2); + + # Should start with a space. + $to =~ s/^(\S)/ $1/; + # Should not end with a space. + $to =~ s/\s+$//; + # '*'s should not have spaces between. + while ($to =~ s/\*\s+\*/\*\*/) { + } + # Modifiers should have spaces. + $to =~ s/(\b$Modifier$)/$1 /; + + #print "from<$from> to<$to> ident<$ident>\n"; + if ($from ne $to && $ident !~ /^$Modifier$/) { + ERROR("\"foo${from}bar\" should be \"foo${to}bar\"\n" . $herecurr); + } + } + +# # no BUG() or BUG_ON() +# if ($line =~ /\b(BUG|BUG_ON)\b/) { +# print "Try to use WARN_ON & Recovery code rather than BUG() or BUG_ON()\n"; +# print "$herecurr"; +# $clean = 0; +# } + + if ($line =~ /\bLINUX_VERSION_CODE\b/) { + WARN("LINUX_VERSION_CODE should be avoided, code should be for the version to which it is merged\n" . $herecurr); + } + +# printk should use KERN_* levels. Note that follow on printk's on the +# same line do not need a level, so we use the current block context +# to try and find and validate the current printk. In summary the current +# printk includes all preceeding printk's which have no newline on the end. +# we assume the first bad printk is the one to report. + if ($line =~ /\bprintk\((?!KERN_)\s*"/) { + my $ok = 0; + for (my $ln = $linenr - 1; $ln >= $first_line; $ln--) { + #print "CHECK<$lines[$ln - 1]\n"; + # we have a preceeding printk if it ends + # with "\n" ignore it, else it is to blame + if ($lines[$ln - 1] =~ m{\bprintk\(}) { + if ($rawlines[$ln - 1] !~ m{\\n"}) { + $ok = 1; + } + last; + } + } + if ($ok == 0) { + WARN("printk() should include KERN_ facility level\n" . $herecurr); + } + } + +# function brace can't be on same line, except for #defines of do while, +# or if closed on same line + if (($line=~/$Type\s*$Ident\(.*\).*\s{/) and + !($line=~/\#\s*define.*do\s{/) and !($line=~/}/)) { + ERROR("open brace '{' following function declarations go on the next line\n" . $herecurr); + } + +# open braces for enum, union and struct go on the same line. + if ($line =~ /^.\s*{/ && + $prevline =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?\s*$/) { + ERROR("open brace '{' following $1 go on the same line\n" . $hereprev); + } + +# check for spacing round square brackets; allowed: +# 1. with a type on the left -- int [] a; +# 2. at the beginning of a line for slice initialisers -- [0...10] = 5, +# 3. inside a curly brace -- = { [0...10] = 5 } + while ($line =~ /(.*?\s)\[/g) { + my ($where, $prefix) = ($-[1], $1); + if ($prefix !~ /$Type\s+$/ && + ($where != 0 || $prefix !~ /^.\s+$/) && + $prefix !~ /{\s+$/) { + ERROR("space prohibited before open square bracket '['\n" . $herecurr); + } + } + +# check for spaces between functions and their parentheses. + while ($line =~ /($Ident)\s+\(/g) { + my $name = $1; + my $ctx_before = substr($line, 0, $-[1]); + my $ctx = "$ctx_before$name"; + + # Ignore those directives where spaces _are_ permitted. + if ($name =~ /^(?: + if|for|while|switch|return|case| + volatile|__volatile__| + __attribute__|format|__extension__| + asm|__asm__)$/x) + { + + # cpp #define statements have non-optional spaces, ie + # if there is a space between the name and the open + # parenthesis it is simply not a parameter group. + } elsif ($ctx_before =~ /^.\s*\#\s*define\s*$/) { + + # cpp #elif statement condition may start with a ( + } elsif ($ctx =~ /^.\s*\#\s*elif\s*$/) { + + # If this whole things ends with a type its most + # likely a typedef for a function. + } elsif ($ctx =~ /$Type$/) { + + } else { + WARN("space prohibited between function name and open parenthesis '('\n" . $herecurr); + } + } +# Check operator spacing. + if (!($line=~/\#\s*include/)) { + my $ops = qr{ + <<=|>>=|<=|>=|==|!=| + \+=|-=|\*=|\/=|%=|\^=|\|=|&=| + =>|->|<<|>>|<|>|=|!|~| + &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|%| + \?|: + }x; + my @elements = split(/($ops|;)/, $opline); + my $off = 0; + + my $blank = copy_spacing($opline); + + for (my $n = 0; $n < $#elements; $n += 2) { + $off += length($elements[$n]); + + # Pick up the preceeding and succeeding characters. + my $ca = substr($opline, 0, $off); + my $cc = ''; + if (length($opline) >= ($off + length($elements[$n + 1]))) { + $cc = substr($opline, $off + length($elements[$n + 1])); + } + my $cb = "$ca$;$cc"; + + my $a = ''; + $a = 'V' if ($elements[$n] ne ''); + $a = 'W' if ($elements[$n] =~ /\s$/); + $a = 'C' if ($elements[$n] =~ /$;$/); + $a = 'B' if ($elements[$n] =~ /(\[|\()$/); + $a = 'O' if ($elements[$n] eq ''); + $a = 'E' if ($ca =~ /^\s*$/); + + my $op = $elements[$n + 1]; + + my $c = ''; + if (defined $elements[$n + 2]) { + $c = 'V' if ($elements[$n + 2] ne ''); + $c = 'W' if ($elements[$n + 2] =~ /^\s/); + $c = 'C' if ($elements[$n + 2] =~ /^$;/); + $c = 'B' if ($elements[$n + 2] =~ /^(\)|\]|;)/); + $c = 'O' if ($elements[$n + 2] eq ''); + $c = 'E' if ($elements[$n + 2] =~ /^\s*\\$/); + } else { + $c = 'E'; + } + + my $ctx = "${a}x${c}"; + + my $at = "(ctx:$ctx)"; + + my $ptr = substr($blank, 0, $off) . "^"; + my $hereptr = "$hereline$ptr\n"; + + # Pull out the value of this operator. + my $op_type = substr($curr_values, $off + 1, 1); + + # Get the full operator variant. + my $opv = $op . substr($curr_vars, $off, 1); + + # Ignore operators passed as parameters. + if ($op_type ne 'V' && + $ca =~ /\s$/ && $cc =~ /^\s*,/) { + +# # Ignore comments +# } elsif ($op =~ /^$;+$/) { + + # ; should have either the end of line or a space or \ after it + } elsif ($op eq ';') { + if ($ctx !~ /.x[WEBC]/ && + $cc !~ /^\\/ && $cc !~ /^;/) { + ERROR("space required after that '$op' $at\n" . $hereptr); + } + + # // is a comment + } elsif ($op eq '//') { + + # No spaces for: + # -> + # : when part of a bitfield + } elsif ($op eq '->' || $opv eq ':B') { + if ($ctx =~ /Wx.|.xW/) { + ERROR("spaces prohibited around that '$op' $at\n" . $hereptr); + } + + # , must have a space on the right. + } elsif ($op eq ',') { + if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/) { + ERROR("space required after that '$op' $at\n" . $hereptr); + } + + # '*' as part of a type definition -- reported already. + } elsif ($opv eq '*_') { + #warn "'*' is part of type\n"; + + # unary operators should have a space before and + # none after. May be left adjacent to another + # unary operator, or a cast + } elsif ($op eq '!' || $op eq '~' || + $opv eq '*U' || $opv eq '-U' || + $opv eq '&U' || $opv eq '&&U') { + if ($ctx !~ /[WEBC]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) { + ERROR("space required before that '$op' $at\n" . $hereptr); + } + if ($op eq '*' && $cc =~/\s*$Modifier\b/) { + # A unary '*' may be const + + } elsif ($ctx =~ /.xW/) { + ERROR("space prohibited after that '$op' $at\n" . $hereptr); + } + + # unary ++ and unary -- are allowed no space on one side. + } elsif ($op eq '++' or $op eq '--') { + if ($ctx !~ /[WEOBC]x[^W]/ && $ctx !~ /[^W]x[WOBEC]/) { + ERROR("space required one side of that '$op' $at\n" . $hereptr); + } + if ($ctx =~ /Wx[BE]/ || + ($ctx =~ /Wx./ && $cc =~ /^;/)) { + ERROR("space prohibited before that '$op' $at\n" . $hereptr); + } + if ($ctx =~ /ExW/) { + ERROR("space prohibited after that '$op' $at\n" . $hereptr); + } + + + # << and >> may either have or not have spaces both sides + } elsif ($op eq '<<' or $op eq '>>' or + $op eq '&' or $op eq '^' or $op eq '|' or + $op eq '+' or $op eq '-' or + $op eq '*' or $op eq '/' or + $op eq '%') + { + if ($ctx =~ /Wx[^WCE]|[^WCE]xW/) { + ERROR("need consistent spacing around '$op' $at\n" . + $hereptr); + } + + # A colon needs no spaces before when it is + # terminating a case value or a label. + } elsif ($opv eq ':C' || $opv eq ':L') { + if ($ctx =~ /Wx./) { + ERROR("space prohibited before that '$op' $at\n" . $hereptr); + } + + # All the others need spaces both sides. + } elsif ($ctx !~ /[EWC]x[CWE]/) { + my $ok = 0; + + # Ignore email addresses + if (($op eq '<' && + $cc =~ /^\S+\@\S+>/) || + ($op eq '>' && + $ca =~ /<\S+\@\S+$/)) + { + $ok = 1; + } + + # Ignore ?: + if (($opv eq ':O' && $ca =~ /\?$/) || + ($op eq '?' && $cc =~ /^:/)) { + $ok = 1; + } + + if ($ok == 0) { + ERROR("spaces required around that '$op' $at\n" . $hereptr); + } + } + $off += length($elements[$n + 1]); + } + } + +# check for multiple assignments + if ($line =~ /^.\s*$Lval\s*=\s*$Lval\s*=(?!=)/) { + CHK("multiple assignments should be avoided\n" . $herecurr); + } + +## # check for multiple declarations, allowing for a function declaration +## # continuation. +## if ($line =~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Ident.*/ && +## $line !~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Type\s*$Ident.*/) { +## +## # Remove any bracketed sections to ensure we do not +## # falsly report the parameters of functions. +## my $ln = $line; +## while ($ln =~ s/\([^\(\)]*\)//g) { +## } +## if ($ln =~ /,/) { +## WARN("declaring multiple variables together should be avoided\n" . $herecurr); +## } +## } + +#need space before brace following if, while, etc + if (($line =~ /\(.*\){/ && $line !~ /\($Type\){/) || + $line =~ /do{/) { + ERROR("space required before the open brace '{'\n" . $herecurr); + } + +# closing brace should have a space following it when it has anything +# on the line + if ($line =~ /}(?!(?:,|;|\)))\S/) { + ERROR("space required after that close brace '}'\n" . $herecurr); + } + +# check spacing on square brackets + if ($line =~ /\[\s/ && $line !~ /\[\s*$/) { + ERROR("space prohibited after that open square bracket '['\n" . $herecurr); + } + if ($line =~ /\s\]/) { + ERROR("space prohibited before that close square bracket ']'\n" . $herecurr); + } + +# check spacing on parentheses + if ($line =~ /\(\s/ && $line !~ /\(\s*(?:\\)?$/ && + $line !~ /for\s*\(\s+;/) { + ERROR("space prohibited after that open parenthesis '('\n" . $herecurr); + } + if ($line =~ /(\s+)\)/ && $line !~ /^.\s*\)/ && + $line !~ /for\s*\(.*;\s+\)/ && + $line !~ /:\s+\)/) { + ERROR("space prohibited before that close parenthesis ')'\n" . $herecurr); + } + +#goto labels aren't indented, allow a single space however + if ($line=~/^.\s+[A-Za-z\d_]+:(?![0-9]+)/ and + !($line=~/^. [A-Za-z\d_]+:/) and !($line=~/^.\s+default:/)) { + WARN("labels should not be indented\n" . $herecurr); + } + +# Return is not a function. + if (defined($stat) && $stat =~ /^.\s*return(\s*)(\(.*);/s) { + my $spacing = $1; + my $value = $2; + + # Flatten any parentheses + $value =~ s/\)\(/\) \(/g; + while ($value =~ s/\[[^\{\}]*\]/1/ || + $value !~ /(?:$Ident|-?$Constant)\s* + $Compare\s* + (?:$Ident|-?$Constant)/x && + $value =~ s/\([^\(\)]*\)/1/) { + } + + if ($value =~ /^(?:$Ident|-?$Constant)$/) { + ERROR("return is not a function, parentheses are not required\n" . $herecurr); + + } elsif ($spacing !~ /\s+/) { + ERROR("space required before the open parenthesis '('\n" . $herecurr); + } + } + +# Need a space before open parenthesis after if, while etc + if ($line=~/\b(if|while|for|switch)\(/) { + ERROR("space required before the open parenthesis '('\n" . $herecurr); + } + +# Check for illegal assignment in if conditional -- and check for trailing +# statements after the conditional. + if ($line =~ /do\s*(?!{)/) { + my ($stat_next) = ctx_statement_block($line_nr_next, + $remain_next, $off_next); + $stat_next =~ s/\n./\n /g; + ##print "stat<$stat> stat_next<$stat_next>\n"; + + if ($stat_next =~ /^\s*while\b/) { + # If the statement carries leading newlines, + # then count those as offsets. + my ($whitespace) = + ($stat_next =~ /^((?:\s*\n[+-])*\s*)/s); + my $offset = + statement_rawlines($whitespace) - 1; + + $suppress_whiletrailers{$line_nr_next + + $offset} = 1; + } + } + if (!defined $suppress_whiletrailers{$linenr} && + $line =~ /\b(?:if|while|for)\s*\(/ && $line !~ /^.\s*#/) { + my ($s, $c) = ($stat, $cond); + + if ($c =~ /\bif\s*\(.*[^<>!=]=[^=].*/s) { + ERROR("do not use assignment in if condition\n" . $herecurr); + } + + # Find out what is on the end of the line after the + # conditional. + substr($s, 0, length($c), ''); + $s =~ s/\n.*//g; + $s =~ s/$;//g; # Remove any comments + if (length($c) && $s !~ /^\s*{?\s*\\*\s*$/ && + $c !~ /}\s*while\s*/) + { + # Find out how long the conditional actually is. + my @newlines = ($c =~ /\n/gs); + my $cond_lines = 1 + $#newlines; + my $stat_real = ''; + + $stat_real = raw_line($linenr, $cond_lines) + . "\n" if ($cond_lines); + if (defined($stat_real) && $cond_lines > 1) { + $stat_real = "[...]\n$stat_real"; + } + + ERROR("trailing statements should be on next line\n" . $herecurr . $stat_real); + } + } + +# Check for bitwise tests written as boolean + if ($line =~ / + (?: + (?:\[|\(|\&\&|\|\|) + \s*0[xX][0-9]+\s* + (?:\&\&|\|\|) + | + (?:\&\&|\|\|) + \s*0[xX][0-9]+\s* + (?:\&\&|\|\||\)|\]) + )/x) + { + WARN("boolean test with hexadecimal, perhaps just 1 \& or \|?\n" . $herecurr); + } + +# if and else should not have general statements after it + if ($line =~ /^.\s*(?:}\s*)?else\b(.*)/) { + my $s = $1; + $s =~ s/$;//g; # Remove any comments + if ($s !~ /^\s*(?:\sif|(?:{|)\s*\\?\s*$)/) { + ERROR("trailing statements should be on next line\n" . $herecurr); + } + } +# if should not continue a brace + if ($line =~ /}\s*if\b/) { + ERROR("trailing statements should be on next line\n" . + $herecurr); + } +# case and default should not have general statements after them + if ($line =~ /^.\s*(?:case\s*.*|default\s*):/g && + $line !~ /\G(?: + (?:\s*$;*)(?:\s*{)?(?:\s*$;*)(?:\s*\\)?\s*$| + \s*return\s+ + )/xg) + { + ERROR("trailing statements should be on next line\n" . $herecurr); + } + + # Check for }else {, these must be at the same + # indent level to be relevant to each other. + if ($prevline=~/}\s*$/ and $line=~/^.\s*else\s*/ and + $previndent == $indent) { + ERROR("else should follow close brace '}'\n" . $hereprev); + } + + if ($prevline=~/}\s*$/ and $line=~/^.\s*while\s*/ and + $previndent == $indent) { + my ($s, $c) = ctx_statement_block($linenr, $realcnt, 0); + + # Find out what is on the end of the line after the + # conditional. + substr($s, 0, length($c), ''); + $s =~ s/\n.*//g; + + if ($s =~ /^\s*;/) { + ERROR("while should follow close brace '}'\n" . $hereprev); + } + } + +#studly caps, commented out until figure out how to distinguish between use of existing and adding new +# if (($line=~/[\w_][a-z\d]+[A-Z]/) and !($line=~/print/)) { +# print "No studly caps, use _\n"; +# print "$herecurr"; +# $clean = 0; +# } + +#no spaces allowed after \ in define + if ($line=~/\#\s*define.*\\\s$/) { + WARN("Whitepspace after \\ makes next lines useless\n" . $herecurr); + } + +#warn if is #included and is available (uses RAW line) + if ($tree && $rawline =~ m{^.\s*\#\s*include\s*\}) { + my $file = "$1.h"; + my $checkfile = "include/linux/$file"; + if (-f "$root/$checkfile" && + $realfile ne $checkfile && + $1 ne 'irq') + { + if ($realfile =~ m{^arch/}) { + CHK("Consider using #include instead of \n" . $herecurr); + } else { + WARN("Use #include instead of \n" . $herecurr); + } + } + } + +# multi-statement macros should be enclosed in a do while loop, grab the +# first statement and ensure its the whole macro if its not enclosed +# in a known good container + if ($realfile !~ m@/vmlinux.lds.h$@ && + $line =~ /^.\s*\#\s*define\s*$Ident(\()?/) { + my $ln = $linenr; + my $cnt = $realcnt; + my ($off, $dstat, $dcond, $rest); + my $ctx = ''; + + my $args = defined($1); + + # Find the end of the macro and limit our statement + # search to that. + while ($cnt > 0 && defined $lines[$ln - 1] && + $lines[$ln - 1] =~ /^(?:-|..*\\$)/) + { + $ctx .= $rawlines[$ln - 1] . "\n"; + $cnt-- if ($lines[$ln - 1] !~ /^-/); + $ln++; + } + $ctx .= $rawlines[$ln - 1]; + + ($dstat, $dcond, $ln, $cnt, $off) = + ctx_statement_block($linenr, $ln - $linenr + 1, 0); + #print "dstat<$dstat> dcond<$dcond> cnt<$cnt> off<$off>\n"; + #print "LINE<$lines[$ln-1]> len<" . length($lines[$ln-1]) . "\n"; + + # Extract the remainder of the define (if any) and + # rip off surrounding spaces, and trailing \'s. + $rest = ''; + while ($off != 0 || ($cnt > 0 && $rest =~ /\\\s*$/)) { + #print "ADDING cnt<$cnt> $off <" . substr($lines[$ln - 1], $off) . "> rest<$rest>\n"; + if ($off != 0 || $lines[$ln - 1] !~ /^-/) { + $rest .= substr($lines[$ln - 1], $off) . "\n"; + $cnt--; + } + $ln++; + $off = 0; + } + $rest =~ s/\\\n.//g; + $rest =~ s/^\s*//s; + $rest =~ s/\s*$//s; + + # Clean up the original statement. + if ($args) { + substr($dstat, 0, length($dcond), ''); + } else { + $dstat =~ s/^.\s*\#\s*define\s+$Ident\s*//; + } + $dstat =~ s/$;//g; + $dstat =~ s/\\\n.//g; + $dstat =~ s/^\s*//s; + $dstat =~ s/\s*$//s; + + # Flatten any parentheses and braces + while ($dstat =~ s/\([^\(\)]*\)/1/ || + $dstat =~ s/\{[^\{\}]*\}/1/ || + $dstat =~ s/\[[^\{\}]*\]/1/) + { + } + + my $exceptions = qr{ + $Declare| + module_param_named| + MODULE_PARAM_DESC| + DECLARE_PER_CPU| + DEFINE_PER_CPU| + __typeof__\(| + union| + struct| + \.$Ident\s*=\s*| + ^\"|\"$ + }x; + #print "REST<$rest> dstat<$dstat>\n"; + if ($rest ne '') { + if ($rest !~ /while\s*\(/ && + $dstat !~ /$exceptions/) + { + ERROR("Macros with multiple statements should be enclosed in a do - while loop\n" . "$here\n$ctx\n"); + } + + } elsif ($ctx !~ /;/) { + if ($dstat ne '' && + $dstat !~ /^(?:$Ident|-?$Constant)$/ && + $dstat !~ /$exceptions/ && + $dstat !~ /^\.$Ident\s*=/ && + $dstat =~ /$Operators/) + { + ERROR("Macros with complex values should be enclosed in parenthesis\n" . "$here\n$ctx\n"); + } + } + } + +# make sure symbols are always wrapped with VMLINUX_SYMBOL() ... +# all assignments may have only one of the following with an assignment: +# . +# ALIGN(...) +# VMLINUX_SYMBOL(...) + if ($realfile eq 'vmlinux.lds.h' && $line =~ /(?:(?:^|\s)$Ident\s*=|=\s*$Ident(?:\s|$))/) { + WARN("vmlinux.lds.h needs VMLINUX_SYMBOL() around C-visible symbols\n" . $herecurr); + } + +# check for redundant bracing round if etc + if ($line =~ /(^.*)\bif\b/ && $1 !~ /else\s*$/) { + my ($level, $endln, @chunks) = + ctx_statement_full($linenr, $realcnt, 1); + #print "chunks<$#chunks> linenr<$linenr> endln<$endln> level<$level>\n"; + #print "APW: <<$chunks[1][0]>><<$chunks[1][1]>>\n"; + if ($#chunks > 0 && $level == 0) { + my $allowed = 0; + my $seen = 0; + my $herectx = $here . "\n"; + my $ln = $linenr - 1; + for my $chunk (@chunks) { + my ($cond, $block) = @{$chunk}; + + # If the condition carries leading newlines, then count those as offsets. + my ($whitespace) = ($cond =~ /^((?:\s*\n[+-])*\s*)/s); + my $offset = statement_rawlines($whitespace) - 1; + + #print "COND<$cond> whitespace<$whitespace> offset<$offset>\n"; + + # We have looked at and allowed this specific line. + $suppress_ifbraces{$ln + $offset} = 1; + + $herectx .= "$rawlines[$ln + $offset]\n[...]\n"; + $ln += statement_rawlines($block) - 1; + + substr($block, 0, length($cond), ''); + + $seen++ if ($block =~ /^\s*{/); + + #print "cond<$cond> block<$block> allowed<$allowed>\n"; + if (statement_lines($cond) > 1) { + #print "APW: ALLOWED: cond<$cond>\n"; + $allowed = 1; + } + if ($block =~/\b(?:if|for|while)\b/) { + #print "APW: ALLOWED: block<$block>\n"; + $allowed = 1; + } + if (statement_block_size($block) > 1) { + #print "APW: ALLOWED: lines block<$block>\n"; + $allowed = 1; + } + } + if ($seen && !$allowed) { + WARN("braces {} are not necessary for any arm of this statement\n" . $herectx); + } + } + } + if (!defined $suppress_ifbraces{$linenr - 1} && + $line =~ /\b(if|while|for|else)\b/) { + my $allowed = 0; + + # Check the pre-context. + if (substr($line, 0, $-[0]) =~ /(\}\s*)$/) { + #print "APW: ALLOWED: pre<$1>\n"; + $allowed = 1; + } + + my ($level, $endln, @chunks) = + ctx_statement_full($linenr, $realcnt, $-[0]); + + # Check the condition. + my ($cond, $block) = @{$chunks[0]}; + #print "CHECKING<$linenr> cond<$cond> block<$block>\n"; + if (defined $cond) { + substr($block, 0, length($cond), ''); + } + if (statement_lines($cond) > 1) { + #print "APW: ALLOWED: cond<$cond>\n"; + $allowed = 1; + } + if ($block =~/\b(?:if|for|while)\b/) { + #print "APW: ALLOWED: block<$block>\n"; + $allowed = 1; + } + if (statement_block_size($block) > 1) { + #print "APW: ALLOWED: lines block<$block>\n"; + $allowed = 1; + } + # Check the post-context. + if (defined $chunks[1]) { + my ($cond, $block) = @{$chunks[1]}; + if (defined $cond) { + substr($block, 0, length($cond), ''); + } + if ($block =~ /^\s*\{/) { + #print "APW: ALLOWED: chunk-1 block<$block>\n"; + $allowed = 1; + } + } + if ($level == 0 && $block =~ /^\s*\{/ && !$allowed) { + my $herectx = $here . "\n";; + my $cnt = statement_rawlines($block); + + for (my $n = 0; $n < $cnt; $n++) { + $herectx .= raw_line($linenr, $n) . "\n";; + } + + WARN("braces {} are not necessary for single statement blocks\n" . $herectx); + } + } + +# don't include deprecated include files (uses RAW line) + for my $inc (@dep_includes) { + if ($rawline =~ m@^.\s*\#\s*include\s*\<$inc>@) { + ERROR("Don't use <$inc>: see Documentation/feature-removal-schedule.txt\n" . $herecurr); + } + } + +# don't use deprecated functions + for my $func (@dep_functions) { + if ($line =~ /\b$func\b/) { + ERROR("Don't use $func(): see Documentation/feature-removal-schedule.txt\n" . $herecurr); + } + } + +# no volatiles please + my $asm_volatile = qr{\b(__asm__|asm)\s+(__volatile__|volatile)\b}; + if ($line =~ /\bvolatile\b/ && $line !~ /$asm_volatile/) { + WARN("Use of volatile is usually wrong: see Documentation/volatile-considered-harmful.txt\n" . $herecurr); + } + +# SPIN_LOCK_UNLOCKED & RW_LOCK_UNLOCKED are deprecated + if ($line =~ /\b(SPIN_LOCK_UNLOCKED|RW_LOCK_UNLOCKED)/) { + ERROR("Use of $1 is deprecated: see Documentation/spinlocks.txt\n" . $herecurr); + } + +# warn about #if 0 + if ($line =~ /^.\s*\#\s*if\s+0\b/) { + CHK("if this code is redundant consider removing it\n" . + $herecurr); + } + +# check for needless kfree() checks + if ($prevline =~ /\bif\s*\(([^\)]*)\)/) { + my $expr = $1; + if ($line =~ /\bkfree\(\Q$expr\E\);/) { + WARN("kfree(NULL) is safe this check is probably not required\n" . $hereprev); + } + } +# check for needless usb_free_urb() checks + if ($prevline =~ /\bif\s*\(([^\)]*)\)/) { + my $expr = $1; + if ($line =~ /\busb_free_urb\(\Q$expr\E\);/) { + WARN("usb_free_urb(NULL) is safe this check is probably not required\n" . $hereprev); + } + } + +# warn about #ifdefs in C files +# if ($line =~ /^.\s*\#\s*if(|n)def/ && ($realfile =~ /\.c$/)) { +# print "#ifdef in C files should be avoided\n"; +# print "$herecurr"; +# $clean = 0; +# } + +# warn about spacing in #ifdefs + if ($line =~ /^.\s*\#\s*(ifdef|ifndef|elif)\s\s+/) { + ERROR("exactly one space required after that #$1\n" . $herecurr); + } + +# check for spinlock_t definitions without a comment. + if ($line =~ /^.\s*(struct\s+mutex|spinlock_t)\s+\S+;/ || + $line =~ /^.\s*(DEFINE_MUTEX)\s*\(/) { + my $which = $1; + if (!ctx_has_comment($first_line, $linenr)) { + CHK("$1 definition without comment\n" . $herecurr); + } + } +# check for memory barriers without a comment. + if ($line =~ /\b(mb|rmb|wmb|read_barrier_depends|smp_mb|smp_rmb|smp_wmb|smp_read_barrier_depends)\(/) { + if (!ctx_has_comment($first_line, $linenr)) { + CHK("memory barrier without comment\n" . $herecurr); + } + } +# check of hardware specific defines + if ($line =~ m@^.\s*\#\s*if.*\b(__i386__|__powerpc64__|__sun__|__s390x__)\b@ && $realfile !~ m@include/asm-@) { + CHK("architecture specific defines should be avoided\n" . $herecurr); + } + +# Check that the storage class is at the beginning of a declaration + if ($line =~ /\b$Storage\b/ && $line !~ /^.\s*$Storage\b/) { + WARN("storage class should be at the beginning of the declaration\n" . $herecurr) + } + +# check the location of the inline attribute, that it is between +# storage class and type. + if ($line =~ /\b$Type\s+$Inline\b/ || + $line =~ /\b$Inline\s+$Storage\b/) { + ERROR("inline keyword should sit between storage class and type\n" . $herecurr); + } + +# Check for __inline__ and __inline, prefer inline + if ($line =~ /\b(__inline__|__inline)\b/) { + WARN("plain inline is preferred over $1\n" . $herecurr); + } + +# check for sizeof(&) + if ($line =~ /\bsizeof\s*\(\s*\&/) { + WARN("sizeof(& should be avoided\n" . $herecurr); + } + +# check for new externs in .c files. + if ($realfile =~ /\.c$/ && defined $stat && + $stat =~ /^.\s*(?:extern\s+)?$Type\s+($Ident)(\s*)\(/s) + { + my $function_name = $1; + my $paren_space = $2; + + my $s = $stat; + if (defined $cond) { + substr($s, 0, length($cond), ''); + } + if ($s =~ /^\s*;/ && + $function_name ne 'uninitialized_var') + { + WARN("externs should be avoided in .c files\n" . $herecurr); + } + + if ($paren_space =~ /\n/) { + WARN("arguments for function declarations should follow identifier\n" . $herecurr); + } + + } elsif ($realfile =~ /\.c$/ && defined $stat && + $stat =~ /^.\s*extern\s+/) + { + WARN("externs should be avoided in .c files\n" . $herecurr); + } + +# checks for new __setup's + if ($rawline =~ /\b__setup\("([^"]*)"/) { + my $name = $1; + + if (!grep(/$name/, @setup_docs)) { + CHK("__setup appears un-documented -- check Documentation/kernel-parameters.txt\n" . $herecurr); + } + } + +# check for pointless casting of kmalloc return + if ($line =~ /\*\s*\)\s*k[czm]alloc\b/) { + WARN("unnecessary cast may hide bugs, see http://c-faq.com/malloc/mallocnocast.html\n" . $herecurr); + } + +# check for gcc specific __FUNCTION__ + if ($line =~ /__FUNCTION__/) { + WARN("__func__ should be used instead of gcc specific __FUNCTION__\n" . $herecurr); + } + +# check for semaphores used as mutexes + if ($line =~ /^.\s*(DECLARE_MUTEX|init_MUTEX)\s*\(/) { + WARN("mutexes are preferred for single holder semaphores\n" . $herecurr); + } +# check for semaphores used as mutexes + if ($line =~ /^.\s*init_MUTEX_LOCKED\s*\(/) { + WARN("consider using a completion\n" . $herecurr); + + } +# recommend strict_strto* over simple_strto* + if ($line =~ /\bsimple_(strto.*?)\s*\(/) { + WARN("consider using strict_$1 in preference to simple_$1\n" . $herecurr); + } +# check for __initcall(), use device_initcall() explicitly please + if ($line =~ /^.\s*__initcall\s*\(/) { + WARN("please use device_initcall() instead of __initcall()\n" . $herecurr); + } +# check for various ops structs, ensure they are const. + my $struct_ops = qr{acpi_dock_ops| + address_space_operations| + backlight_ops| + block_device_operations| + dentry_operations| + dev_pm_ops| + dma_map_ops| + extent_io_ops| + file_lock_operations| + file_operations| + hv_ops| + ide_dma_ops| + intel_dvo_dev_ops| + item_operations| + iwl_ops| + kgdb_arch| + kgdb_io| + kset_uevent_ops| + lock_manager_operations| + microcode_ops| + mtrr_ops| + neigh_ops| + nlmsvc_binding| + pci_raw_ops| + pipe_buf_operations| + platform_hibernation_ops| + platform_suspend_ops| + proto_ops| + rpc_pipe_ops| + seq_operations| + snd_ac97_build_ops| + soc_pcmcia_socket_ops| + stacktrace_ops| + sysfs_ops| + tty_operations| + usb_mon_operations| + wd_ops}x; + if ($line !~ /\bconst\b/ && + $line =~ /\bstruct\s+($struct_ops)\b/) { + WARN("struct $1 should normally be const\n" . + $herecurr); + } + +# use of NR_CPUS is usually wrong +# ignore definitions of NR_CPUS and usage to define arrays as likely right + if ($line =~ /\bNR_CPUS\b/ && + $line !~ /^.\s*\s*#\s*if\b.*\bNR_CPUS\b/ && + $line !~ /^.\s*\s*#\s*define\b.*\bNR_CPUS\b/ && + $line !~ /^.\s*$Declare\s.*\[[^\]]*NR_CPUS[^\]]*\]/ && + $line !~ /\[[^\]]*\.\.\.[^\]]*NR_CPUS[^\]]*\]/ && + $line !~ /\[[^\]]*NR_CPUS[^\]]*\.\.\.[^\]]*\]/) + { + WARN("usage of NR_CPUS is often wrong - consider using cpu_possible(), num_possible_cpus(), for_each_possible_cpu(), etc\n" . $herecurr); + } + +# check for %L{u,d,i} in strings + my $string; + while ($line =~ /(?:^|")([X\t]*)(?:"|$)/g) { + $string = substr($rawline, $-[1], $+[1] - $-[1]); + $string =~ s/%%/__/g; + if ($string =~ /(?mutex.\n" . $herecurr); + } + } + } + + # If we have no input at all, then there is nothing to report on + # so just keep quiet. + if ($#rawlines == -1) { + exit(0); + } + + # In mailback mode only produce a report in the negative, for + # things that appear to be patches. + if ($mailback && ($clean == 1 || !$is_patch)) { + exit(0); + } + + # This is not a patch, and we are are in 'no-patch' mode so + # just keep quiet. + if (!$chk_patch && !$is_patch) { + exit(0); + } + + if (!$is_patch) { + ERROR("Does not appear to be a unified-diff format patch\n"); + } + if ($is_patch && $chk_signoff && $signoff == 0) { + ERROR("Missing Signed-off-by: line(s)\n"); + } + + print report_dump(); + if ($summary && !($clean == 1 && $quiet == 1)) { + print "$filename " if ($summary_file); + print "total: $cnt_error errors, $cnt_warn warnings, " . + (($check)? "$cnt_chk checks, " : "") . + "$cnt_lines lines checked\n"; + print "\n" if ($quiet == 0); + } + + if ($clean == 1 && $quiet == 0) { + print "$vname has no obvious style problems and is ready for submission.\n" + } + if ($clean == 0 && $quiet == 0) { + print "$vname has style problems, please review. If any of these errors\n"; + print "are false positives report them to the maintainer, see\n"; + print "CHECKPATCH in MAINTAINERS.\n"; + } + + return $clean; +} diff --git a/www/Makefile.am b/www/Makefile.am new file mode 100644 index 0000000..60c1afa --- /dev/null +++ b/www/Makefile.am @@ -0,0 +1,4 @@ +defaultwwwdir = $(pkgdatadir)/www +defaultwww_DATA = monitor.lua style.css favicon.ico index.lua + +EXTRA_DIST=$(defaultwww_DATA) diff --git a/www/favicon.ico b/www/favicon.ico new file mode 100644 index 0000000..c78647d Binary files /dev/null and b/www/favicon.ico differ diff --git a/www/index.lua b/www/index.lua new file mode 100644 index 0000000..817535a --- /dev/null +++ b/www/index.lua @@ -0,0 +1,28 @@ + +function footer() + return "" +end + +str = "\ +\ + \ + Psensor Web Server\ + \ + \ +\ + \ +\ +

Welcome to the Psensor Web Server!

\ +\ +

Go to the Monitoring Page.

\ +\ +
" + .. footer() + .. "\ +\ +" + +return str \ No newline at end of file diff --git a/www/monitor.lua b/www/monitor.lua new file mode 100644 index 0000000..3042ad3 --- /dev/null +++ b/www/monitor.lua @@ -0,0 +1,188 @@ +-- +-- Convenient functions for HTML output +-- + +function td(content) + return "" .. content .. "" +end + +function th(style, content) + if style then + return ""..content.."" + else + return ""..content.."" + end +end + +function tr(style,...) + if style then + ret = "" + else + ret = "" + end + + for i,s in ipairs(arg) do + ret = ret .. s + end + + ret = ret .. "\n" + + return ret +end + +function h2(str) + return "

"..str.."

\n" +end + + +-- Formats sensor information to HTML 'tr' +function sensor_to_tr(id,sensor) + return tr(nil, + td(sensor["name"]), + td(sensor["measure_last"]), + td(sensor["measure_min"]), + td(sensor["measure_max"])) +end + + +-- Formats number of bytes to string +function format_mem_size(bytes) + if (bytes == 0) then + return "0" + end + + if (bytes < 1024) then + return bytes .. " o"; + end + + mo_bytes = 1024 * 1024; + + if (bytes < mo_bytes) then + return math.ceil(bytes / 1024) .. " Ko" + end + + go_bytes = 1024 * mo_bytes; + + if (bytes < go_bytes) then + return math.ceil(bytes / mo_bytes) .. " Mo" + end + + return math.ceil(bytes / go_bytes) .. " Go" +end + +-- Formats uptime to string +function format_uptime(uptime) + uptime_s = sysinfo["uptime"]%60 + uptime_mn = math.floor( (sysinfo["uptime"] / 60) % 60) + uptime_h = math.floor( (sysinfo["uptime"] / (60*60)) % 24) + uptime_d = math.floor(sysinfo["uptime"] / (60*60*24)) + + return uptime_d .. "d " .. + uptime_h .. "h " .. + string.format("%02.d",uptime_mn) .. "mn " .. + string.format("%02d",uptime_s) .. "s" +end + +str = "

Psensor Monitoring Server

" + +if sysinfo then + +-- +-- Uptime +-- + + str = str .. "

Uptime: " .. format_uptime(sysinfo["uptime"]) .. "

" + +-- +-- CPU +-- + + str = str .. h2("CPU") + + str = str .. "" + .. tr("title", + th(nil,"Current usage"), + th(nil,"Load 1mn"), + th(nil,"Load 5mn"), + th(nil,"Load 15mn")) + + str = str .. "" + + if sysinfo["load"] then + str = str .. td(math.ceil(100*sysinfo["load"]) .. "%") + else + str = str .. td("N/A") + end + + str = str .. td(string.format("%.2f",sysinfo["load_1mn"])) .. + td(string.format("%.2f",sysinfo["load_5mn"])) .. + td(string.format("%.2f",sysinfo["load_15mn"])) .. + "" .. + "
" + +-- +-- Memory +-- + + totalram = format_mem_size(sysinfo["totalram"] * sysinfo["mem_unit"]) + freeram = format_mem_size(sysinfo["freeram"] * sysinfo["mem_unit"]) + sharedram = format_mem_size(sysinfo["sharedram"] * sysinfo["mem_unit"]) + bufferram = format_mem_size(sysinfo["bufferram"] * sysinfo["mem_unit"]) + usedram = format_mem_size(sysinfo["totalram"] - sysinfo["freeram"]) + + totalswap = format_mem_size(sysinfo["totalswap"] * sysinfo["mem_unit"]) + freeswap = format_mem_size(sysinfo["freeswap"] * sysinfo["mem_unit"]) + usedswap = format_mem_size(sysinfo["totalswap"] - sysinfo["freeswap"]) + + str = str + .. h2("Memory") + + .. "" + + .. tr("title", + th(nil,""), + th(nil,"Total"), + th(nil,"Used"), + th(nil,"Free"), + th(nil,"Shared"), + th(nil,"Buffer")) + + .. tr(nil, + th(nil,"Memory"), + td(totalram), + td(usedram), + td(freeram), + td(sharedram), + td(bufferram)) + + .. tr(nil, + th(nil,"Swap"), + td(totalswap), + td(usedswap), + td(freeswap)) + + .. "
" + +end + +-- +-- Sensors +-- + +if sensors then + + str = str .. "

Sensors

" + .. "" + .. "" + + for i,sensor in ipairs(sensors) do + str = str .. sensor_to_tr(i,sensor) + end + + str = str .. "
NameValueMinMax

psensor-server" + +end + + +return str + diff --git a/www/style.css b/www/style.css new file mode 100644 index 0000000..90d4dab --- /dev/null +++ b/www/style.css @@ -0,0 +1,23 @@ +body { + font-family: Ubuntu, Verdana, Helvetica, Arial, sans-serif; + background: #fff; + color: Black; + margin: 1em 0em 0em 1em; + padding-left: 0em 0em 0em 0em; +} + +table { + border: 1px solid #ddd; +} + +tr.title { + background-color: #eee; +} + +th { + text-align: center; +} + +td { + text-align: right; +} \ No newline at end of file